Add FishNet network library

This commit is contained in:
2026-09-17 20:29:57 +08:00
parent b0e708d54c
commit 668121e35c
2015 changed files with 213270 additions and 0 deletions
@@ -0,0 +1,17 @@
namespace FishNet.Transporting
{
/// <summary>
/// Channel which data is sent or received.
/// </summary>
public enum Channel : byte
{
/// <summary>
/// Data will be sent ordered reliable.
/// </summary>
Reliable = 0,
/// <summary>
/// Data will be sent unreliable.
/// </summary>
Unreliable = 1
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7cd503d67a974984385164c53bd3e518
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Channels.cs
uploadId: 987062
@@ -0,0 +1,57 @@
namespace FishNet.Transporting
{
/// <summary>
/// States the local connection can be in.
/// </summary>
[System.Flags]
public enum LocalConnectionState : int
{
/// <summary>
/// Connection is fully stopped.
/// </summary>
Stopped = 1 << 0,
/// <summary>
/// Connection is stopping.
/// </summary>
Stopping = 1 << 1,
/// <summary>
/// Connection is starting but not yet established.
/// </summary>
Starting = 1 << 2,
/// <summary>
/// Connection is established.
/// </summary>
Started = 1 << 3
// StoppedError = (1 << 4),
// StoppedClosed = (1 << 5),
}
public static class LocalConnectionStateExtensions
{
/// <summary>
/// True if the connection state is stopped or stopping.
/// </summary>
public static bool IsStoppedOrStopping(this LocalConnectionState connectionState) => connectionState == LocalConnectionState.Stopped || connectionState == LocalConnectionState.Stopping;
/// <summary>
/// True if the connection state is started or starting.
/// </summary>
public static bool IsStartedOrStarting(this LocalConnectionState connectionState) => connectionState == LocalConnectionState.Started || connectionState == LocalConnectionState.Starting;
}
/// <summary>
/// States a remote client can be in.
/// </summary>
public enum RemoteConnectionState : byte
{
/// <summary>
/// Connection is fully stopped.
/// </summary>
Stopped = 0,
/// <summary>
/// Connection is established.
/// </summary>
Started = 2
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 45640e2b3919981499b359ecc2154d3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/ConnectionStates.cs
uploadId: 987062
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
namespace FishNet.Transporting
{
/// <summary>
/// Container for connected clients state for a client.
/// </summary>
public struct ConnectedClientsArgs
{
/// <summary>
/// Collection of client ids connected to the server.
/// </summary>
public List<int> ClientIds { get; private set; }
public ConnectedClientsArgs(List<int> clientIds)
{
ClientIds = clientIds;
}
}
/// <summary>
/// Container about data received on the server.
/// </summary>
public struct ServerReceivedDataArgs
{
/// <summary>
/// Data received.
/// </summary>
public ArraySegment<byte> Data;
/// <summary>
/// Channel data was received on.
/// </summary>
public Channel Channel;
/// <summary>
/// ConnectionId from which client sent data, if data was received on the server.
/// </summary>
public int ConnectionId;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// Delegate to invoke after data is processed.
/// </summary>
/// <returns></returns>
public Action FinalizeMethod;
public ServerReceivedDataArgs(ArraySegment<byte> data, Channel channel, int connectionId, int transportIndex)
{
Data = data;
Channel = channel;
ConnectionId = connectionId;
TransportIndex = transportIndex;
FinalizeMethod = null;
}
public ServerReceivedDataArgs(ArraySegment<byte> data, Channel channel, int connectionId, int transportIndex, Action finalizeMethod)
{
Data = data;
Channel = channel;
ConnectionId = connectionId;
TransportIndex = transportIndex;
FinalizeMethod = finalizeMethod;
}
}
/// <summary>
/// Container about data received on the local client.
/// </summary>
public struct ClientReceivedDataArgs
{
/// <summary>
/// Data received.
/// </summary>
public ArraySegment<byte> Data;
/// <summary>
/// Channel data was received on.
/// </summary>
public Channel Channel;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
public ClientReceivedDataArgs(ArraySegment<byte> data, Channel channel, int transportIndex)
{
Data = data;
Channel = channel;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for a client.
/// </summary>
public struct RemoteConnectionStateArgs
{
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// New connection state.
/// </summary>
public RemoteConnectionState ConnectionState;
/// <summary>
/// ConnectionId for which client the state changed. Will be -1 if ConnectionState was for the local server.
/// </summary>
public int ConnectionId;
public RemoteConnectionStateArgs(RemoteConnectionState connectionState, int connectionId, int transportIndex)
{
ConnectionState = connectionState;
ConnectionId = connectionId;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for the server.
/// </summary>
public struct ServerConnectionStateArgs
{
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// New connection state.
/// </summary>
public LocalConnectionState ConnectionState;
public ServerConnectionStateArgs(LocalConnectionState connectionState, int transportIndex)
{
ConnectionState = connectionState;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for the local client.
/// </summary>
public struct ClientConnectionStateArgs
{
/// <summary>
/// New connection state.
/// </summary>
public LocalConnectionState ConnectionState;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
public ClientConnectionStateArgs(LocalConnectionState connectionState, int transportIndex)
{
ConnectionState = connectionState;
TransportIndex = transportIndex;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 718b9d27800e70848b50b2c7b0117e5c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/EventStructures.cs
uploadId: 987062
@@ -0,0 +1,17 @@
namespace FishNet.Transporting
{
/// <summary>
/// Channel which data is sent or received.
/// </summary>
public enum IPAddressType : byte
{
/// <summary>
/// Address is IPv4.
/// </summary>
IPv4 = 0,
/// <summary>
/// Address is IPv6.
/// </summary>
IPv6 = 1
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 11a2c7610ce4ce34a915683bd4607714
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/IPAddressType.cs
uploadId: 987062
@@ -0,0 +1,32 @@
using FishNet.Managing.Timing;
using UnityEngine;
namespace FishNet.Transporting
{
[DisallowMultipleComponent]
[DefaultExecutionOrder(short.MinValue)]
internal class NetworkReaderLoop : MonoBehaviour
{
#region Private.
/// <summary>
/// TimeManager this loop is for.
/// </summary>
private TimeManager _timeManager;
#endregion
private void Awake()
{
_timeManager = GetComponent<TimeManager>();
}
private void FixedUpdate()
{
_timeManager.TickFixedUpdate();
}
private void Update()
{
_timeManager.TickUpdate();
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0d359d6ef33641f41a2ae67d1abdfdd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs
uploadId: 987062
@@ -0,0 +1,35 @@
using FishNet.Managing.Timing;
using UnityEngine;
namespace FishNet.Transporting
{
[DisallowMultipleComponent]
[DefaultExecutionOrder(short.MaxValue)]
internal class NetworkWriterLoop : MonoBehaviour
{
#region Private.
/// <summary>
/// TimeManager this loop is for.
/// </summary>
private TimeManager _timeManager;
#endregion
private void Awake()
{
_timeManager = GetComponent<TimeManager>();
}
private void LateUpdate()
{
Iterate();
}
/// <summary>
/// Performs read on transport.
/// </summary>
private void Iterate()
{
_timeManager.TickLateUpdate();
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2fed05d526ab23949bac6cd2bf041c35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs
uploadId: 987062
@@ -0,0 +1,39 @@
using FishNet.Documenting;
namespace FishNet.Transporting
{
/// <summary>
/// PacketIds to indicate the type of packet which is being sent or arriving.
/// </summary>
[APIExclude]
public enum PacketId : ushort
{
Unset = 0,
// Not used with network traffic statistics.
Authenticated = 1,
// Not used with network traffic statistics.
Split = 2,
ObjectSpawn = 3,
ObjectDespawn = 4,
PredictedSpawnResult = 5,
SyncType = 7,
ServerRpc = 8,
ObserversRpc = 9,
TargetRpc = 10,
// Not used with network traffic statistics.
OwnershipChange = 11,
Broadcast = 12,
// Used only as outbound identifier for network traffic statistics - this Id is never transmitted.
BulkSpawnOrDespawn = 13,
PingPong = 14,
Replicate = 15,
Reconcile = 16,
// Not used with network traffic statistics.
Disconnect = 17,
TimingUpdate = 18,
UNUSED2 = 19,
StateUpdate = 20,
// Not used with network traffic statistics.
Version = 21
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3f3b7256982245b46a2925e2b94ce149
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/PacketId.cs
uploadId: 987062
@@ -0,0 +1,265 @@
using FishNet.Managing;
using FishNet.Managing.Logging;
using System;
using UnityEngine;
namespace FishNet.Transporting
{
/// <summary>
/// Processes connection states, and data sent to and from a socket.
/// </summary>
public abstract class Transport : MonoBehaviour
{
#region Private.
/// <summary>
/// NetworkManager for this transport.
/// </summary>
public NetworkManager NetworkManager { get; private set; }
/// <summary>
/// Index this transport belongs to when using multiple transports at once.
/// </summary>
public int Index { get; private set; }
#endregion
#region Initialization and unity.
/// <summary>
/// Initializes the transport. Use this instead of Awake.
/// <param name = "transportIndex">Index this transport belongs to when using multiple transports at once.</param>
/// </summary>
public virtual void Initialize(NetworkManager networkManager, int transportIndex)
{
NetworkManager = networkManager;
Index = transportIndex;
}
#endregion
#region ConnectionStates.
/// <summary>
/// Gets the address of a remote connection Id.
/// </summary>
/// <param name = "connectionId">Connectionid to get the address for.</param>
/// <returns></returns>
public abstract string GetConnectionAddress(int connectionId);
/// <summary>
/// Called when a connection state changes for the local client.
/// </summary>
public abstract event Action<ClientConnectionStateArgs> OnClientConnectionState;
/// <summary>
/// Called when a connection state changes for the local server.
/// </summary>
public abstract event Action<ServerConnectionStateArgs> OnServerConnectionState;
/// <summary>
/// Called when a connection state changes for a remote client.
/// </summary>
public abstract event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
/// <summary>
/// Handles a ConnectionStateArgs for the local client.
/// </summary>
/// <param name = "connectionStateArgs">Data being handled.</param>
public abstract void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs);
/// <summary>
/// Handles a ConnectionStateArgs for the local server.
/// </summary>
/// <param name = "connectionStateArgs">Data being handled.</param>
public abstract void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs);
/// <summary>
/// Handles a ConnectionStateArgs for a remote client.
/// </summary>
/// <param name = "connectionStateArgs">Data being handled.</param>
public abstract void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs);
/// <summary>
/// Gets the current local ConnectionState.
/// </summary>
/// <param name = "server">True if getting ConnectionState for the server.</param>
public abstract LocalConnectionState GetConnectionState(bool server);
/// <summary>
/// Gets the current ConnectionState of a client connected to the server. Can only be called on the server.
/// </summary>
/// <param name = "connectionId">ConnectionId to get ConnectionState for.</param>
public abstract RemoteConnectionState GetConnectionState(int connectionId);
#endregion
#region Sending.
/// <summary>
/// Sends to the server.
/// </summary>
/// <param name = "channelId">Channel to use.</param>
/// <param name = "segment">Data to send.</param>
public abstract void SendToServer(byte channelId, ArraySegment<byte> segment);
/// <summary>
/// Sends to a client.
/// </summary>
/// <param name = "channelId">Channel to use.</param>
/// <param name = "segment">Data to send.</param>
/// <param name = "connectionId">ConnectionId to send to. When sending to clients can be used to specify which connection to send to.</param>
public abstract void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId);
#endregion
#region Receiving
/// <summary>
/// Called when the client receives data.
/// </summary>
public abstract event Action<ClientReceivedDataArgs> OnClientReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name = "receivedDataArgs">Data being handled.</param>
public abstract void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs);
/// <summary>
/// Called when the server receives data.
/// </summary>
public abstract event Action<ServerReceivedDataArgs> OnServerReceivedData;
/// <summary>
/// Handles a ServerReceivedDataArgs.
/// </summary>
/// <param name = "receivedDataArgs">Data being handled.</param>
public abstract void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs);
/// <summary>
/// Returns packet loss percentage. Not all transports support this feature.
/// </summary>
/// <param name = "asServer">True to return packet loss on the server, false to return packet loss on the client.</param>
public virtual float GetPacketLoss(bool asServer) => 0f;
#endregion
#region Iterating.
/// <summary>
/// Processes data received by the socket.
/// </summary>
/// <param name = "asServer">True to read data from clients, false to read data from the server.
public abstract void IterateIncoming(bool asServer);
/// <summary>
/// Processes data to be sent by the socket.
/// </summary>
/// <param name = "asServer">True to send data from the local server to clients, false to send from the local client to server.
public abstract void IterateOutgoing(bool asServer);
#endregion
#region Configuration.
/// <summary>
/// Returns if the transport is only run locally, offline.
/// While true several security checks are disabled.
/// </summary>
/// <param name = "connectionid">Optional connectionId to check against.</param>
public virtual bool IsLocalTransport(int connectionid) => false;
/// <summary>
/// Gets how long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name = "asServer">True to get the timeout for the server socket, false for the client socket.</param>
/// <returns></returns>
public virtual float GetTimeout(bool asServer) => -1f;
/// <summary>
/// Sets how long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name = "asServer">True to set the timeout for the server socket, false for the client socket.</param>
public virtual void SetTimeout(float value, bool asServer) { }
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// </summary>
/// <returns>Maximum clients transport allows.</returns>
public virtual int GetMaximumClients()
{
string message = $"The current transport does not support this feature.";
NetworkManager.LogWarning(message);
return -1;
}
/// <summary>
/// Sets the maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
/// </summary>
/// <param name = "value">Maximum clients to allow.</param>
public virtual void SetMaximumClients(int value)
{
string message = $"The current transport does not support this feature.";
NetworkManager.LogWarning(message);
}
/// <summary>
/// Sets which address the client will connect to.
/// </summary>
/// <param name = "address">Address client will connect to.</param>
public virtual void SetClientAddress(string address) { }
/// <summary>
/// Returns which address the client will connect to.
/// </summary>
public virtual string GetClientAddress() => string.Empty;
/// <summary>
/// Sets which address the server will bind to.
/// </summary>
/// <param name = "address">Address server will bind to.</param>
/// <param name = "addressType">Address type to set.</param>
public virtual void SetServerBindAddress(string address, IPAddressType addressType) { }
/// <summary>
/// Gets which address the server will bind to.
/// </summary>
/// <param name = "addressType">Address type to return.</param>
public virtual string GetServerBindAddress(IPAddressType addressType) => string.Empty;
/// <summary>
/// Sets which port to use.
/// </summary>
/// <param name = "port">Port to use.</param>
public virtual void SetPort(ushort port) { }
/// <summary>
/// Gets which port to use.
/// </summary>
public virtual ushort GetPort() => 0;
#endregion
#region Start and stop.
/// <summary>
/// Starts the local server or client using configured settings.
/// </summary>
/// <param name = "server">True to start server.</param>
public abstract bool StartConnection(bool server);
/// <summary>
/// Stops the local server or client.
/// </summary>
/// <param name = "server">True to stop server.</param>
public abstract bool StopConnection(bool server);
/// <summary>
/// Stops a remote client from the server, disconnecting the client.
/// </summary>
/// <param name = "connectionId">ConnectionId of the client to disconnect.</param>
/// <param name = "immediately">
/// True to abrutly stop the client socket. The technique used to accomplish immediate disconnects may vary depending on the transport.
/// When not using immediate disconnects it's recommended to perform disconnects using the ServerManager rather than accessing the transport directly.
/// </param>
public abstract bool StopConnection(int connectionId, bool immediately);
/// <summary>
/// Stops both client and server.
/// </summary>
public abstract void Shutdown();
#endregion
#region Channels.
/// <summary>
/// Gets the MTU for a channel.
/// </summary>
/// <param name = "channel">Channel to get MTU for.</param>
/// <returns>MTU of channel.</returns>
public abstract int GetMTU(byte channel);
#endregion
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 78aba14618b37ea4bb067fa95ede84e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transport.cs
uploadId: 987062
@@ -0,0 +1,14 @@
using FishNet.Managing;
using System;
using UnityEngine;
namespace FishNet.Transporting
{
public static class TransportConsts
{
/// <summary>
/// Value used when a transport index is not known or set for a connection.
/// </summary>
public const int UNSET_TRANSPORT_INDEX = -1;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 490a5b566793e5a4e8a97a583eab55e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/TransportConsts.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91d2fe16f6fe54e479292316bafa8f87
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a3fe0c14d0e92e342a72a5c3c47eded6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,2 @@
1.0.0
- Initial release.
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 9b339dd67a0ce7f458236a3ad1d97322
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt
uploadId: 987062
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 314b449d3505bd24487ba69b61c2fda5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs
uploadId: 987062
@@ -0,0 +1 @@
1.0.0
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e4f9d944e2ca8484587859cf4ec80b6c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dbea3fa76aeb43b44b1f6830cafcbb78
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 940f5a2b53a9afb46bde9acd4da241dc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,268 @@
using SynapseSocket.Connections;
using SynapseSocket.Core;
using SynapseSocket.Core.Configuration;
using SynapseSocket.Core.Events;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using FishNet.Managing;
namespace FishNet.Transporting.Synapse.Client
{
/// <summary>
/// Manages the client-side Synapse connection to a remote server.
/// </summary>
public class ClientSocket : CommonSocket
{
~ClientSocket()
{
StopConnection();
}
private string _serverAddress = string.Empty;
private ushort _serverPort;
private SynapseConfig? _pendingConfig;
private SynapseManager? _manager;
private SynapseConnection? _serverConnection;
private IPEndPoint? _resolvedServerEndPoint;
private ConcurrentQueue<Packet> _incomingPackets = new();
private Queue<Packet> _outgoingPackets = new();
/// <summary>
/// Default connection timeout when none is specified.
/// </summary>
public const uint DefaultTimeoutMilliseconds = 15000;
/// <summary>
/// Initializes this socket for use.
/// </summary>
internal void Initialize(Transport transport)
{
Transport = transport;
}
/// <summary>
/// Returns the server port if the socket is running, otherwise null.
/// </summary>
internal ushort? GetPort()
{
if (_manager is null || !_manager.IsRunning)
return null;
return _serverPort;
}
/// <summary>
/// Starts a connection to the specified address and port.
/// </summary>
internal bool StartConnection(string address, ushort port, SynapseConfig config)
{
if (GetConnectionState() != LocalConnectionState.Stopped)
StopSocket();
LocalConnectionStates.Enqueue(LocalConnectionState.Starting);
IterateIncoming();
_pendingConfig = config;
_serverAddress = address;
_serverPort = port;
ResetQueues();
StartSocket();
return true;
}
/// <summary>
/// Stops the local client connection.
/// </summary>
internal bool StopConnection()
{
LocalConnectionState localConnectionState = GetConnectionState();
if (localConnectionState == LocalConnectionState.Stopped || localConnectionState == LocalConnectionState.Stopping)
return false;
SetConnectionState(LocalConnectionState.Stopping, false);
StopSocket();
return true;
}
/// <summary>
/// Processes queued incoming packets and connection state changes.
/// </summary>
internal void IterateIncoming()
{
while (LocalConnectionStates.TryDequeue(out LocalConnectionState localConnectionState))
SetConnectionState(localConnectionState, false);
LocalConnectionState state = GetConnectionState();
if (state != LocalConnectionState.Started)
{
ResetQueues();
if (state == LocalConnectionState.Stopped)
{
StopSocket();
return;
}
}
while (_incomingPackets.TryDequeue(out Packet packet))
{
ClientReceivedDataArgs dataArgs = new(packet.GetArraySegment(), (Channel)packet.Channel, Transport.Index);
Transport.HandleClientReceivedDataArgs(dataArgs);
packet.Dispose();
}
}
/// <summary>
/// Processes queued outgoing packets, sending each to the server.
/// </summary>
internal void IterateOutgoing()
{
SynapseManager? manager = _manager;
SynapseConnection? serverConnection = _serverConnection;
if (manager is null || serverConnection is null || GetConnectionState() != LocalConnectionState.Started)
{
ClearPacketQueue(ref _outgoingPackets);
return;
}
int count = _outgoingPackets.Count;
for (int i = 0; i < count; i++)
{
Packet outgoing = _outgoingPackets.Dequeue();
bool isReliable = outgoing.Channel == (byte)Channel.Reliable;
// Payload is copied inside SendAsync before the first await; safe to dispose after.
_ = manager.SendAsync(serverConnection, outgoing.GetArraySegment(), isReliable, CancellationToken.None);
outgoing.Dispose();
}
}
/// <summary>
/// Enqueues data to be sent to the server on the next IterateOutgoing call.
/// </summary>
internal void SendToServer(byte channelId, ArraySegment<byte> segment)
{
if (GetConnectionState() != LocalConnectionState.Started)
return;
Send(ref _outgoingPackets, channelId, segment, 0);
}
private void StartSocket()
{
if (!IPAddress.TryParse(_serverAddress, out IPAddress? ipAddress))
{
IPHostEntry hostEntry = Dns.GetHostEntry(_serverAddress);
if (hostEntry.AddressList.Length == 0)
{
Transport.NetworkManager.LogError($"Could not resolve server address: {_serverAddress}.");
StopSocket();
return;
}
ipAddress = hostEntry.AddressList[0];
}
_resolvedServerEndPoint = new(ipAddress, _serverPort);
SynapseConfig synapseConfig = _pendingConfig!;
synapseConfig.BindEndPoints = new() { new(IPAddress.Any, 0) };
if (synapseConfig.Security.SignatureProviderAsset != null)
synapseConfig.Security.SignatureProvider = synapseConfig.Security.SignatureProviderAsset;
if (synapseConfig.Security.SignatureValidatorAsset != null)
synapseConfig.Security.SignatureValidator = synapseConfig.Security.SignatureValidatorAsset;
_manager = new(synapseConfig);
_manager.PacketReceived += Manager_PacketReceived;
_manager.ConnectionClosed += Manager_ConnectionClosed;
_manager.ConnectionFailed += Manager_ConnectionFailed;
_manager.UnhandledException += Manager_UnhandledException;
_ = StartSocketAsync();
}
private async Task StartSocketAsync()
{
SynapseManager? manager = _manager;
IPEndPoint? resolvedServerEndPoint = _resolvedServerEndPoint;
if (manager is null || resolvedServerEndPoint is null)
return;
try
{
await manager.StartAsync(CancellationToken.None).ConfigureAwait(false);
_serverConnection = await manager.ConnectAsync(resolvedServerEndPoint, CancellationToken.None).ConfigureAwait(false);
LocalConnectionStates.Enqueue(LocalConnectionState.Started);
}
catch (Exception exception)
{
Transport.NetworkManager.LogError($"Client failed to connect to {_serverAddress}:{_serverPort} - {exception.Message}");
StopSocket();
}
}
private void StopSocket()
{
SynapseManager? manager = _manager;
_manager = null;
_serverConnection = null;
_resolvedServerEndPoint = null;
if (manager is not null)
_ = manager.StopAsync();
if (GetConnectionState() != LocalConnectionState.Stopped)
LocalConnectionStates.Enqueue(LocalConnectionState.Stopped);
}
private void ResetQueues()
{
ClearGenericQueue(ref LocalConnectionStates);
ClearPacketQueue(ref _incomingPackets);
ClearPacketQueue(ref _outgoingPackets);
}
private void Manager_PacketReceived(PacketReceivedEventArgs packetReceivedEventArgs)
{
byte channel = packetReceivedEventArgs.IsReliable ? (byte)Channel.Reliable : (byte)Channel.Unreliable;
Packet packet = new(0, packetReceivedEventArgs.Payload, channel);
_incomingPackets.Enqueue(packet);
}
private void Manager_ConnectionClosed(ConnectionEventArgs connectionEventArgs)
{
// Ignore the event if we already initiated the stop.
if (_manager is null)
return;
StopConnection();
}
private void Manager_ConnectionFailed(ConnectionFailedEventArgs connectionFailedEventArgs)
{
Transport.NetworkManager.LogWarning($"Client connection failed: {connectionFailedEventArgs.Reason} - {connectionFailedEventArgs.Message}");
StopSocket();
}
private void Manager_UnhandledException(Exception exception)
{
Transport.NetworkManager.LogError($"Unhandled exception in Synapse client transport: {exception}");
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 93658dbb858a62d4282f881cede668fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Core/ClientSocket.cs
uploadId: 987062
@@ -0,0 +1,94 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System;
namespace FishNet.Transporting.Synapse
{
/// <summary>
/// Abstract base for client and server sockets.
/// Manages local connection state and packet queuing.
/// </summary>
public abstract class CommonSocket
{
/// <summary>
/// Current local connection state.
/// </summary>
private LocalConnectionState _connectionState = LocalConnectionState.Stopped;
/// <summary>
/// Pending local connection state changes to be applied on the main thread.
/// </summary>
protected ConcurrentQueue<LocalConnectionState> LocalConnectionStates = new();
/// <summary>
/// Transport that owns this socket.
/// </summary>
protected Transport Transport;
/// <summary>
/// Returns the current local connection state.
/// </summary>
internal LocalConnectionState GetConnectionState() => _connectionState;
/// <summary>
/// Applies a new local connection state and notifies the transport.
/// </summary>
protected void SetConnectionState(LocalConnectionState connectionState, bool isServer)
{
if (connectionState == _connectionState)
return;
_connectionState = connectionState;
if (isServer)
Transport.HandleServerConnectionState(new(connectionState, Transport.Index));
else
Transport.HandleClientConnectionState(new(connectionState, Transport.Index));
}
/// <summary>
/// Enqueues an outgoing packet to the provided queue.
/// Does nothing if the socket is not started.
/// </summary>
internal void Send(ref Queue<Packet> queue, byte channelId, ArraySegment<byte> segment, int connectionId)
{
if (GetConnectionState() != LocalConnectionState.Started)
return;
Packet outgoing = new(connectionId, segment, channelId);
queue.Enqueue(outgoing);
}
/// <summary>
/// Drains all entries from a concurrent queue of any type.
/// </summary>
internal void ClearGenericQueue<T>(ref ConcurrentQueue<T> queue)
{
while (queue.TryDequeue(out _)) { }
}
/// <summary>
/// Drains and disposes all packets from a concurrent packet queue.
/// </summary>
internal void ClearPacketQueue(ref ConcurrentQueue<Packet> queue)
{
while (queue.TryDequeue(out Packet packet))
packet.Dispose();
}
/// <summary>
/// Drains and disposes all packets from a packet queue.
/// </summary>
internal void ClearPacketQueue(ref Queue<Packet> queue)
{
int count = queue.Count;
for (int i = 0; i < count; i++)
{
Packet packet = queue.Dequeue();
packet.Dispose();
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 492e34ccd3a2e514ea638b4677846296
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Core/CommonSocket.cs
uploadId: 987062
@@ -0,0 +1,370 @@
using FishNet.Connection;
using SynapseSocket.Connections;
using SynapseSocket.Core;
using SynapseSocket.Core.Configuration;
using SynapseSocket.Core.Events;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using FishNet.Managing;
namespace FishNet.Transporting.Synapse.Server
{
/// <summary>
/// Manages the server-side Synapse transport, accepting and tracking remote clients.
/// </summary>
public class ServerSocket : CommonSocket
{
~ServerSocket()
{
StopConnection();
}
private ushort _port;
private string _ipv4BindAddress = string.Empty;
private string _ipv6BindAddress = string.Empty;
private int _maximumClients;
private SynapseConfig? _pendingConfig;
private SynapseManager? _manager;
private ConcurrentDictionary<SynapseConnection, int> _connectionToId = new();
private ConcurrentDictionary<int, SynapseConnection> _idToConnection = new();
private int _nextConnectionId;
private ConcurrentQueue<Packet> _incomingPackets = new();
private Queue<Packet> _outgoingPackets = new();
private ConcurrentQueue<RemoteConnectionEvent> _remoteConnectionEvents = new();
/// <summary>
/// Default connection timeout when none is specified.
/// </summary>
public const uint DefaultTimeoutMilliseconds = 15000;
/// <summary>
/// Returns the remote connection state for a given connection ID.
/// </summary>
internal RemoteConnectionState GetConnectionState(int connectionId)
{
if (!_idToConnection.TryGetValue(connectionId, out SynapseConnection? synapseConnection))
return RemoteConnectionState.Stopped;
return synapseConnection.State == ConnectionState.Connected
? RemoteConnectionState.Started
: RemoteConnectionState.Stopped;
}
/// <summary>
/// Returns the remote endpoint address string for a given connection ID.
/// </summary>
internal string GetConnectionAddress(int connectionId)
{
if (GetConnectionState() != LocalConnectionState.Started)
{
Transport.NetworkManager.LogWarning("Server socket is not started.");
return string.Empty;
}
if (!_idToConnection.TryGetValue(connectionId, out SynapseConnection? synapseConnection))
{
Transport.NetworkManager.LogWarning($"Connection Id {connectionId} was not found.");
return string.Empty;
}
return synapseConnection.RemoteEndPoint.ToString();
}
/// <summary>
/// Returns the maximum number of clients the server accepts.
/// </summary>
internal int GetMaximumClients() =>
Math.Min(_maximumClients, NetworkConnection.MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE);
/// <summary>
/// Updates the maximum number of clients at runtime.
/// </summary>
internal void SetMaximumClients(int value)
{
_maximumClients = value;
}
/// <summary>
/// Initializes this socket for use.
/// </summary>
internal void Initialize(Transport transport)
{
Transport = transport;
}
/// <summary>
/// Returns the listening port if the server is running, otherwise null.
/// </summary>
internal ushort? GetPort()
{
if (_manager is null || !_manager.IsRunning)
return null;
return _port;
}
/// <summary>
/// Starts the server on the specified port and address.
/// </summary>
internal bool StartConnection(ushort port, int maximumClients, string ipv4BindAddress, string ipv6BindAddress, SynapseConfig config)
{
if (GetConnectionState() != LocalConnectionState.Stopped)
StopSocket();
LocalConnectionStates.Enqueue(LocalConnectionState.Starting);
IterateIncoming();
_pendingConfig = config;
_port = port;
_maximumClients = maximumClients;
_ipv4BindAddress = ipv4BindAddress;
_ipv6BindAddress = ipv6BindAddress;
ResetQueues();
StartSocket();
return true;
}
/// <summary>
/// Stops the local server.
/// </summary>
internal bool StopConnection()
{
if (_manager is null || (GetConnectionState() == LocalConnectionState.Stopped || GetConnectionState() == LocalConnectionState.Stopping))
return false;
LocalConnectionStates.Enqueue(LocalConnectionState.Stopping);
StopSocket();
return true;
}
/// <summary>
/// Disconnects a specific remote client by connection ID.
/// </summary>
internal bool StopConnection(int connectionId)
{
if (GetConnectionState() != LocalConnectionState.Started || _manager is null)
return false;
if (!_idToConnection.TryGetValue(connectionId, out SynapseConnection? synapseConnection))
return false;
_ = _manager.DisconnectAsync(synapseConnection, CancellationToken.None);
return true;
}
/// <summary>
/// Processes queued incoming packets and remote connection events.
/// </summary>
internal void IterateIncoming()
{
while (LocalConnectionStates.TryDequeue(out LocalConnectionState localConnectionState))
SetConnectionState(localConnectionState, true);
LocalConnectionState state = GetConnectionState();
if (state != LocalConnectionState.Started)
{
ResetQueues();
if (state == LocalConnectionState.Stopped)
{
StopSocket();
return;
}
}
while (_remoteConnectionEvents.TryDequeue(out RemoteConnectionEvent remoteConnectionEvent))
{
RemoteConnectionState remoteConnectionState = remoteConnectionEvent.IsConnected
? RemoteConnectionState.Started
: RemoteConnectionState.Stopped;
Transport.HandleRemoteConnectionState(new(remoteConnectionState, remoteConnectionEvent.ConnectionId, Transport.Index));
}
while (_incomingPackets.TryDequeue(out Packet packet))
{
if (_idToConnection.ContainsKey(packet.ConnectionId))
{
ServerReceivedDataArgs dataArgs = new(packet.GetArraySegment(), (Channel)packet.Channel, packet.ConnectionId, Transport.Index);
Transport.HandleServerReceivedDataArgs(dataArgs);
}
packet.Dispose();
}
}
/// <summary>
/// Processes queued outgoing packets, sending each to its target connection.
/// </summary>
internal void IterateOutgoing()
{
SynapseManager? manager = _manager;
if (manager is null || GetConnectionState() != LocalConnectionState.Started)
{
ClearPacketQueue(ref _outgoingPackets);
return;
}
int count = _outgoingPackets.Count;
for (int i = 0; i < count; i++)
{
Packet outgoing = _outgoingPackets.Dequeue();
bool isReliable = outgoing.Channel == (byte)Channel.Reliable;
if (outgoing.ConnectionId == NetworkConnection.UNSET_CLIENTID_VALUE)
{
foreach (SynapseConnection synapseConnection in _connectionToId.Keys)
_ = manager.SendAsync(synapseConnection, outgoing.GetArraySegment(), isReliable, CancellationToken.None);
}
else if (_idToConnection.TryGetValue(outgoing.ConnectionId, out SynapseConnection? synapseConnection))
{
_ = manager.SendAsync(synapseConnection, outgoing.GetArraySegment(), isReliable, CancellationToken.None);
}
outgoing.Dispose();
}
}
/// <summary>
/// Enqueues data to be sent to a specific client on the next IterateOutgoing call.
/// </summary>
internal void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
Send(ref _outgoingPackets, channelId, segment, connectionId);
}
private void StartSocket()
{
IPAddress ipv4Address;
if (string.IsNullOrEmpty(_ipv4BindAddress))
{
ipv4Address = IPAddress.Any;
}
else if (!IPAddress.TryParse(_ipv4BindAddress, out ipv4Address!))
{
Transport.NetworkManager.LogError($"IPv4 bind address {_ipv4BindAddress} failed to parse. Clear the bind address field to use any bind address.");
StopConnection();
return;
}
List<IPEndPoint> bindEndPoints = new() { new(ipv4Address, _port) };
if (!string.IsNullOrEmpty(_ipv6BindAddress))
{
if (!IPAddress.TryParse(_ipv6BindAddress, out IPAddress? ipv6Address))
{
Transport.NetworkManager.LogError($"IPv6 bind address {_ipv6BindAddress} failed to parse. Clear the IPv6 bind address field to disable IPv6 binding.");
StopConnection();
return;
}
bindEndPoints.Add(new(ipv6Address, _port));
}
SynapseConfig synapseConfig = _pendingConfig!;
synapseConfig.BindEndPoints = bindEndPoints;
synapseConfig.MaximumConcurrentConnections = (uint)_maximumClients;
if (synapseConfig.Security.SignatureProviderAsset != null)
synapseConfig.Security.SignatureProvider = synapseConfig.Security.SignatureProviderAsset;
if (synapseConfig.Security.SignatureValidatorAsset != null)
synapseConfig.Security.SignatureValidator = synapseConfig.Security.SignatureValidatorAsset;
_manager = new(synapseConfig);
_manager.ConnectionEstablished += Manager_ConnectionEstablished;
_manager.ConnectionClosed += Manager_ConnectionClosed;
_manager.PacketReceived += Manager_PacketReceived;
_manager.UnhandledException += Manager_UnhandledException;
_ = StartSocketAsync();
}
private async Task StartSocketAsync()
{
SynapseManager? manager = _manager;
if (manager is null)
return;
try
{
await manager.StartAsync(CancellationToken.None).ConfigureAwait(false);
LocalConnectionStates.Enqueue(LocalConnectionState.Started);
}
catch (Exception exception)
{
Transport.NetworkManager.LogError($"Server failed to start: {exception.Message}");
StopSocket();
}
}
private void StopSocket()
{
SynapseManager? manager = _manager;
_manager = null;
if (manager is not null)
_ = manager.StopAsync();
if (GetConnectionState() != LocalConnectionState.Stopped)
LocalConnectionStates.Enqueue(LocalConnectionState.Stopped);
}
private void ResetQueues()
{
ClearGenericQueue(ref LocalConnectionStates);
ClearPacketQueue(ref _incomingPackets);
ClearPacketQueue(ref _outgoingPackets);
ClearGenericQueue(ref _remoteConnectionEvents);
_connectionToId.Clear();
_idToConnection.Clear();
_nextConnectionId = 0;
}
private void Manager_ConnectionEstablished(ConnectionEventArgs connectionEventArgs)
{
int connectionId = Interlocked.Increment(ref _nextConnectionId) - 1;
_connectionToId[connectionEventArgs.Connection] = connectionId;
_idToConnection[connectionId] = connectionEventArgs.Connection;
_remoteConnectionEvents.Enqueue(new(true, connectionId));
}
private void Manager_ConnectionClosed(ConnectionEventArgs connectionEventArgs)
{
if (!_connectionToId.TryRemove(connectionEventArgs.Connection, out int connectionId))
return;
_idToConnection.TryRemove(connectionId, out _);
_remoteConnectionEvents.Enqueue(new(false, connectionId));
}
private void Manager_PacketReceived(PacketReceivedEventArgs packetReceivedEventArgs)
{
if (!_connectionToId.TryGetValue(packetReceivedEventArgs.Connection, out int connectionId))
return;
byte channel = packetReceivedEventArgs.IsReliable ? (byte)Channel.Reliable : (byte)Channel.Unreliable;
Packet packet = new(connectionId, packetReceivedEventArgs.Payload, channel);
_incomingPackets.Enqueue(packet);
}
private void Manager_UnhandledException(Exception exception)
{
Transport.NetworkManager.LogError($"Unhandled exception in Synapse server transport: {exception}");
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d9e3840ecbee44b4dac14c1022eeb441
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Core/ServerSocket.cs
uploadId: 987062
@@ -0,0 +1,47 @@
using FishNet.Utility.Performance;
using System;
namespace FishNet.Transporting.Synapse
{
internal struct Packet
{
public readonly int ConnectionId;
public readonly byte[] Data;
public readonly int Length;
public readonly byte Channel;
public Packet(int connectionId, byte[] data, int length, byte channel)
{
ConnectionId = connectionId;
Data = data;
Length = length;
Channel = channel;
}
public Packet(int connectionId, ArraySegment<byte> segment, byte channel)
{
Data = ByteArrayPool.Retrieve(segment.Count);
Buffer.BlockCopy(segment.Array, segment.Offset, Data, 0, segment.Count);
ConnectionId = connectionId;
Length = segment.Count;
Channel = channel;
}
public ArraySegment<byte> GetArraySegment() => new(Data, 0, Length);
public void Dispose() => ByteArrayPool.Store(Data);
}
internal struct RemoteConnectionEvent
{
public readonly bool IsConnected;
public readonly int ConnectionId;
public RemoteConnectionEvent(bool isConnected, int connectionId)
{
IsConnected = isConnected;
ConnectionId = connectionId;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 14c3984a670aa5a4e829e3b165b80c87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Core/Supporting.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: db3e0776ef8fe584c831c880b2a1ea35
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,523 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using SynapseSocket.Core.Configuration;
using UnityEditor;
using UnityEngine;
namespace FishNet.Transporting.Synapse.Editing
{
[CustomPropertyDrawer(typeof(ConnectionConfig))]
public class ConnectionConfigDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
return lineHeight * 3f;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(rect, property.FindPropertyRelative("KeepAliveIntervalMilliseconds"), new GUIContent("Keep Alive Interval", "How often keep-alive packets are sent (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("TimeoutMilliseconds"), new GUIContent("Timeout", "Time without a response before a connection is considered lost (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SweepWindowMilliseconds"), new GUIContent("Sweep Window", "Interval for sweeping stale connection state (ms)."));
}
}
[CustomPropertyDrawer(typeof(ReliableConfig))]
public class ReliableConfigDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
float height = lineHeight * 4f;
SerializedProperty ackBatching = property.FindPropertyRelative("AckBatchingEnabled");
if (ackBatching != null && ackBatching.boolValue)
height += lineHeight;
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumPending"), new GUIContent("Max Pending", "Maximum number of unacknowledged reliable packets before the connection stalls."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("ResendMilliseconds"), new GUIContent("Resend Interval", "Time to wait before resending an unacknowledged reliable packet (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumRetries"), new GUIContent("Max Retries", "Maximum number of resend attempts before the connection is dropped."));
rect.y += lineHeight;
SerializedProperty ackBatching = property.FindPropertyRelative("AckBatchingEnabled");
EditorGUI.PropertyField(rect, ackBatching, new GUIContent("Ack Batching", "Batch multiple acknowledgements into a single packet to reduce overhead."));
rect.y += lineHeight;
if (ackBatching != null && ackBatching.boolValue)
{
EditorGUI.indentLevel++;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("AckBatchIntervalMilliseconds"), new GUIContent("Ack Batch Interval", "How long to wait before flushing a batched acknowledgement (ms)."));
EditorGUI.indentLevel--;
}
}
}
[CustomPropertyDrawer(typeof(LatencySimulatorConfig))]
public class LatencySimulatorConfigDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
float height = lineHeight;
SerializedProperty isEnabled = property.FindPropertyRelative("IsEnabled");
if (isEnabled != null && isEnabled.boolValue)
height += lineHeight * 5f;
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
SerializedProperty isEnabled = property.FindPropertyRelative("IsEnabled");
EditorGUI.PropertyField(rect, isEnabled, new GUIContent("Is Enabled", "Enable the latency simulator to test under simulated network conditions."));
rect.y += lineHeight;
if (isEnabled != null && isEnabled.boolValue)
{
EditorGUI.indentLevel++;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("BaseLatencyMilliseconds"), new GUIContent("Base Latency", "Fixed delay added to all packets (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("JitterMilliseconds"), new GUIContent("Jitter", "Random delay variation added on top of base latency (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("PacketLossChance"), new GUIContent("Loss Chance", "Probability that a packet is dropped (0–1)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("ReorderChance"), new GUIContent("Reorder Chance", "Probability that a packet is delivered out of order (0–1)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("OutOfOrderExtraDelayMilliseconds"), new GUIContent("OOO Delay", "Extra delay applied to reordered packets (ms)."));
EditorGUI.indentLevel--;
}
}
}
[CustomPropertyDrawer(typeof(NatTraversalConfig))]
public class NatTraversalConfigDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
float height = lineHeight;
SerializedProperty mode = property.FindPropertyRelative("Mode");
if (mode != null && mode.enumValueIndex != 0)
{
height += lineHeight * 3f;
SerializedProperty fullCone = property.FindPropertyRelative("FullCone");
if (fullCone != null)
height += EditorGUI.GetPropertyHeight(fullCone, true);
}
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
SerializedProperty mode = property.FindPropertyRelative("Mode");
EditorGUI.PropertyField(rect, mode, new GUIContent("Mode", "NAT traversal strategy to use when connecting through a NAT."));
rect.y += lineHeight;
if (mode != null && mode.enumValueIndex != 0)
{
EditorGUI.indentLevel++;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("ProbeCount"), new GUIContent("Probe Count", "Number of probe packets sent per traversal attempt."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("IntervalMilliseconds"), new GUIContent("Interval", "Time between probe packets (ms)."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumAttempts"), new GUIContent("Max Attempts", "Maximum number of traversal attempts before giving up."));
rect.y += lineHeight;
SerializedProperty fullCone = property.FindPropertyRelative("FullCone");
if (fullCone != null)
{
float fullConeHeight = EditorGUI.GetPropertyHeight(fullCone, true);
rect.height = fullConeHeight;
EditorGUI.PropertyField(rect, fullCone, new("Full Cone", "Settings for full-cone NAT traversal."), true);
rect.height = EditorGUIUtility.singleLineHeight;
}
EditorGUI.indentLevel--;
}
}
}
[CustomPropertyDrawer(typeof(SecurityConfig))]
public class SecurityConfigDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
return lineHeight * 8f;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SignatureProviderAsset"), new GUIContent("Sig Provider", "Asset that generates packet signatures for outgoing packets."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SignatureValidatorAsset"), new GUIContent("Sig Validator", "Asset that validates packet signatures on incoming packets."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumPacketsPerSecond"), new GUIContent("Max Packets/Sec", "Maximum packets allowed per second per connection before triggering a violation."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumBytesPerSecond"), new GUIContent("Max Bytes/Sec", "Maximum bytes allowed per second per connection before triggering a violation."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumOutOfOrderReliablePackets"), new GUIContent("Max OOO Reliable", "Maximum out-of-order reliable packets buffered before the connection is dropped."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumReassembledPacketSize"), new GUIContent("Max Reassembled Size", "Maximum size in bytes of a fully reassembled segmented packet."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("AllowUnknownPackets"), new GUIContent("Allow Unknown", "Allow packets with unrecognized types instead of dropping them."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("DisableHandshakeReplayProtection"), new GUIContent("Disable Replay Protection", "Disable handshake replay protection. Not recommended in production."));
}
}
[CustomPropertyDrawer(typeof(SynapseConfig))]
public class SynapseConfigDrawer : PropertyDrawer
{
private static readonly Dictionary<string, bool> _foldouts = new();
private static bool GetFoldout(string key, bool defaultExpanded = true)
{
if (!_foldouts.TryGetValue(key, out bool expanded))
{
expanded = defaultExpanded;
_foldouts[key] = expanded;
}
return expanded;
}
private static bool DrawFoldout(Rect rect, string key, string label)
{
bool current = GetFoldout(key);
bool next = EditorGUI.Foldout(rect, current, label, true, EditorStyles.foldoutHeader);
if (next != current)
_foldouts[key] = next;
return next;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
float height = 0f;
// Performance
height += lineHeight;
if (GetFoldout(property.propertyPath + ".perf"))
height += lineHeight * 4f;
// Segmentation
height += lineHeight;
if (GetFoldout(property.propertyPath + ".seg"))
{
height += lineHeight;
SerializedProperty segmentMode = property.FindPropertyRelative("UnreliableSegmentMode");
if (segmentMode != null && segmentMode.enumValueIndex != 0)
height += lineHeight * 3f;
}
// Reliability
height += lineHeight;
if (GetFoldout(property.propertyPath + ".rel"))
{
SerializedProperty reliable = property.FindPropertyRelative("Reliable");
if (reliable != null)
height += EditorGUI.GetPropertyHeight(reliable, false);
}
// Connection
height += lineHeight;
if (GetFoldout(property.propertyPath + ".con"))
{
SerializedProperty connection = property.FindPropertyRelative("Connection");
if (connection != null)
height += EditorGUI.GetPropertyHeight(connection, false);
}
// Misc
height += lineHeight;
if (GetFoldout(property.propertyPath + ".misc"))
height += lineHeight * 2f;
// Security
height += lineHeight;
if (GetFoldout(property.propertyPath + ".sec"))
{
SerializedProperty security = property.FindPropertyRelative("Security");
if (security != null)
height += EditorGUI.GetPropertyHeight(security, false);
}
// Latency Simulator
height += lineHeight;
if (GetFoldout(property.propertyPath + ".lat"))
{
SerializedProperty latencySimulator = property.FindPropertyRelative("LatencySimulator");
if (latencySimulator != null)
height += EditorGUI.GetPropertyHeight(latencySimulator, false);
}
// NAT Traversal
height += lineHeight;
if (GetFoldout(property.propertyPath + ".nat"))
{
SerializedProperty natTraversal = property.FindPropertyRelative("NatTraversal");
if (natTraversal != null)
height += EditorGUI.GetPropertyHeight(natTraversal, false);
}
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
Rect rect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
// Performance
bool perfExpanded = DrawFoldout(rect, property.propertyPath + ".perf", "Performance");
rect.y += lineHeight;
if (perfExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumPacketSize"), new GUIContent("Max Packet Size", "Maximum size in bytes of a single outgoing packet before it is segmented."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumTransmissionUnit"), new GUIContent("MTU", "Maximum transmission unit reported to the upper layer for packet sizing decisions."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SocketReceiveBufferBytes"), new GUIContent("Recv Buffer", "UDP socket receive buffer size in bytes."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SocketSendBufferBytes"), new GUIContent("Send Buffer", "UDP socket send buffer size in bytes."));
rect.y += lineHeight;
EditorGUI.indentLevel--;
}
// Segmentation
bool segExpanded = DrawFoldout(rect, property.propertyPath + ".seg", "Segmentation");
rect.y += lineHeight;
if (segExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty segmentMode = property.FindPropertyRelative("UnreliableSegmentMode");
EditorGUI.PropertyField(rect, segmentMode, new GUIContent("Segment Mode", "Controls how large unreliable packets are split across multiple datagrams."));
rect.y += lineHeight;
if (segmentMode != null && segmentMode.enumValueIndex != 0)
{
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumSegments"), new GUIContent("Max Segments", "Maximum number of fragments a single packet may be split into."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("MaximumConcurrentSegmentAssembliesPerConnection"), new GUIContent("Max Concurrent Assemblies", "Maximum number of in-progress segment assemblies allowed per connection."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("SegmentAssemblyTimeoutMilliseconds"), new GUIContent("Assembly Timeout", "Time before an incomplete segment assembly is discarded (ms)."));
rect.y += lineHeight;
}
EditorGUI.indentLevel--;
}
// Reliability
bool relExpanded = DrawFoldout(rect, property.propertyPath + ".rel", "Reliability");
rect.y += lineHeight;
if (relExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty reliable = property.FindPropertyRelative("Reliable");
if (reliable != null)
{
float reliableHeight = EditorGUI.GetPropertyHeight(reliable, false);
rect.height = reliableHeight;
EditorGUI.PropertyField(rect, reliable, false);
rect.height = EditorGUIUtility.singleLineHeight;
rect.y += reliableHeight;
}
EditorGUI.indentLevel--;
}
// Connection
bool conExpanded = DrawFoldout(rect, property.propertyPath + ".con", "Connection");
rect.y += lineHeight;
if (conExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty connection = property.FindPropertyRelative("Connection");
if (connection != null)
{
float connectionHeight = EditorGUI.GetPropertyHeight(connection, false);
rect.height = connectionHeight;
EditorGUI.PropertyField(rect, connection, false);
rect.height = EditorGUIUtility.singleLineHeight;
rect.y += connectionHeight;
}
EditorGUI.indentLevel--;
}
// Misc
bool miscExpanded = DrawFoldout(rect, property.propertyPath + ".misc", "Misc");
rect.y += lineHeight;
if (miscExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("CopyReceivedPayloads"), new GUIContent("Copy Payloads", "Copy received payloads before delivering them, allowing safe access after the callback returns."));
rect.y += lineHeight;
EditorGUI.PropertyField(rect, property.FindPropertyRelative("EnableTelemetry"), new GUIContent("Enable Telemetry", "Collect and expose internal transport metrics."));
rect.y += lineHeight;
EditorGUI.indentLevel--;
}
// Security
bool secExpanded = DrawFoldout(rect, property.propertyPath + ".sec", "Security");
rect.y += lineHeight;
if (secExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty security = property.FindPropertyRelative("Security");
if (security != null)
{
float securityHeight = EditorGUI.GetPropertyHeight(security, false);
rect.height = securityHeight;
EditorGUI.PropertyField(rect, security, false);
rect.height = EditorGUIUtility.singleLineHeight;
rect.y += securityHeight;
}
EditorGUI.indentLevel--;
}
// Latency Simulator
bool latExpanded = DrawFoldout(rect, property.propertyPath + ".lat", "Latency Simulator");
rect.y += lineHeight;
if (latExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty latencySimulator = property.FindPropertyRelative("LatencySimulator");
if (latencySimulator != null)
{
float latencyHeight = EditorGUI.GetPropertyHeight(latencySimulator, false);
rect.height = latencyHeight;
EditorGUI.PropertyField(rect, latencySimulator, false);
rect.height = EditorGUIUtility.singleLineHeight;
rect.y += latencyHeight;
}
EditorGUI.indentLevel--;
}
// NAT Traversal
bool natExpanded = DrawFoldout(rect, property.propertyPath + ".nat", "NAT Traversal");
rect.y += lineHeight;
if (natExpanded)
{
EditorGUI.indentLevel++;
SerializedProperty natTraversal = property.FindPropertyRelative("NatTraversal");
if (natTraversal != null)
{
float natHeight = EditorGUI.GetPropertyHeight(natTraversal, false);
rect.height = natHeight;
EditorGUI.PropertyField(rect, natTraversal, false);
rect.height = EditorGUIUtility.singleLineHeight;
rect.y += natHeight;
}
EditorGUI.indentLevel--;
}
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f70aad99fd0c6484ebf628ec2a19d197
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Editor/SynapseConfigDrawers.cs
uploadId: 987062
@@ -0,0 +1,66 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace FishNet.Transporting.Synapse.Editing
{
[CustomEditor(typeof(Synapse), true)]
[CanEditMultipleObjects]
public class SynapseEditor : Editor
{
private SerializedProperty _ipv4BindAddress;
private SerializedProperty _ipv6BindAddress;
private SerializedProperty _port;
private SerializedProperty _maximumClients;
private SerializedProperty _clientAddress;
private SerializedProperty _synapseConfig;
protected virtual void OnEnable()
{
_ipv4BindAddress = serializedObject.FindProperty(nameof(_ipv4BindAddress));
_ipv6BindAddress = serializedObject.FindProperty(nameof(_ipv6BindAddress));
_port = serializedObject.FindProperty(nameof(_port));
_maximumClients = serializedObject.FindProperty(nameof(_maximumClients));
_clientAddress = serializedObject.FindProperty(nameof(_clientAddress));
_synapseConfig = serializedObject.FindProperty(nameof(_synapseConfig));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
Synapse synapse = (Synapse)target;
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(synapse), typeof(Synapse), false);
GUI.enabled = true;
EditorGUILayout.LabelField("Server", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_ipv4BindAddress, new GUIContent("IPv4 Bind Address", "IPv4 address to bind the server to. Leave empty to bind to all interfaces."));
EditorGUILayout.PropertyField(_ipv6BindAddress, new GUIContent("IPv6 Bind Address", "IPv6 address to bind the server to. Leave empty to disable IPv6 binding."));
EditorGUILayout.PropertyField(_port, new GUIContent("Port", "Port used by both server and client."));
EditorGUILayout.PropertyField(_maximumClients, new GUIContent("Max Clients", "Maximum number of clients that may be connected at once."));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Client", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_clientAddress, new GUIContent("Client Address", "Address the client will connect to."));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
_synapseConfig.isExpanded = EditorGUILayout.Foldout(_synapseConfig.isExpanded, "Configuration", true, EditorStyles.foldoutHeader);
if (_synapseConfig.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_synapseConfig, GUIContent.none, true);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 23c16c06a24739444839b7cd87b9dcb1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Editor/SynapseEditor.cs
uploadId: 987062
@@ -0,0 +1,16 @@
{
"name": "SynapseSocket",
"rootNamespace": "",
"references": [
"GUID:7c88a4a7926ee5145ad2dfa06f454c67"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: aca4fca328231064c9c1fa8cccd971cf
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Synapse.asmdef
uploadId: 987062
@@ -0,0 +1,426 @@
using FishNet.Managing;
using FishNet.Managing.Transporting;
using System;
using SynapseSocket.Core.Configuration;
using UnityEngine;
namespace FishNet.Transporting.Synapse
{
/// <summary>
/// FishNet transport backed by the SynapseSocket UDP networking library.
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("FishNet/Transport/Synapse")]
public class Synapse : Transport
{
~Synapse()
{
Shutdown();
}
/* Server. */
/// <summary>
/// IPv4 address to bind the server to. Leave empty to bind to all interfaces.
/// </summary>
[Tooltip("IPv4 address to bind the server to. Leave empty to bind to all interfaces.")]
[SerializeField]
private string _ipv4BindAddress = string.Empty;
/// <summary>
/// IPv6 address to bind the server to. Leave empty to disable IPv6 binding.
/// </summary>
[Tooltip("IPv6 address to bind the server to. Leave empty to disable IPv6 binding.")]
[SerializeField]
private string _ipv6BindAddress = string.Empty;
/// <summary>
/// Port used by both server and client.
/// </summary>
[Tooltip("Port used by both server and client.")]
[SerializeField]
private ushort _port = 7770;
/// <summary>
/// Maximum number of clients that may be connected at once.
/// </summary>
[Tooltip("Maximum number of clients that may be connected at once.")]
[Range(1, 9999)]
[SerializeField]
private int _maximumClients = 4095;
/* Client. */
/// <summary>
/// Address the client will connect to.
/// </summary>
[Tooltip("Address the client will connect to.")]
[SerializeField]
private string _clientAddress = "localhost";
/// <summary>
/// Advanced Synapse configuration applied when starting the server or client.
/// </summary>
[Tooltip("Advanced Synapse configuration applied when starting the server or client.")]
[SerializeField]
private SynapseConfig _synapseConfig = new();
/// <summary>
/// Server socket. Exposed for advanced use; access with caution outside this class.
/// </summary>
public Server.ServerSocket ServerSocket = new();
/// <summary>
/// Client socket. Exposed for advanced use; access with caution outside this class.
/// </summary>
public Client.ClientSocket ClientSocket = new();
/// <summary>
/// MTU returned to FishNet for packet sizing decisions.
/// Synapse handles segmentation internally, so this reflects the per-segment wire limit.
/// </summary>
private const int MaximumTransmissionUnit = 1200;
#region Initialization.
/// <summary>
/// Called by FishNet to initialize this transport.
/// </summary>
public override void Initialize(NetworkManager networkManager, int transportIndex)
{
base.Initialize(networkManager, transportIndex);
}
private void OnDestroy()
{
Shutdown();
}
#endregion
#region Connection states.
/// <summary>
/// Called when the local client connection state changes.
/// </summary>
public override event Action<ClientConnectionStateArgs> OnClientConnectionState;
/// <summary>
/// Called when the local server connection state changes.
/// </summary>
public override event Action<ServerConnectionStateArgs> OnServerConnectionState;
/// <summary>
/// Called when a remote client connection state changes.
/// </summary>
public override event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
/// <summary>
/// Returns the local connection state for either the server or client.
/// </summary>
public override LocalConnectionState GetConnectionState(bool isServer)
{
if (isServer)
return ServerSocket.GetConnectionState();
else
return ClientSocket.GetConnectionState();
}
/// <summary>
/// Returns the connection state of a remote client on the server.
/// </summary>
public override RemoteConnectionState GetConnectionState(int connectionId)
{
return ServerSocket.GetConnectionState(connectionId);
}
/// <summary>
/// Returns the remote endpoint address for a given connection ID.
/// </summary>
public override string GetConnectionAddress(int connectionId)
{
return ServerSocket.GetConnectionAddress(connectionId);
}
/// <summary>
/// Forwards a client connection state change to subscribers.
/// </summary>
public override void HandleClientConnectionState(ClientConnectionStateArgs clientConnectionStateArgs)
{
OnClientConnectionState?.Invoke(clientConnectionStateArgs);
}
/// <summary>
/// Forwards a server connection state change to subscribers.
/// </summary>
public override void HandleServerConnectionState(ServerConnectionStateArgs serverConnectionStateArgs)
{
OnServerConnectionState?.Invoke(serverConnectionStateArgs);
}
/// <summary>
/// Forwards a remote client connection state change to subscribers.
/// </summary>
public override void HandleRemoteConnectionState(RemoteConnectionStateArgs remoteConnectionStateArgs)
{
OnRemoteConnectionState?.Invoke(remoteConnectionStateArgs);
}
#endregion
#region Iterating.
/// <summary>
/// Processes data received by the socket.
/// </summary>
public override void IterateIncoming(bool isServer)
{
if (isServer)
ServerSocket.IterateIncoming();
else
ClientSocket.IterateIncoming();
}
/// <summary>
/// Processes data to be sent by the socket.
/// </summary>
public override void IterateOutgoing(bool isServer)
{
if (isServer)
ServerSocket.IterateOutgoing();
else
ClientSocket.IterateOutgoing();
}
#endregion
#region Sending.
/// <summary>
/// Sends data from the local client to the server.
/// </summary>
public override void SendToServer(byte channelId, ArraySegment<byte> segment)
{
SanitizeChannel(ref channelId);
ClientSocket.SendToServer(channelId, segment);
}
/// <summary>
/// Sends data from the server to a specific client.
/// </summary>
public override void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
SanitizeChannel(ref channelId);
ServerSocket.SendToClient(channelId, segment, connectionId);
}
#endregion
#region Receiving.
/// <summary>
/// Called when the client receives data.
/// </summary>
public override event Action<ClientReceivedDataArgs> OnClientReceivedData;
/// <summary>
/// Forwards received client data to subscribers.
/// </summary>
public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs clientReceivedDataArgs)
{
OnClientReceivedData?.Invoke(clientReceivedDataArgs);
}
/// <summary>
/// Called when the server receives data.
/// </summary>
public override event Action<ServerReceivedDataArgs> OnServerReceivedData;
/// <summary>
/// Forwards received server data to subscribers.
/// </summary>
public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs serverReceivedDataArgs)
{
OnServerReceivedData?.Invoke(serverReceivedDataArgs);
}
#endregion
#region Configuration.
/// <summary>
/// Returns how long in seconds until a connection times out.
/// </summary>
public override float GetTimeout(bool isServer)
{
return _synapseConfig.Connection.TimeoutMilliseconds / 1000f;
}
/// <summary>
/// Sets how long in seconds until a connection times out.
/// Takes effect on the next connection start.
/// </summary>
public override void SetTimeout(float value, bool isServer)
{
_synapseConfig.Connection.TimeoutMilliseconds = (uint)(Math.Max(0f, value) * 1000f);
}
/// <summary>
/// Returns the maximum number of clients allowed to connect.
/// </summary>
public override int GetMaximumClients()
{
return ServerSocket.GetMaximumClients();
}
/// <summary>
/// Sets the maximum number of clients allowed to connect.
/// </summary>
public override void SetMaximumClients(int value)
{
_maximumClients = value;
ServerSocket.SetMaximumClients(value);
}
/// <summary>
/// Sets the address the client will connect to.
/// </summary>
public override void SetClientAddress(string address)
{
_clientAddress = address;
}
/// <summary>
/// Returns the address the client will connect to.
/// </summary>
public override string GetClientAddress()
{
return _clientAddress;
}
/// <summary>
/// Sets the address the server will bind to.
/// </summary>
public override void SetServerBindAddress(string address, IPAddressType addressType)
{
if (addressType == IPAddressType.IPv4)
_ipv4BindAddress = address;
else
_ipv6BindAddress = address;
}
/// <summary>
/// Returns the address the server will bind to.
/// </summary>
public override string GetServerBindAddress(IPAddressType addressType)
{
return addressType == IPAddressType.IPv4 ? _ipv4BindAddress : _ipv6BindAddress;
}
/// <summary>
/// Sets the port used by both server and client.
/// </summary>
public override void SetPort(ushort port)
{
_port = port;
}
/// <summary>
/// Returns the active port. Prefers the server port, then the client port, then the configured value.
/// </summary>
public override ushort GetPort()
{
ushort? serverPort = ServerSocket?.GetPort();
if (serverPort.HasValue)
return serverPort.Value;
ushort? clientPort = ClientSocket?.GetPort();
if (clientPort.HasValue)
return clientPort.Value;
return _port;
}
#endregion
#region Start and stop.
/// <summary>
/// Starts the local server or client.
/// </summary>
public override bool StartConnection(bool isServer)
{
if (isServer)
return StartServer();
return StartClient(_clientAddress);
}
/// <summary>
/// Stops the local server or client.
/// </summary>
public override bool StopConnection(bool isServer)
{
if (isServer)
return StopServer();
return StopClient();
}
/// <summary>
/// Stops a remote client from the server.
/// </summary>
public override bool StopConnection(int connectionId, bool immediately)
{
return ServerSocket.StopConnection(connectionId);
}
/// <summary>
/// Stops both the server and client.
/// </summary>
public override void Shutdown()
{
StopConnection(false);
StopConnection(true);
}
private bool StartServer()
{
ServerSocket.Initialize(this);
return ServerSocket.StartConnection(_port, _maximumClients, _ipv4BindAddress, _ipv6BindAddress, _synapseConfig);
}
private bool StopServer()
{
if (ServerSocket is null)
return false;
return ServerSocket.StopConnection();
}
private bool StartClient(string address)
{
ClientSocket.Initialize(this);
return ClientSocket.StartConnection(address, _port, _synapseConfig);
}
private bool StopClient()
{
if (ClientSocket is null)
return false;
return ClientSocket.StopConnection();
}
#endregion
#region Channels.
/// <summary>
/// Clamps an invalid channel ID to reliable (0).
/// </summary>
private void SanitizeChannel(ref byte channelId)
{
if (channelId >= TransportManager.CHANNEL_COUNT)
{
NetworkManager.LogWarning($"Channel {channelId} is out of range. Defaulting to reliable.");
channelId = 0;
}
}
/// <summary>
/// Returns the MTU for the given channel.
/// Synapse handles segmentation internally; this value guides FishNet's packet sizing.
/// </summary>
public override int GetMTU(byte channel)
{
return MaximumTransmissionUnit;
}
#endregion
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a9dcdd54dc4d60c47a1cf5cde3450c47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/Synapse.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3ef946b2e8b58064792753e801ad224d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 789a9c2912bfd734799ee3c31240dfe5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 385e3bf47ee12f24682dd4103f561388
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,48 @@
using System;
namespace CodeBoost.CodeAnalysis
{
/// <summary>
/// Include this source when creating signatures.
/// </summary>
[AttributeUsage(AttributeTargets.All, Inherited = false)]
internal sealed class CreateSignatureAttribute : Attribute { }
/// <summary>
/// Ignore this source when creating signatures.
/// </summary>
[AttributeUsage(AttributeTargets.All, Inherited = false)]
[CreateSignature]
internal sealed class IgnoreSignatureAttribute : Attribute { }
/// <summary>
/// Preserve the logic of this source when creating signatures.
/// </summary>
[AttributeUsage(validOn: AttributeTargets.Method | AttributeTargets.Property, Inherited = false)]
[CreateSignature]
internal sealed class PreserveLogicAttribute : Attribute { }
/// <summary>
/// Indicates that a member must be reset when an object is pooled.
/// </summary>
/// <remarks>While this attribute is present code generation should ensure the value is reset within IPoolResettable.OnReturn or PoolResettableAttribute methods.</remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
[CreateSignature]
public sealed class PoolResettableMemberAttribute : Attribute { }
/// <summary>
/// Indicates that a method handles resetting or initializing one or more members attributed with PoolResettableMemberAttribute.
/// </summary>
/// <remarks>Attributed methods are expected to execute when appropriate to reset or initializing members.</remarks>
[AttributeUsage(AttributeTargets.Method)]
[CreateSignature]
public sealed class PoolResettableMethodAttribute : Attribute { }
/// <summary>
/// Indicates that a member must be disposed when an object is pooled.
/// </summary>
/// <remarks>While this attribute is present code generation should ensure the value is disposed within IPoolResettable.OnReturn or PoolResettableAttribute methods.</remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
[CreateSignature]
public sealed class PoolDisposableMemberAttribute : Attribute { }
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3ce1f191f0c2f1340a822102f7344a40
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/CodeAnalysis/Attributes.cs
uploadId: 987062
@@ -0,0 +1,16 @@
{
"name": "GameKit.Dependencies",
"rootNamespace": "",
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 3f63d6f7a8daa2e49bcf0602c329e95e
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/CodeBoost.xasmdef
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 913bfea2739371c46a3056a6a6d8e18a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,78 @@
using CodeBoost.Extensions;
namespace CodeBoost.Environment
{
/// <summary>
/// A static ApplicationState which uses the currently registered IApplicationState.
/// </summary>
public static class ApplicationState
{
static ApplicationState()
{
ApplicationStateService.ApplicationStateSet += ApplicationStateService_OnApplicationStateSet;
/* If an IApplicationState is already set then call
* value set callback to initialize for it. */
if (ApplicationStateService.ApplicationState is not null)
ApplicationStateService_OnApplicationStateSet(previousApplicationState: null, ApplicationStateService.ApplicationState);
}
private static void ApplicationStateService_OnApplicationStateSet(IApplicationState previousApplicationState, IApplicationState applicationState)
{
if (previousApplicationState is not null)
previousApplicationState.FocusChanged -= ApplicationState_OnFocusChanged;
if (applicationState is not null)
applicationState.FocusChanged += ApplicationState_OnFocusChanged;
}
/// <summary>
/// Called when the application focus state changes.
/// </summary>
public static event IApplicationState.FocusChangeEventHandler FocusChanged;
/// <summary>
/// Callback for when the
/// </summary>
/// <param name="isFocused"></param>
private static void ApplicationState_OnFocusChanged(bool isFocused) => FocusChanged?.Invoke(isFocused);
/// <summary>
/// True if the application is quitting.
/// </summary>
public static bool IsQuitting() => ApplicationStateService.IsQuitting();
/// <summary>
/// True if the application is playing.
/// </summary>
public static bool IsPlaying() => ApplicationStateService.IsPlaying();
/// <summary>
/// Quits the application for editor or builds.
/// </summary>
public static void Quit() => ApplicationStateService.Quit();
/// <summary>
/// True if the application is being run within an editor.
/// </summary>
public static bool IsEditor() => ApplicationStateService.IsEditor();
/// <summary>
/// True if the application is a build with development or debugging enabled.
/// </summary>
/// >
public static bool IsDevelopmentBuild() => ApplicationStateService.IsDevelopmentBuild();
/// <summary>
/// True if a GUI build, such as a client build.
/// </summary>
public static bool IsGuiBuild() => ApplicationStateService.IsGuiBuild();
/// <summary>
/// True if a headless build, such as a server build.
/// </summary>
public static bool IsHeadlessBuild() => ApplicationStateService.IsHeadlessBuild();
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 51445f8405b41d345aaafa14b1e4e3c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Environment/ApplicationState.cs
uploadId: 987062
@@ -0,0 +1,120 @@
using CodeBoost.Extensions;
namespace CodeBoost.Environment
{
public static class ApplicationStateService
{
/// <summary>
/// Called when ApplicationState is set.
/// </summary>
internal static event ApplicationStateSetEventHandler ApplicationStateSet;
internal delegate void ApplicationStateSetEventHandler(IApplicationState previousApplicationState, IApplicationState nextApplicationState);
/// <summary>
/// ILogger to use.
/// </summary>
internal static IApplicationState ApplicationState;
/// <summary>
/// Message when trying to access ApplicationState when there is not an instance created.
/// </summary>
private static readonly string ApplicationStateIsNullMessage = $"[{nameof(ApplicationState)}] is null. Use [{nameof(ApplicationStateService)}] to set a service.";
static ApplicationStateService()
{
UseApplicationState(new IdeApplicationState());
}
/// <summary>
/// Specifies which ILogger to use.
/// </summary>
public static void UseApplicationState(IApplicationState applicationState)
{
IApplicationState previousApplicationState = ApplicationState;
ApplicationState = applicationState;
ApplicationStateSet?.Invoke(previousApplicationState, applicationState);
}
/// <summary>
/// True if the application is quitting.
/// </summary>
internal static bool IsQuitting()
{
if (ApplicationState is not null)
return ApplicationState.IsQuitting();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// True if the application is playing.
/// </summary>
internal static bool IsPlaying()
{
if (ApplicationState is not null)
return ApplicationState.IsQuitting();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// Quits the application for editor or builds.
/// </summary>
internal static void Quit()
{
if (ApplicationState is not null)
ApplicationState.Quit();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// True if the application is being run within an editor.
/// </summary>
public static bool IsEditor()
{
if (ApplicationState is not null)
return ApplicationState.IsEditor();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// True if the application is a build with development or debugging enabled.
/// </summary>
/// >
public static bool IsDevelopmentBuild()
{
if (ApplicationState is not null)
return ApplicationState.IsDevelopment();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// True if a GUI build, such as a client build.
/// </summary>
public static bool IsGuiBuild()
{
if (ApplicationState is not null)
return ApplicationState.IsGuiBuild();
throw new(ApplicationStateIsNullMessage);
}
/// <summary>
/// True if a headless build, such as a server build.
/// </summary>
public static bool IsHeadlessBuild()
{
if (ApplicationState is not null)
return ApplicationState.IsHeadlessBuild();
throw new(ApplicationStateIsNullMessage);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: de0534821603bd84b92ff71c13d6977d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Environment/ApplicationStateService.cs
uploadId: 987062
@@ -0,0 +1,49 @@
namespace CodeBoost.Environment
{
public interface IApplicationState
{
/// <summary>
/// Called when the application focus state changes.
/// </summary>
public event FocusChangeEventHandler FocusChanged;
public delegate void FocusChangeEventHandler(bool isFocused);
/// <summary>
/// True if the application is quitting.
/// </summary>
public bool IsQuitting();
/// <summary>
/// True if the application is playing.
/// </summary>
public bool IsPlaying();
/// <summary>
/// Quits the application for editor or builds.
/// </summary>
public void Quit();
/// <summary>
/// True if the application is being run within an editor, false if a build.
/// </summary>
public bool IsEditor();
/// <summary>
/// True if the application is in development mode, false if release mode.
/// </summary>
/// <remarks>This should be true if running in the editor.</remarks>
public bool IsDevelopment();
/// <summary>
/// True if a GUI build, such as a client build.
/// </summary>
public bool IsGuiBuild();
/// <summary>
/// True if a headless build, such as a server build.
/// </summary>
public bool IsHeadlessBuild();
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ee2a9a53a96b21a4bbff019dcd24608d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Environment/IApplicationState.cs
uploadId: 987062
@@ -0,0 +1,66 @@
namespace CodeBoost.Environment
{
/// <summary>
/// Provides application states for development within an IDE.
/// </summary>
public class IdeApplicationState : IApplicationState
{
/// <summary>
/// Called when the application focus state changes.
/// </summary>
/// <remarks>Event never invokes for this type.</remarks>
public event IApplicationState.FocusChangeEventHandler FocusChanged;
/// <summary>
/// Returns the value of System.Environment.HasShutdownStarted.
/// </summary>
/// <returns>System.Environment.HasShutdownStarted.</returns>
public bool IsQuitting() => System.Environment.HasShutdownStarted;
/// <summary>
/// Returns if IsQuitting is false.
/// </summary>
/// <returns>True if IsQuitting is false.</returns>
public bool IsPlaying() => !IsQuitting();
/// <summary>
/// Exits System.Environment.
/// </summary>
public void Quit()
{
System.Environment.Exit(exitCode: 0);
}
/// <summary>
/// Unconditionally returns true.
/// </summary>
/// <returns>True.</returns>
public bool IsEditor() => true;
/// <summary>
/// Returns if the DEBUG preprocessor is active.
/// </summary>
/// <returns>Active state of Preprocessor DEBUG.</returns>
public bool IsDevelopment()
{
#if DEBUG
return true;
#else
return false;
#endif
}
/// <summary>
/// Unconditionally returns false.
/// </summary>
/// <returns>False.</returns>
public bool IsGuiBuild() => false;
/// <summary>
/// Unconditionally returns false.
/// </summary>
/// <returns>False.</returns>
public bool IsHeadlessBuild() => false;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b018639a2facd9d48860a56d37188ead
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Environment/IDEApplicationState.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 656606d7f7813c346b0ea3624a4f8301
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,77 @@
using System;
using System.Buffers;
namespace CodeBoost.Extensions
{
public static class ArraySegmentExtensions
{
/// <summary>
/// If not null pools the Array using ArrayPool.Shared.
/// </summary>
public static void PoolArrayIntoShared(this ArraySegment<byte> arraySegment)
{
if (arraySegment.Array is not null)
ArrayPool<byte>.Shared.Return(arraySegment.Array);
}
/// <summary>
/// Gets a non-allocating enumerator for an ArraySegment.
/// </summary>
/// <param name="segment">Value to enumerate.</param>
/// <typeparam name="T0">Type for the ArraySegment.</typeparam>
/// <returns>An enumerator which uses offset and count of an ArraySegment.</returns>
public static ArraySegmentEnumerator<T0> GetEnumerator<T0>(this ArraySegment<T0> segment) => new(segment);
/// <summary>
/// A non-allocating enumerator for ArraySegments.
/// </summary>
/// <typeparam name="T0">Type for the ArraySegment.</typeparam>
public struct ArraySegmentEnumerator<T0>
{
/// <summary>
/// Array of the ArraySegment.
/// </summary>
private readonly T0[]? _array;
/// <summary>
/// End of the array according to the ArraySegment.Count and ArraySegment.Offset.
/// </summary>
private readonly int _end;
/// <summary>
/// Current index being enumerated.
/// </summary>
private int _index;
public ArraySegmentEnumerator(ArraySegment<T0> segment)
{
_array = segment.Array;
if (_array is null)
{
_index = -1;
_end = -1;
return;
}
_index = segment.Offset - 1;
_end = segment.Offset + segment.Count;
}
/// <summary>
/// Iterates to the next entry.
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
_index++;
return _index < _end;
}
/// <summary>
/// Gets the current entry being enumerated.
/// </summary>
public T0? Current => _array is null ? default : _array[_index];
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 71ee8f17861ef2f428e9dd3b651b1426
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Extensions/ArraySegmentExtensions.cs
uploadId: 987062
@@ -0,0 +1,144 @@
using System.Text;
using CodeBoost.Performance;
namespace CodeBoost.Extensions
{
public static partial class StringExtensions
{
/// <summary>
/// Value representing when an index is not found or specified.
/// </summary>
public const int UnsetIndex = -1;
/// <summary>
/// Converts a member string text to PascalCase
/// </summary>
/// <remarks>Leading non-alpha characters are removed and the first alpha character is capitalized.</remarks>
public static string MemberToPascalCase(this string value)
{
int index = value.GetFirstLetterOrDigitIndex();
//Index not found. String is null or has no chars/numbers.
if (index == UnsetIndex)
return value;
char firstValidChar = value[index];
//First character is not a letter, return as-is.
if (!char.IsLetter(firstValidChar))
return value;
//Already capitalized.
if (char.IsUpper(firstValidChar))
return value;
StringBuilder stringBuilder = ObjectPool<StringBuilder>.Rent();
stringBuilder.Clear();
stringBuilder.Append(char.ToUpperInvariant(firstValidChar));
stringBuilder.Append(value.Substring(index + 1));
string result = stringBuilder.ToString();
ObjectPool<StringBuilder>.Return(stringBuilder);
return result;
}
/// <summary>
/// Converts a pascal case string to member case with an optional prefix.
/// </summary>
/// <example>With a prefix of '_' value 'HelloWorld' is returned as '_helloWorld'.</example>
/// <remarks>Prefix is only added if missing.</remarks>
public static string PascalCaseToMember(this string value, string prefix = "_")
{
int index = value.GetFirstLetterOrDigitIndex();
//Index not found. String is null or has no chars/numbers.
if (index == UnsetIndex)
return value;
char firstValidChar = value[index];
int prefixLength = prefix.Length;
/* There are marginally more efficient ways to handle these prefix operations
* but allocations are going to occur either way - use what is easier to read. */
StringBuilder stringBuilder = ObjectPool<StringBuilder>.Rent();
stringBuilder.Clear();
//There is a prefix.
if (prefixLength > 0)
{
if (prefixLength >= value.Length)
{
stringBuilder.Append(prefix);
AppendLowerFirstCharAndRemainingValue();
return stringBuilder.ToString();
}
//If prefix is not yet added then do so.
if (value.Substring(0, prefixLength) != prefix)
stringBuilder.Append(prefix);
}
//Add renaming with lowercase char.
AppendLowerFirstCharAndRemainingValue();
string result = stringBuilder.ToString();
ObjectPool<StringBuilder>.Return(stringBuilder);
return result;
//Appends lowercase first char, and any renaming text in value.
void AppendLowerFirstCharAndRemainingValue()
{
stringBuilder.Append(char.ToLowerInvariant(firstValidChar));
//If value has enough length remaining append it as well.
if (value.Length >= index)
stringBuilder.Append(value.Substring(index + 1));
}
}
/// <summary>
/// Converts a string into a byte array.
/// </summary>
/// <returns>Number of bytes written to buffer.</returns>
/// <remarks>Buffer is instantiated as a new array if it is not large enough.</remarks>
public static byte[] ToBytesNonAllocated(this string value, out int bytesWritten)
{
int valueLength = value.Length;
UTF8Encoding encoding = Utf8EncodingPool.Rent();
// Number of minimum bytes the buffer must be.
int bytesNeeded = encoding.GetMaxByteCount(valueLength);
byte[] array = System.Buffers.ArrayPool<byte>.Shared.Rent(bytesNeeded);
bytesWritten = encoding.GetBytes(value, charIndex: 0, valueLength, array, byteIndex: 0);
Utf8EncodingPool.Return(encoding);
return array;
}
/// <summary>
/// Returns index of the first letter or number in a string.
/// </summary>
public static int GetFirstLetterOrDigitIndex(this string value)
{
if (string.IsNullOrWhiteSpace(value))
return UnsetIndex;
for (int i = 0; i < value.Length; i++)
{
if (char.IsLetterOrDigit(value[i]))
return i;
}
return UnsetIndex;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f8c536d170d14704294bd05282965b4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Extensions/StringExtensions.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8ba665e9db40d08479de3a1b6934e14e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
using System;
namespace CodeBoost.Logging
{
/// <summary>
/// A logger which uses Console writing for messages.
/// </summary>
public class ConsoleLogger : ILogger
{
/// <summary>
/// Returns the settings to use for this logger.
/// </summary>
/// <returns>Settings to use for logger.</returns>
public LoggerSetting GetLoggerSetting() => LoggerSetting.LoggerServiceSetting;
/// <summary>
/// Disables always including stacktrace in development environments.
/// </summary>
/// <returns>True to disable always including stacktrace in development environments.</returns>
/// <remarks>Even with unconditional inclusions disabled stacktrace will still be included for higher level log calls.</remarks>
public bool DisableUnconditionalDevelopmentStacktrace() => true;
/// <summary>
/// Logs a message as information.
/// </summary>
public void LogInformation(string message) => Console.WriteLine($"Information :: {Logger.AddStackTraceIfDevelopment(message)}");
/// <summary>
/// Logs a message as warning.
/// </summary>
public void LogWarning(string message) => Console.WriteLine($"Warning :: {Logger.AddStackTraceIfDevelopment(message)}");
/// <summary>
/// Logs a message as error.
/// </summary>
public void LogError(string message) => Console.WriteLine($"Error :: {Logger.AddStackTrace(message)}");
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 056be24ffa1f7614ba406bdae2731f2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/ConsoleLogger.cs
uploadId: 987062
@@ -0,0 +1,34 @@
namespace CodeBoost.Logging
{
public interface ILogger
{
/// <summary>
/// Returns the settings to use for this logger.
/// </summary>
/// <returns>Settings to use for logger.</returns>
public LoggerSetting GetLoggerSetting();
/// <summary>
/// Disables always including stacktrace in development environments.
/// </summary>
/// <returns>True to disable always including stacktrace in development environments.</returns>
/// <remarks>Even with unconditional inclusions disabled stacktrace will still be included for higher level log calls.</remarks>
public bool DisableUnconditionalDevelopmentStacktrace();
/// <summary>
/// Logs a message as information.
/// </summary>
public void LogInformation(string message);
/// <summary>
/// Logs a message as warning.
/// </summary>
public void LogWarning(string message);
/// <summary>
/// Logs a message as error.
/// </summary>
public void LogError(string message);
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 07c83916caa16644b952fabf0e906fad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/ILogger.cs
uploadId: 987062
@@ -0,0 +1,94 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using CodeBoost.Environment;
using CodeBoost.Extensions;
using System;
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#pragma warning disable CS0067 // Event is never used
namespace CodeBoost.Logging
{
/// <summary>
/// A static Logger which uses the currently registered ILogger.
/// </summary>
public static class Logger<TOuter, TInner0>
{
public static void LogInformation(string message, [CallerMemberName] string methodName = "") => LoggingService.LogInformation($"{Logger.GetLogMessagePrefix(typeof(TOuter), typeof(TInner0), methodName)}{message}");
public static void LogWarning(string message, [CallerMemberName] string methodName = "") => LoggingService.LogWarning($"{Logger.GetLogMessagePrefix(typeof(TOuter), typeof(TInner0), methodName)}{message}");
public static void LogError(string message, [CallerMemberName] string methodName = "") => LoggingService.LogError($"{Logger.GetLogMessagePrefix(typeof(TOuter), typeof(TInner0), methodName)}{message}");
}
/// <summary>
/// A static Logger which uses the currently registered ILogger.
/// </summary>
public static class Logger<T0>
{
public static void LogInformation(string message, [CallerMemberName] string methodName = "") => LoggingService.LogInformation($"{Logger.GetLogMessagePrefix(typeof(T0), methodName)}{message}");
public static void LogWarning(string message, [CallerMemberName] string methodName = "") => LoggingService.LogWarning($"{Logger.GetLogMessagePrefix(typeof(T0), methodName)}{message}");
public static void LogError(string message, [CallerMemberName] string methodName = "") => LoggingService.LogError($"{Logger.GetLogMessagePrefix(typeof(T0), methodName)}{message}");
}
/// <summary>
/// A static Logger which uses the currently registered ILogger.
/// </summary>
public static class Logger
{
/// <summary>
/// Disables always including stacktrace in development environments.
/// </summary>
/// <returns>True to disable always including stacktrace in development environments.</returns>
/// <remarks>Even with unconditional inclusions disabled stacktrace will still be included for higher level log calls.</remarks>
public static bool DisableUnconditionalDevelopmentStacktrace() => LoggingService.DisableUnconditionalDevelopmentStacktrace();
public static void LogInformation(string message, [CallerMemberName] string methodName = "") => LoggingService.LogInformation($"{Logger.GetLogMessagePrefix(methodName)}{message}");
public static void LogWarning(string message, [CallerMemberName] string methodName = "") => LoggingService.LogWarning($"{Logger.GetLogMessagePrefix(methodName)}{message}");
public static void LogError(string message, [CallerMemberName] string methodName = "") => LoggingService.LogError($"{Logger.GetLogMessagePrefix(methodName)}{message}");
public static void LogInformation(Type type, string message, [CallerMemberName] string methodName = "") => Logger.LogInformation($"{Logger.GetLogMessagePrefix(type, methodName)}{message}");
public static void LogWarning(Type type, string message, [CallerMemberName] string methodName = "") => Logger.LogWarning($"{Logger.GetLogMessagePrefix(type, methodName)}{message}");
public static void LogError(Type type, string message, [CallerMemberName] string methodName = "") => Logger.LogError($"{Logger.GetLogMessagePrefix(type, methodName)}{message}");
/// <summary>
/// Returns the prefix to use for a method under an invoking type.
/// </summary>
/// <param name="outerType">Type which contains the method logging the message.</param>
/// <param name="methodName">Name of the method logging the message.</param>
/// <returns></returns>
public static string GetLogMessagePrefix(Type outerType, Type innerType, string methodName) => $"[{outerType.Name}<{innerType.Name}>::{methodName}]: ";
/// <summary>
/// Returns the prefix to use for a method under an invoking type.
/// </summary>
/// <param name="outerType">Type which contains the method logging the message.</param>
/// <param name="methodName">Name of the method logging the message.</param>
/// <returns></returns>
public static string GetLogMessagePrefix(Type outerType, string methodName) => $"[{outerType.Name}::{methodName}]: ";
/// <summary>
/// Returns the prefix to use for a method under an invoking type.
/// </summary>
/// <param name="methodName">Name of the method logging the message.</param>
/// <returns></returns>
public static string GetLogMessagePrefix(string methodName) => $"[{methodName}]: ";
/// <summary>
/// Adds a StackTrace onto message if application is a development build.
/// </summary>
public static string AddStackTraceIfDevelopment(string message)
{
if (!ApplicationState.IsDevelopmentBuild())
return message;
if (DisableUnconditionalDevelopmentStacktrace())
return message;
return AddStackTrace(message);
}
/// <summary>
/// Adds a StackTrace to a message.
/// </summary>
public static string AddStackTrace(string message) => $"{message}: {new StackTrace(fNeedFileInfo: true)}";
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 50be6db4d26a87146865f754f858741d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/Logger.cs
uploadId: 987062
@@ -0,0 +1,26 @@
namespace CodeBoost.Logging
{
/// <summary>
/// What type of messages to log.
/// </summary>
public enum LoggerLevel : byte
{
/// <summary>
/// Log error and higher.
/// </summary>
Error = 1,
/// <summary>
/// Log warning and higher.
/// </summary>
Warning = 2,
/// <summary>
/// Log information and higher.
/// </summary>
Information = 3,
/// <summary>
/// All logging is disabled.
/// </summary>
Disabled = 4
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e732bcc2c514d8c49a67e86344b8ff7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/LoggerLevel.cs
uploadId: 987062
@@ -0,0 +1,38 @@
namespace CodeBoost.Logging
{
/// <summary>
/// What type of messages to log.
/// </summary>
public class LoggerSetting
{
/// <summary>
/// Logging level for editor.
/// </summary>
// Do not make readonly so that the field may be serialized.
// ReSharper disable once FieldCanBeMadeReadOnly.Global
public LoggerLevel Editor = LoggerLevel.Information;
/// <summary>
/// Logging level for development builds.
/// </summary>
// Do not make readonly so that the field may be serialized.
// ReSharper disable once FieldCanBeMadeReadOnly.Global
public LoggerLevel DevelopmentBuilds = LoggerLevel.Warning;
/// <summary>
/// Logging level for release builds.
/// </summary>
// Do not make readonly so that the field may be serialized.
// ReSharper disable once FieldCanBeMadeReadOnly.Global
public LoggerLevel ReleaseBuilds = LoggerLevel.Error;
/// <summary>
/// Default settings to use.
/// </summary>
public static readonly LoggerSetting LoggerServiceSetting = new()
{
DevelopmentBuilds = LoggerLevel.Information,
Editor = LoggerLevel.Information,
ReleaseBuilds = LoggerLevel.Error,
};
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d4a853f153f2b5144b26a8673ea87533
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/LoggerSetting.cs
uploadId: 987062
@@ -0,0 +1,150 @@
using System.Diagnostics;
using CodeBoost.Environment;
using CodeBoost.Extensions;
using System;
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#pragma warning disable CS0067 // Event is never used
namespace CodeBoost.Logging
{
public static class LoggingService
{
/// <summary>
/// Called when Logger is set.
/// </summary>
public static event LoggerSetEventHandler? LoggerSet;
public delegate void LoggerSetEventHandler(ILogger logger);
/// <summary>
/// ILogger to use.
/// </summary>
public static ILogger? Logger;
/// <summary>
/// Cached value of logging level for performance.
/// </summary>
private static byte _loggerLevelAsUnderlyingType;
/// <summary>
/// Message when trying to access Logger when there is not an instance created.
/// </summary>
private static readonly string LoggerIsNullMessage = $"[{nameof(LoggingService)}] is null. Use [{nameof(LoggingService)}] to set a service.";
static LoggingService()
{
UseLogger(new ConsoleLogger());
}
/// <summary>
/// Specifies which ILogger to use.
/// </summary>
public static void UseLogger(ILogger logger)
{
Logger = logger;
CacheLoggerLevelAsUnderlyingType();
LoggerSet?.Invoke(logger);
}
public static bool DisableUnconditionalDevelopmentStacktrace()
{
if (Logger is not null)
return Logger.DisableUnconditionalDevelopmentStacktrace();
throw new(LoggerIsNullMessage);
}
/// <summary>
/// Logs a message as information.
/// </summary>
public static void LogInformation(string message)
{
if (Logger is not null)
{
if (_loggerLevelAsUnderlyingType < (byte)LoggerLevel.Information)
return;
Logger.LogInformation(message);
}
else
{
throw new(LoggerIsNullMessage);
}
}
/// <summary>
/// Logs a message as warning.
/// </summary>
public static void LogWarning(string message)
{
if (Logger is not null)
{
if (_loggerLevelAsUnderlyingType < (byte)LoggerLevel.Warning)
return;
Logger.LogWarning(message);
}
else
{
throw new(LoggerIsNullMessage);
}
}
/// <summary>
/// Logs a message as error.
/// </summary>
public static void LogError(string message)
{
if (Logger is not null)
{
if (_loggerLevelAsUnderlyingType < (byte)LoggerLevel.Error)
return;
Logger.LogError(message);
}
else
{
throw new(LoggerIsNullMessage);
}
}
/// <summary>
/// Gets logger level for the current environment.
/// </summary>
/// <returns></returns>
private static LoggerLevel GetLoggerLevel()
{
IApplicationState? applicationState = ApplicationStateService.ApplicationState;
ILogger? logger = Logger;
// Missing dependencies.
if (logger is null || applicationState is null)
return LoggerLevel.Disabled;
LoggerSetting loggingSetting = logger.GetLoggerSetting();
bool isEditor = applicationState.IsEditor();
if (isEditor)
return loggingSetting.Editor;
bool isDevelopment = applicationState.IsDevelopment();
if (isDevelopment)
return loggingSetting.DevelopmentBuilds;
// If here then is release builds.
return loggingSetting.ReleaseBuilds;
}
/// <summary>
/// Gets logger level as the underlying type.
/// </summary>
/// <returns></returns>
private static byte GetLoggerLevelAsUnderlyingType() => (byte)GetLoggerLevel();
/// <summary>
/// Caches the logger level to use.
/// </summary>
private static void CacheLoggerLevelAsUnderlyingType() => _loggerLevelAsUnderlyingType = GetLoggerLevelAsUnderlyingType();
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a7d80dda9d45eb7428b487b1f05a836f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Logging/LoggingService.cs
uploadId: 987062
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6023f3c3e07698e4f9010137ab86baeb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
using CodeBoost.Types;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for BoostedQueue collections.
/// </summary>
public static class BoostedQueuePool<T0>
{
/// <summary>
/// Stack for TheadLocal BoostedQueue.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<BoostedQueue<T0>>> Wrapper;
/// <summary>
/// Stack for global BoostedQueue.
/// </summary>
private static readonly Stack<BoostedQueue<T0>> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static BoostedQueuePool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)))
// {
// Logger.LogError(typeof(BoostedQueuePool<>), $"[{typeof(T0).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
//
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a BoostedQueue.
/// </summary>
/// <returns>A cleared BoostedQueue collection.</returns>
public static BoostedQueue<T0> Rent()
{
BoostedQueue<T0> result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a BoostedQueue and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref BoostedQueue<T0>? value)
{
Return(value);
value = null;
}
/// <summary>
/// Returns a BoostedQueue.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(BoostedQueue<T0>? value)
{
if (value is null)
return;
value.Clear();
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal BoostedQueue Stack into the global Stack.
/// </summary>
private static void Flush(Stack<BoostedQueue<T0>> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out BoostedQueue<T0>? item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b1d619324886bea4d9d24bb7e0690dc0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/BoostedQueuePool.cs
uploadId: 987062
@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for Dictionary collections.
/// </summary>
public static class DictionaryPool<T0, T1>
{
/// <summary>
/// Stack for TheadLocal Dictionary.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<Dictionary<T0, T1>>> Wrapper;
/// <summary>
/// Stack for global Dictionary.
/// </summary>
private static readonly Stack<Dictionary<T0, T1>> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static DictionaryPool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)) || typeof(IPoolResettable).IsAssignableFrom(typeof(T1)))
// {
// Logger.LogError(typeof(DictionaryPool<,>), $"[{typeof(T0).Name}] or [{typeof(T1).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a Dictionary.
/// </summary>
/// <returns>A cleared Dictionary collection.</returns>
public static Dictionary<T0, T1> Rent()
{
Dictionary<T0, T1> result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a Dictionary and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref Dictionary<T0, T1> value)
{
Return(value);
value = null;
}
/// <summary>
/// Returns a Dictionary.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(Dictionary<T0, T1> value)
{
if (value is null)
return;
value.Clear();
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal Dictionary Stack into the global Stack.
/// </summary>
private static void Flush(Stack<Dictionary<T0, T1>> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out Dictionary<T0, T1> item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 75f32e842c87e42488052042a2ae7de1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/DictionaryPool.cs
uploadId: 987062
@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for HashSet collections.
/// </summary>
public static class HashSetPool<T0>
{
/// <summary>
/// Stack for TheadLocal HashSet.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<HashSet<T0>>> Wrapper;
/// <summary>
/// Stack for global HashSet.
/// </summary>
private static readonly Stack<HashSet<T0>> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static HashSetPool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)))
// {
// Logger.LogError(typeof(HashSetPool<>), $"[{typeof(T0).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a HashSet.
/// </summary>
/// <returns>A cleared HashSet collection.</returns>
public static HashSet<T0> Rent()
{
HashSet<T0> result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a HashSet and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref HashSet<T0>? value)
{
Return(value);
value = null;
}
/// <summary>
/// Returns a HashSet.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(HashSet<T0>? value)
{
if (value is null)
return;
value.Clear();
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal HashSet Stack into the global Stack.
/// </summary>
private static void Flush(Stack<HashSet<T0>> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out HashSet<T0>? item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 283af8e67a94d5d45a0d7ee1cb9bc5ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/HashSetPool.cs
uploadId: 987062
@@ -0,0 +1,23 @@
using CodeBoost.CodeAnalysis;
namespace CodeBoost.Performance
{
/// <summary>
/// Implement to reset values when returning to a pool, as well to initialize when renting from a pool.
/// </summary>
public interface IPoolResettable
{
/// <summary>
/// Resets values when being placed into a pool.
/// </summary>
[CreateSignature]
void OnReturn();
/// <summary>
/// Initializes values when being retrieved from a pool.
/// </summary>
[CreateSignature]
void OnRent();
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1edbf5d5a1040d3418b21320638e5408
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/IPoolResettable.cs
uploadId: 987062
@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for List collections.
/// </summary>
public static class ListPool<T0>
{
/// <summary>
/// Stack for TheadLocal List.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<List<T0>>> Wrapper;
/// <summary>
/// Stack for global List.
/// </summary>
private static readonly Stack<List<T0>> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static ListPool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)))
// {
// Logger.LogError(typeof(List<>), $"[{typeof(T0).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a List.
/// </summary>
/// <returns>A cleared List collection.</returns>
public static List<T0> Rent()
{
List<T0> result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a List and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref List<T0>? value)
{
Return(value);
value = null;
}
/// <summary>
/// Returns a List.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(List<T0>? value)
{
if (value is null)
return;
value.Clear();
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal List Stack into the global Stack.
/// </summary>
private static void Flush(Stack<List<T0>> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out List<T0>? item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 119045979a98ace4d87e5918e9ee1a85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/ListPool.cs
uploadId: 987062
@@ -0,0 +1,43 @@
using System;
using System.Runtime.InteropServices;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// Returns the memory size of a managed type.
/// </summary>
public static class ManagedSizeOf<T0> where T0 : struct
{
/// <summary>
/// Cached size value.
/// </summary>
// ReSharper disable once StaticMemberInGenericType
public static int Value { get; private set; }
static ManagedSizeOf()
{
Type type = typeof(T0);
//Enums must be handled as the underlying numeric type.
if (type.IsEnum)
type = Enum.GetUnderlyingType(type);
try
{
Value = Marshal.SizeOf(type);
}
catch (Exception ex)
{
/* An exception can occur if the type has a property
* which can take up memory dynamically, such as
* a String. */
Value = 0;
Logger.LogError(typeof(ManagedSizeOf<T0>), $"Type [{typeof(T0).FullName}] caused an exception: [{ex.Message}].");
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9fc51ebf633fcb34fa79ac6d712c0b24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/ManagedSizeOf.cs
uploadId: 987062
@@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for generic objects.
/// </summary>
public static class ObjectPool<T0> where T0 : new()
{
/// <summary>
/// Stack for TheadLocal Object.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<T0>> Wrapper;
/// <summary>
/// Stack for global Object.
/// </summary>
private static readonly Stack<T0> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static ObjectPool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)))
// {
// Logger.LogError(typeof(ObjectPool<>), $"[{typeof(T0).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a generic object.
/// </summary>
/// <returns>A new or pooled instance of T0.</returns>
public static T0 Rent()
{
T0 result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a generic object and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref T0? value)
{
Return(value);
value = default;
}
/// <summary>
/// Returns a generic object.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(T0? value)
{
if (value is null)
return;
// Note: If T0 implements an interface like IResettable,
// you would call value.Clear() or value.Reset() here.
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal Object Stack into the global Stack.
/// </summary>
private static void Flush(Stack<T0> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out T0? item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a694be2649aa2734fafb43663546891b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/ObjectPool.cs
uploadId: 987062
@@ -0,0 +1,19 @@
namespace CodeBoost.Performance
{
/// <summary>
/// How to handle values when they may need to be pooled.
/// </summary>
/// <remarks>This can be used to allow pooling of collection values while optionally pooling the collection itself.</remarks>
public enum PoolReturnType
{
/// <summary>
/// Do nothing with the object.
/// </summary>
None = 0,
/// <summary>
/// Return the object.
/// </summary>
Return = 1,
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7831dfe6bc5fb424e95512ae561610df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/PoolReturnType.cs
uploadId: 987062
@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Threading;
using CodeBoost.Extensions;
using CodeBoost.Logging;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for Queue collections.
/// </summary>
public static class QueuePool<T0>
{
/// <summary>
/// Stack for TheadLocal Queue.
/// </summary>
private static readonly ThreadLocal<ThreadLocalStackWrapper<Queue<T0>>> Wrapper;
/// <summary>
/// Stack for global Queue.
/// </summary>
private static readonly Stack<Queue<T0>> GlobalStack = new();
/// <summary>
/// Maximum number of entries allowed in the global stack.
/// </summary>
private const int MaximumGlobalStackSize = 200;
/// <summary>
/// Maximum number of entries allowed in the ThreadLocal stack.
/// </summary>
private const int MaximumThreadLocalStackSize = 100;
static QueuePool()
{
// if (typeof(IPoolResettable).IsAssignableFrom(typeof(T0)))
// {
// Logger.LogError(typeof(QueuePool<>), $"[{typeof(T0).Name}] implements IPoolResettable; use the Resettable pool instead.");
// return;
// }
Wrapper = new(valueFactory: () => new(Flush), trackAllValues: false);
}
/// <summary>
/// Rents a Queue.
/// </summary>
/// <returns>A cleared Queue collection.</returns>
public static Queue<T0> Rent()
{
Queue<T0> result;
if (Wrapper.Value.LocalStack.TryPop(out result))
return result;
lock (GlobalStack)
{
if (GlobalStack.TryPop(out result))
return result;
}
return new();
}
/// <summary>
/// Returns a Queue and sets the provided reference to null;
/// This Method will not execute if the value is null.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void ReturnAndNullifyReference(ref Queue<T0>? value)
{
Return(value);
value = null;
}
/// <summary>
/// Returns a Queue.
/// </summary>
/// <param name = "value"> Value to return. </param>
public static void Return(Queue<T0>? value)
{
if (value is null)
return;
value.Clear();
if (Wrapper.Value.LocalStack.Count < MaximumThreadLocalStackSize)
{
Wrapper.Value.LocalStack.Push(value);
return;
}
lock (GlobalStack)
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(value);
}
//If here both stacks are at capacity.
}
/// <summary>
/// Flushes the ThreadLocal Queue Stack into the global Stack.
/// </summary>
private static void Flush(Stack<Queue<T0>> localStack)
{
if (localStack.Count == 0)
return;
lock (GlobalStack)
{
while (localStack.TryPop(out Queue<T0>? item))
{
if (GlobalStack.Count < MaximumGlobalStackSize)
GlobalStack.Push(item);
else
break;
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2aa8e5eca0632fc4faf351b3327a9b01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/QueuePool.cs
uploadId: 987062
@@ -0,0 +1,41 @@
using CodeBoost.Types;
namespace CodeBoost.Performance
{
/// <summary>
/// A pool for a BoostedQueue which is resettable.
/// </summary>
public static class ResettableBoostedQueuePool<T0> where T0 : IPoolResettable, new()
{
/// <summary>
/// Retrieves an instance of BoostedQueue.
/// </summary>
public static BoostedQueue<T0> Rent() => BoostedQueuePool<T0>.Rent();
/// <summary>
/// Stores an instance of BoostedQueue and sets the original reference to null.
/// </summary>
public static void ReturnAndNullifyReference(ref BoostedQueue<T0>? value, PoolReturnType collectionReturnType)
{
Return(value, collectionReturnType);
value = null;
}
/// <summary>
/// Stores an instance of BoostedQueue.
/// </summary>
public static void Return(BoostedQueue<T0>? value, PoolReturnType collectionReturnType)
{
if (value is null)
return;
while (value.TryDequeue(out T0? item))
item?.OnReturn();
if (collectionReturnType is PoolReturnType.Return)
BoostedQueuePool<T0>.Return(value);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: eac49a6bd3f8d0c42afc28d03e3e3963
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.7.3R
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Synapse/SynapseSocket/CodeBoost/Performance/ResettableBoostedQueuePool.cs
uploadId: 987062

Some files were not shown because too many files have changed in this diff Show More