mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
explicit message types
This commit is contained in:
parent
72132b89a8
commit
d8e33f933f
@ -103,8 +103,25 @@ public struct EntityStateMessage : NetworkMessage
|
||||
public ArraySegment<byte> payload;
|
||||
}
|
||||
|
||||
// state update for unreliable sync.
|
||||
// baseline is always sent over Reliable channel.
|
||||
public struct EntityStateMessageUnreliableBaseline : NetworkMessage
|
||||
{
|
||||
// baseline messages send their tick number as byte.
|
||||
// delta messages are checked against that tick to avoid applying a
|
||||
// delta on top of the wrong baseline.
|
||||
// (byte is enough, we just need something small to compare against)
|
||||
public byte baselineTick;
|
||||
|
||||
public uint netId;
|
||||
// the serialized component data
|
||||
// -> ArraySegment to avoid unnecessary allocations
|
||||
public ArraySegment<byte> payload;
|
||||
}
|
||||
|
||||
// state update for unreliable sync
|
||||
public struct EntityStateMessageUnreliable : NetworkMessage
|
||||
// delta is always sent over Unreliable channel.
|
||||
public struct EntityStateMessageUnreliableDelta : NetworkMessage
|
||||
{
|
||||
// baseline messages send their tick number as byte.
|
||||
// delta messages are checked against that tick to avoid applying a
|
||||
|
@ -516,7 +516,8 @@ internal static void RegisterMessageHandlers(bool hostMode)
|
||||
RegisterHandler<ObjectSpawnFinishedMessage>(_ => { });
|
||||
// host mode doesn't need state updates
|
||||
RegisterHandler<EntityStateMessage>(_ => { });
|
||||
RegisterHandler<EntityStateMessageUnreliable>(_ => { });
|
||||
RegisterHandler<EntityStateMessageUnreliableBaseline>(_ => { });
|
||||
RegisterHandler<EntityStateMessageUnreliableDelta>(_ => { });
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -528,7 +529,8 @@ internal static void RegisterMessageHandlers(bool hostMode)
|
||||
RegisterHandler<ObjectSpawnStartedMessage>(OnObjectSpawnStarted);
|
||||
RegisterHandler<ObjectSpawnFinishedMessage>(OnObjectSpawnFinished);
|
||||
RegisterHandler<EntityStateMessage>(OnEntityStateMessage);
|
||||
RegisterHandler<EntityStateMessageUnreliable>(OnEntityStateMessageUnreliable);
|
||||
RegisterHandler<EntityStateMessageUnreliableBaseline>(OnEntityStateMessageUnreliableBaseline);
|
||||
RegisterHandler<EntityStateMessageUnreliableDelta>(OnEntityStateMessageUnreliableDelta);
|
||||
}
|
||||
|
||||
// These handlers are the same for host and remote clients
|
||||
@ -1447,63 +1449,84 @@ static void OnEntityStateMessage(EntityStateMessage message)
|
||||
else Debug.LogWarning($"Did not find target for sync message for {message.netId}. Were all prefabs added to the NetworkManager's spawnable list?\nNote: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message.");
|
||||
}
|
||||
|
||||
static void OnEntityStateMessageUnreliable(EntityStateMessageUnreliable message, int channelId)
|
||||
static void OnEntityStateMessageUnreliableBaseline(EntityStateMessageUnreliableBaseline message, int channelId)
|
||||
{
|
||||
// safety check: baseline should always arrive over Reliable channel.
|
||||
if (channelId != Channels.Reliable)
|
||||
{
|
||||
Debug.LogError($"Client OnEntityStateMessageUnreliableBaseline arrived on channel {channelId} instead of Reliable. This should never happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug.Log($"NetworkClient.OnUpdateVarsMessage {msg.netId}");
|
||||
if (spawned.TryGetValue(message.netId, out NetworkIdentity identity) && identity != null)
|
||||
{
|
||||
// unreliable delta?
|
||||
if (channelId == Channels.Unreliable)
|
||||
{
|
||||
// unreliable state sync messages may arrive out of order.
|
||||
// only ever apply state that's newer than the last received state.
|
||||
// note that we send one EntityStateMessage per Entity,
|
||||
// so there will be multiple with the same == timestamp.
|
||||
//
|
||||
// note that a reliable baseline may arrive before/after a delta.
|
||||
// that is fine.
|
||||
if (connection.remoteTimeStamp < identity.lastUnreliableStateTime)
|
||||
{
|
||||
// debug log to show that it's working.
|
||||
// can be tested via LatencySimulation scramble easily.
|
||||
Debug.Log($"Client caught out of order Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
// UDP messages may accidentally arrive twice.
|
||||
// or even intentionally, if unreliableRedundancy is turned on.
|
||||
else if (connection.remoteTimeStamp == identity.lastUnreliableStateTime)
|
||||
{
|
||||
// only log this if unreliableRedundancy is disabled.
|
||||
// otherwise it's expected and will happen a lot.
|
||||
if (!unreliableRedundancy) Debug.Log($"Client caught duplicate Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure this delta is for the correct baseline.
|
||||
// we don't want to apply an old delta on top of a new baseline.
|
||||
if (message.baselineTick != identity.lastUnreliableBaselineReceived)
|
||||
{
|
||||
Debug.Log($"Client caught Unreliable state message for old baseline for {identity} with baselineTick={identity.lastUnreliableBaselineReceived} messageBaseline={message.baselineTick}. This is fine.");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the new last received time for unreliable
|
||||
identity.lastUnreliableStateTime = connection.remoteTimeStamp;
|
||||
}
|
||||
// reliable baseline?
|
||||
else if (channelId == Channels.Reliable)
|
||||
{
|
||||
// set the last received reliable baseline tick number.
|
||||
identity.lastUnreliableBaselineReceived = message.baselineTick;
|
||||
}
|
||||
// set the last received reliable baseline tick number.
|
||||
identity.lastUnreliableBaselineReceived = message.baselineTick;
|
||||
|
||||
// iniital is always 'true' because unreliable state sync alwasy serializes full
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(message.payload))
|
||||
{
|
||||
// full state updates (initial=true) arrive over reliable.
|
||||
identity.DeserializeClient(reader, true);
|
||||
}
|
||||
}
|
||||
// no warning. unreliable messages often arrive before/after the reliable spawn/despawn messages.
|
||||
// else Debug.LogWarning($"Did not find target for sync message for {message.netId}. Were all prefabs added to the NetworkManager's spawnable list?\nNote: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message.");
|
||||
}
|
||||
|
||||
static void OnEntityStateMessageUnreliableDelta(EntityStateMessageUnreliableDelta message, int channelId)
|
||||
{
|
||||
// safety check: baseline should always arrive over Reliable channel.
|
||||
if (channelId != Channels.Unreliable)
|
||||
{
|
||||
Debug.LogError($"Client OnEntityStateMessageUnreliableDelta arrived on channel {channelId} instead of Unreliable. This should never happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug.Log($"NetworkClient.OnUpdateVarsMessage {msg.netId}");
|
||||
if (spawned.TryGetValue(message.netId, out NetworkIdentity identity) && identity != null)
|
||||
{
|
||||
// unreliable state sync messages may arrive out of order.
|
||||
// only ever apply state that's newer than the last received state.
|
||||
// note that we send one EntityStateMessage per Entity,
|
||||
// so there will be multiple with the same == timestamp.
|
||||
//
|
||||
// note that a reliable baseline may arrive before/after a delta.
|
||||
// that is fine.
|
||||
if (connection.remoteTimeStamp < identity.lastUnreliableStateTime)
|
||||
{
|
||||
// debug log to show that it's working.
|
||||
// can be tested via LatencySimulation scramble easily.
|
||||
Debug.Log($"Client caught out of order Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
// UDP messages may accidentally arrive twice.
|
||||
// or even intentionally, if unreliableRedundancy is turned on.
|
||||
else if (connection.remoteTimeStamp == identity.lastUnreliableStateTime)
|
||||
{
|
||||
// only log this if unreliableRedundancy is disabled.
|
||||
// otherwise it's expected and will happen a lot.
|
||||
if (!unreliableRedundancy) Debug.Log($"Client caught duplicate Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure this delta is for the correct baseline.
|
||||
// we don't want to apply an old delta on top of a new baseline.
|
||||
if (message.baselineTick != identity.lastUnreliableBaselineReceived)
|
||||
{
|
||||
Debug.Log($"Client caught Unreliable state message for old baseline for {identity} with baselineTick={identity.lastUnreliableBaselineReceived} messageBaseline={message.baselineTick}. This is fine.");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the new last received time for unreliable
|
||||
identity.lastUnreliableStateTime = connection.remoteTimeStamp;
|
||||
|
||||
// iniital is always 'true' because unreliable state sync alwasy serializes full
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(message.payload))
|
||||
{
|
||||
// delta state updates (initial=false) arrive over unreliable.
|
||||
bool initialState = channelId == Channels.Reliable;
|
||||
identity.DeserializeClient(reader, initialState);
|
||||
identity.DeserializeClient(reader, false);
|
||||
}
|
||||
}
|
||||
// no warning. unreliable messages often arrive before/after the reliable spawn/despawn messages.
|
||||
@ -1755,7 +1778,7 @@ static void BroadcastToServer(bool unreliableBaselineElapsed)
|
||||
// (do this before baseline, since baseline clears dirty bits)
|
||||
if (writer.Position > 0)
|
||||
{
|
||||
EntityStateMessageUnreliable message = new EntityStateMessageUnreliable
|
||||
EntityStateMessageUnreliableDelta message = new EntityStateMessageUnreliableDelta
|
||||
{
|
||||
baselineTick = identity.lastUnreliableBaselineSent,
|
||||
netId = identity.netId,
|
||||
@ -1789,7 +1812,7 @@ static void BroadcastToServer(bool unreliableBaselineElapsed)
|
||||
identity.lastUnreliableBaselineSent = (byte)Time.frameCount;
|
||||
|
||||
// send state update message
|
||||
EntityStateMessageUnreliable message = new EntityStateMessageUnreliable
|
||||
EntityStateMessageUnreliableBaseline message = new EntityStateMessageUnreliableBaseline
|
||||
{
|
||||
baselineTick = identity.lastUnreliableBaselineSent,
|
||||
netId = identity.netId,
|
||||
|
@ -323,7 +323,8 @@ internal static void RegisterMessageHandlers()
|
||||
RegisterHandler<NetworkPingMessage>(NetworkTime.OnServerPing, false);
|
||||
RegisterHandler<NetworkPongMessage>(NetworkTime.OnServerPong, false);
|
||||
RegisterHandler<EntityStateMessage>(OnEntityStateMessage, true);
|
||||
RegisterHandler<EntityStateMessageUnreliable>(OnEntityStateMessageUnreliable, true);
|
||||
RegisterHandler<EntityStateMessageUnreliableBaseline>(OnEntityStateMessageUnreliableBaseline, true);
|
||||
RegisterHandler<EntityStateMessageUnreliableDelta>(OnEntityStateMessageUnreliableDelta, true);
|
||||
RegisterHandler<TimeSnapshotMessage>(OnTimeSnapshotMessage, false); // unreliable may arrive before reliable authority went through
|
||||
}
|
||||
|
||||
@ -439,8 +440,15 @@ static void OnEntityStateMessage(NetworkConnectionToClient connection, EntitySta
|
||||
}
|
||||
|
||||
// for client's owned ClientToServer components.
|
||||
static void OnEntityStateMessageUnreliable(NetworkConnectionToClient connection, EntityStateMessageUnreliable message, int channelId)
|
||||
static void OnEntityStateMessageUnreliableBaseline(NetworkConnectionToClient connection, EntityStateMessageUnreliableBaseline message, int channelId)
|
||||
{
|
||||
// safety check: baseline should always arrive over Reliable channel.
|
||||
if (channelId != Channels.Reliable)
|
||||
{
|
||||
Debug.LogError($"Server OnEntityStateMessageUnreliableBaseline arrived on channel {channelId} instead of Reliable. This should never happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
// need to validate permissions carefully.
|
||||
// an attacker may attempt to modify a not-owned or not-ClientToServer component.
|
||||
|
||||
@ -450,47 +458,8 @@ static void OnEntityStateMessageUnreliable(NetworkConnectionToClient connection,
|
||||
// owned by the connection?
|
||||
if (identity.connectionToClient == connection)
|
||||
{
|
||||
// unreliable delta?
|
||||
if (channelId == Channels.Unreliable)
|
||||
{
|
||||
// unreliable state sync messages may arrive out of order.
|
||||
// only ever apply state that's newer than the last received state.
|
||||
// note that we send one EntityStateMessage per Entity,
|
||||
// so there will be multiple with the same == timestamp.
|
||||
if (connection.remoteTimeStamp < identity.lastUnreliableStateTime)
|
||||
{
|
||||
// debug log to show that it's working.
|
||||
// can be tested via LatencySimulation scramble easily.
|
||||
Debug.Log($"Server caught out of order Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
// UDP messages may accidentally arrive twice.
|
||||
// or even intentionally, if unreliableRedundancy is turned on.
|
||||
else if (connection.remoteTimeStamp == identity.lastUnreliableStateTime)
|
||||
{
|
||||
// only log this if unreliableRedundancy is disabled.
|
||||
// otherwise it's expected and will happen a lot.
|
||||
if (!unreliableRedundancy) Debug.Log($"Server caught duplicate Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure this delta is for the correct baseline.
|
||||
// we don't want to apply an old delta on top of a new baseline.
|
||||
if (message.baselineTick != identity.lastUnreliableBaselineReceived)
|
||||
{
|
||||
Debug.Log($"Server caught Unreliable state message for old baseline for {identity} with baselineTick={identity.lastUnreliableBaselineReceived} messageBaseline={message.baselineTick}. This is fine.");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the new last received time for unreliable
|
||||
identity.lastUnreliableStateTime = connection.remoteTimeStamp;
|
||||
}
|
||||
// reliable baseline?
|
||||
else if (channelId == Channels.Reliable)
|
||||
{
|
||||
// set the last received reliable baseline tick number.
|
||||
identity.lastUnreliableBaselineReceived = message.baselineTick;
|
||||
}
|
||||
// set the last received reliable baseline tick number.
|
||||
identity.lastUnreliableBaselineReceived = message.baselineTick;
|
||||
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(message.payload))
|
||||
{
|
||||
@ -498,9 +467,87 @@ static void OnEntityStateMessageUnreliable(NetworkConnectionToClient connection,
|
||||
// failure to deserialize disconnects to prevent exploits.
|
||||
//
|
||||
// full state updates (initial=true) arrive over reliable.
|
||||
if (!identity.DeserializeServer(reader, true))
|
||||
{
|
||||
if (exceptionsDisconnect)
|
||||
{
|
||||
Debug.LogError($"Server failed to deserialize client unreliable state for {identity.name} with netId={identity.netId}, Disconnecting.");
|
||||
connection.Disconnect();
|
||||
}
|
||||
else
|
||||
Debug.LogWarning($"Server failed to deserialize client unreliable state for {identity.name} with netId={identity.netId}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// An attacker may attempt to modify another connection's entity
|
||||
// This could also be a race condition of message in flight when
|
||||
// RemoveClientAuthority is called, so not malicious.
|
||||
// Don't disconnect, just log the warning.
|
||||
else
|
||||
Debug.LogWarning($"EntityStateMessage from {connection} for {identity.name} without authority.");
|
||||
}
|
||||
// no warning. unreliable messages often arrive before/after the reliable spawn/despawn messages.
|
||||
// else Debug.LogWarning($"Did not find target for sync message for {message.netId} . Note: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message.");
|
||||
}
|
||||
|
||||
// for client's owned ClientToServer components.
|
||||
static void OnEntityStateMessageUnreliableDelta(NetworkConnectionToClient connection, EntityStateMessageUnreliableDelta message, int channelId)
|
||||
{
|
||||
// safety check: baseline should always arrive over Reliable channel.
|
||||
if (channelId != Channels.Unreliable)
|
||||
{
|
||||
Debug.LogError($"Server OnEntityStateMessageUnreliableDelta arrived on channel {channelId} instead of Unreliable. This should never happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
// need to validate permissions carefully.
|
||||
// an attacker may attempt to modify a not-owned or not-ClientToServer component.
|
||||
|
||||
// valid netId?
|
||||
if (spawned.TryGetValue(message.netId, out NetworkIdentity identity) && identity != null)
|
||||
{
|
||||
// owned by the connection?
|
||||
if (identity.connectionToClient == connection)
|
||||
{
|
||||
// unreliable state sync messages may arrive out of order.
|
||||
// only ever apply state that's newer than the last received state.
|
||||
// note that we send one EntityStateMessage per Entity,
|
||||
// so there will be multiple with the same == timestamp.
|
||||
if (connection.remoteTimeStamp < identity.lastUnreliableStateTime)
|
||||
{
|
||||
// debug log to show that it's working.
|
||||
// can be tested via LatencySimulation scramble easily.
|
||||
Debug.Log($"Server caught out of order Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
// UDP messages may accidentally arrive twice.
|
||||
// or even intentionally, if unreliableRedundancy is turned on.
|
||||
else if (connection.remoteTimeStamp == identity.lastUnreliableStateTime)
|
||||
{
|
||||
// only log this if unreliableRedundancy is disabled.
|
||||
// otherwise it's expected and will happen a lot.
|
||||
if (!unreliableRedundancy) Debug.Log($"Server caught duplicate Unreliable state message for {identity.name}. This is fine.\nIdentity timestamp={identity.lastUnreliableStateTime:F3} batch remoteTimestamp={connection.remoteTimeStamp:F3}");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure this delta is for the correct baseline.
|
||||
// we don't want to apply an old delta on top of a new baseline.
|
||||
if (message.baselineTick != identity.lastUnreliableBaselineReceived)
|
||||
{
|
||||
Debug.Log($"Server caught Unreliable state message for old baseline for {identity} with baselineTick={identity.lastUnreliableBaselineReceived} messageBaseline={message.baselineTick}. This is fine.");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the new last received time for unreliable
|
||||
identity.lastUnreliableStateTime = connection.remoteTimeStamp;
|
||||
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(message.payload))
|
||||
{
|
||||
// DeserializeServer checks permissions internally.
|
||||
// failure to deserialize disconnects to prevent exploits.
|
||||
//
|
||||
// delta state updates (initial=false) arrive over unreliable.
|
||||
bool initialState = channelId == Channels.Reliable;
|
||||
if (!identity.DeserializeServer(reader, initialState))
|
||||
if (!identity.DeserializeServer(reader, false))
|
||||
{
|
||||
if (exceptionsDisconnect)
|
||||
{
|
||||
@ -2099,7 +2146,7 @@ static void BroadcastToConnection(NetworkConnectionToClient connection, bool unr
|
||||
// reliable baseline also clears dirty bits, so unreliable must be sent first.
|
||||
if (deltaSerialization != null)
|
||||
{
|
||||
EntityStateMessageUnreliable message = new EntityStateMessageUnreliable
|
||||
EntityStateMessageUnreliableDelta message = new EntityStateMessageUnreliableDelta
|
||||
{
|
||||
baselineTick = identity.lastUnreliableBaselineSent,
|
||||
netId = identity.netId,
|
||||
@ -2124,7 +2171,7 @@ static void BroadcastToConnection(NetworkConnectionToClient connection, bool unr
|
||||
// just something small to compare against.
|
||||
identity.lastUnreliableBaselineSent = (byte)Time.frameCount;
|
||||
|
||||
EntityStateMessageUnreliable message = new EntityStateMessageUnreliable
|
||||
EntityStateMessageUnreliableBaseline message = new EntityStateMessageUnreliableBaseline
|
||||
{
|
||||
baselineTick = identity.lastUnreliableBaselineSent,
|
||||
netId = identity.netId,
|
||||
|
Loading…
Reference in New Issue
Block a user