Update FishNet

This commit is contained in:
2026-04-07 03:11:52 +07:00
parent 9675b7b31d
commit ba7513d478
869 changed files with 3675 additions and 2764 deletions
@@ -218,10 +218,6 @@ namespace FishNet.Managing.Server
/// </summary>
private float _nextTimeoutCheckTime;
/// <summary>
/// Used to read splits.
/// </summary>
private SplitReader _splitReader = new();
/// <summary>
/// </summary>
private NetworkTrafficStatistics _networkTrafficStatistics;
#if DEVELOPMENT
@@ -611,46 +607,44 @@ namespace FishNet.Managing.Server
{
//Sanity check to make sure transports are following proper types/ranges.
int id = args.ConnectionId;
if (id < 0 || id > NetworkConnection.MAXIMUM_CLIENTID_VALUE)
if (id < 0)
{
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {NetworkConnection.MAXIMUM_CLIENTID_VALUE}. The client has been disconnected.");
return;
}
//Valid Id.
else
//If started then add to authenticated clients.
if (args.ConnectionState == RemoteConnectionState.Started)
{
//If started then add to authenticated clients.
if (args.ConnectionState == RemoteConnectionState.Started)
NetworkManager.Log($"Remote connection started for Id {id}.");
NetworkConnection conn = new(NetworkManager, id, args.TransportIndex, true);
Clients.Add(args.ConnectionId, conn);
_clientsList.Add(conn);
OnRemoteConnectionState?.Invoke(conn, args);
//Do nothing else until the client sends it's version.
}
//If stopping.
else if (args.ConnectionState == RemoteConnectionState.Stopped)
{
/* If client's connection is found then clean
* them up from server. */
if (Clients.TryGetValueIL2CPP(id, out NetworkConnection conn))
{
NetworkManager.Log($"Remote connection started for Id {id}.");
NetworkConnection conn = new(NetworkManager, id, args.TransportIndex, true);
Clients.Add(args.ConnectionId, conn);
_clientsList.Add(conn);
conn.SetDisconnecting(true);
OnRemoteConnectionState?.Invoke(conn, args);
Clients.Remove(id);
_clientsList.Remove(conn);
Objects.ClientDisconnected(conn);
BroadcastClientConnectionChange(false, conn);
//Return predictedObjectIds.
Queue<int> pqId = conn.PredictedObjectIds;
while (pqId.Count > 0)
Objects.CacheObjectId(pqId.Dequeue());
//Do nothing else until the client sends it's version.
}
//If stopping.
else if (args.ConnectionState == RemoteConnectionState.Stopped)
{
/* If client's connection is found then clean
* them up from server. */
if (Clients.TryGetValueIL2CPP(id, out NetworkConnection conn))
{
conn.SetDisconnecting(true);
OnRemoteConnectionState?.Invoke(conn, args);
Clients.Remove(id);
_clientsList.Remove(conn);
Objects.ClientDisconnected(conn);
BroadcastClientConnectionChange(false, conn);
//Return predictedObjectIds.
Queue<int> pqId = conn.PredictedObjectIds;
while (pqId.Count > 0)
Objects.CacheObjectId(pqId.Dequeue());
conn.ResetState();
NetworkManager.Log($"Remote connection stopped for Id {id}.");
}
conn.ResetState();
NetworkManager.Log($"Remote connection stopped for Id {id}.");
}
}
}
@@ -717,6 +711,14 @@ namespace FishNet.Managing.Server
//Not from a valid connection. Should not be possible.
if (args.ConnectionId < 0)
return;
/* Connection isn't available. This should never happen.
* Force an immediate disconnect. */
if (!Clients.TryGetValueIL2CPP(args.ConnectionId, out NetworkConnection connection))
{
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"ConnectionId {args.ConnectionId} not found within Clients. Connection will be kicked immediately.");
return;
}
ArraySegment<byte> segment;
if (NetworkManager.TransportManager.HasIntermediateLayer)
@@ -735,7 +737,7 @@ namespace FishNet.Managing.Server
//If over MTU kick client immediately.
if (segment.Count > channelMtu)
{
ExceededMTUKick();
ExceededMTUKick(segment.Count, channelMtu);
return;
}
@@ -761,30 +763,35 @@ namespace FishNet.Managing.Server
#endif
//Skip packetId.
reader.ReadPacketId();
int expectedMessages;
_splitReader.GetHeader(reader, out expectedMessages);
//If here split message is to be read into splitReader.
_splitReader.Write(tick, reader, expectedMessages);
/* If fullMessage returns 0 count then the split
* has not written fully yet. Otherwise, if there is
* data within then reinitialize reader with the
* full message. */
ArraySegment<byte> fullMessage = _splitReader.GetFullMessage();
if (fullMessage.Count == 0)
int expectedMessages = reader.ReadInt32();
if (!connection.TryGetSplitReader(expectedMessages, out SplitReader splitReader))
{
//Intentionally a normal log rather than error to prevent spam from client attacks.
NetworkManager.Log($"Something went wrong when trying to get the [{nameof(splitReader)}] for connection [{connection.ToString()}].");
connection.Kick(KickReason.UnusualActivity);
return;
/* If here then all data has been received.
* It's possible the client could have exceeded
* maximum MTU but not the maximum number of splits.
* This is because the length of each split
* is not written, so we don't know how much data of the
* final message actually belonged to the split vs
* unrelated data added afterwards. We're going to cut
* the client some slack in this situation for the sake
* of keeping things simple. */
}
if (!splitReader.Write(reader))
{
//Intentionally a normal log rather than error to prevent spam from client attacks.
NetworkManager.Log($"Something went wrong when trying to write a split message for connection [{connection.ToString()}].");
connection.Kick(KickReason.UnusualActivity);
return;
}
//This will return true if the full message has been written.
if (!splitReader.TryGetFullMessage(out ArraySegment<byte> fullMessage))
return;
reader.Initialize(fullMessage, NetworkManager, dataSource);
//Once here the split reader can be returned.
connection.StoreSplitReader();
}
//Parse reader.
@@ -794,64 +801,55 @@ namespace FishNet.Managing.Server
#if DEVELOPMENT
NetworkManager.PacketIdHistory.ReceivedPacket(packetId, packetFromServer: false);
#endif
NetworkConnection conn;
/* Connection isn't available. This should never happen.
* Force an immediate disconnect. */
if (!Clients.TryGetValueIL2CPP(args.ConnectionId, out conn))
{
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"ConnectionId {args.ConnectionId} not found within Clients. Connection will be kicked immediately.");
return;
}
conn.LocalTick.Update(timeManager, tick, EstimatedTick.OldTickOption.Discard);
conn.PacketTick.Update(timeManager, tick, EstimatedTick.OldTickOption.SetLastRemoteTick);
connection.LocalTick.Update(timeManager, tick, EstimatedTick.OldTickOption.Discard);
connection.PacketTick.Update(timeManager, tick, EstimatedTick.OldTickOption.SetLastRemoteTick);
/* If connection isn't authenticated and isn't a broadcast
* then disconnect client. If a broadcast then process
* normally; client may still become disconnected if the broadcast
* does not allow to be called while not authenticated. */
if (!conn.IsAuthenticated && packetId != PacketId.Version && packetId != PacketId.Broadcast)
if (!connection.IsAuthenticated && packetId != PacketId.Version && packetId != PacketId.Broadcast)
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent packetId {packetId} without being authenticated. Connection will be kicked immediately.");
connection.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {connection.ClientId} sent packetId {packetId} without being authenticated. Connection will be kicked immediately.");
return;
}
if (packetId == PacketId.Replicate)
{
Objects.ParseReplicateRpc(reader, conn, args.Channel);
Objects.ParseReplicateRpc(reader, connection, args.Channel);
}
else if (packetId == PacketId.ServerRpc)
{
Objects.ParseServerRpc(reader, conn, args.Channel);
Objects.ParseServerRpc(reader, connection, args.Channel);
}
else if (packetId == PacketId.ObjectSpawn)
{
if (!GetAllowPredictedSpawning())
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
connection.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {connection.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
return;
}
Objects.ReadSpawn(reader, conn);
Objects.ReadSpawn(reader, connection);
}
else if (packetId == PacketId.ObjectDespawn)
{
if (!GetAllowPredictedSpawning())
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
connection.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {connection.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
return;
}
Objects.ReadDespawn(reader, conn);
Objects.ReadDespawn(reader, connection);
}
else if (packetId == PacketId.Broadcast)
{
ParseBroadcast(reader, conn, args.Channel);
ParseBroadcast(reader, connection, args.Channel);
}
else if (packetId == PacketId.PingPong)
{
ParsePingPong(reader, conn);
ParsePingPong(reader, connection);
}
else if (packetId == PacketId.Version)
{
ParseVersion(reader, conn, args.TransportIndex);
ParseVersion(reader, connection, args.TransportIndex);
}
else
{
@@ -880,9 +878,9 @@ namespace FishNet.Managing.Server
#endif
//Kicks connection for exceeding MTU.
void ExceededMTUKick()
void ExceededMTUKick(int sentCount, int maximumCount)
{
Kick(args.ConnectionId, KickReason.ExploitExcessiveData, LoggingType.Common, $"ConnectionId {args.ConnectionId} sent a message larger than allowed amount. Connection will be kicked immediately.");
Kick(args.ConnectionId, KickReason.ExploitExcessiveData, LoggingType.Common, $"ConnectionId {args.ConnectionId} sent a message of {sentCount} bytes while the maximum allowed amount is {maximumCount}. Connection will be kicked immediately.");
}
}