mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
perf: NetworkServer.Broadcast serialization lookup removed. serializations are now cached and rebuilt in NetworkIdentity based on timestamp.
=> way easier => way faster because we don't need to recycle two writers for every .spawned at the end of broadcast
This commit is contained in:
parent
fe3b7ae57c
commit
089e5bbb59
@ -19,6 +19,16 @@ namespace Mirror
|
||||
// to everyone etc.
|
||||
public enum Visibility { Default, ForceHidden, ForceShown }
|
||||
|
||||
public struct NetworkIdentitySerialization
|
||||
{
|
||||
public float tickTimeStamp;
|
||||
public NetworkWriter ownerWriter;
|
||||
public NetworkWriter observersWriter;
|
||||
// TODO there is probably a more simple way later
|
||||
public int ownerWritten;
|
||||
public int observersWritten;
|
||||
}
|
||||
|
||||
/// <summary>NetworkIdentity identifies objects across the network.</summary>
|
||||
[DisallowMultipleComponent]
|
||||
[AddComponentMenu("Network/NetworkIdentity")]
|
||||
@ -144,6 +154,19 @@ internal set
|
||||
[Tooltip("Visibility can overwrite interest management. ForceHidden can be useful to hide monsters while they respawn. ForceShown can be useful for score NetworkIdentities that should always broadcast to everyone in the world.")]
|
||||
public Visibility visible = Visibility.Default;
|
||||
|
||||
// broadcasting serializes all entities around a player for each player.
|
||||
// we don't want to serialize one entity twice in the same tick.
|
||||
// so we cache the last serialization and remember the timestamp so we
|
||||
// know which Update it was serialized.
|
||||
// (timestamp is the same while inside Update)
|
||||
// => this way we don't need to pool thousands of writers either.
|
||||
// => way easier to store them per object
|
||||
NetworkIdentitySerialization lastSerialization = new NetworkIdentitySerialization
|
||||
{
|
||||
ownerWriter = new NetworkWriter(),
|
||||
observersWriter = new NetworkWriter()
|
||||
};
|
||||
|
||||
/// <summary>Prefab GUID used to spawn prefabs across the network.</summary>
|
||||
//
|
||||
// The AssetId trick:
|
||||
@ -903,6 +926,32 @@ internal void OnSerializeAllSafely(bool initialState, NetworkWriter ownerWriter,
|
||||
}
|
||||
}
|
||||
|
||||
// get cached serialization for this tick (or serialize if none yet)
|
||||
internal NetworkIdentitySerialization GetSerializationAtTick(float tickTimeStamp)
|
||||
{
|
||||
// serialize fresh if tick is newer than last one
|
||||
if (lastSerialization.tickTimeStamp < tickTimeStamp)
|
||||
{
|
||||
// reset
|
||||
lastSerialization.ownerWriter.Position = 0;
|
||||
lastSerialization.observersWriter.Position = 0;
|
||||
|
||||
// serialize
|
||||
OnSerializeAllSafely(false,
|
||||
lastSerialization.ownerWriter,
|
||||
out lastSerialization.ownerWritten,
|
||||
lastSerialization.observersWriter,
|
||||
out lastSerialization.observersWritten);
|
||||
|
||||
// set timestamp
|
||||
lastSerialization.tickTimeStamp = tickTimeStamp;
|
||||
//Debug.Log($"{name} (netId={netId}) serialized for tick={tickTimeStamp}");
|
||||
}
|
||||
|
||||
// return it
|
||||
return lastSerialization;
|
||||
}
|
||||
|
||||
void OnDeserializeSafely(NetworkBehaviour comp, NetworkReader reader, bool initialState)
|
||||
{
|
||||
// read header as 4 bytes and calculate this chunk's start+end
|
||||
|
@ -1370,70 +1370,11 @@ public static void RebuildObservers(NetworkIdentity identity, bool initialize)
|
||||
}
|
||||
|
||||
// broadcasting ////////////////////////////////////////////////////////
|
||||
// cache NetworkIdentity serializations
|
||||
// Update() shouldn't serialize multiple times for multiple connections
|
||||
struct Serialization
|
||||
{
|
||||
public PooledNetworkWriter ownerWriter;
|
||||
public PooledNetworkWriter observersWriter;
|
||||
// TODO there is probably a more simple way later
|
||||
public int ownerWritten;
|
||||
public int observersWritten;
|
||||
}
|
||||
static Dictionary<NetworkIdentity, Serialization> serializations =
|
||||
new Dictionary<NetworkIdentity, Serialization>();
|
||||
|
||||
// helper function to get an entity's serialization with caching
|
||||
static Serialization GetEntitySerialization(NetworkIdentity identity)
|
||||
{
|
||||
// multiple connections might be observed by the
|
||||
// same NetworkIdentity, but we don't want to
|
||||
// serialize them multiple times. look it up first.
|
||||
//
|
||||
// IMPORTANT: don't forget to return them to pool!
|
||||
// TODO make this easier later. for now aim for
|
||||
// feature parity to not break projects.
|
||||
// TODO let the entity cache it's own serialization
|
||||
// and recompute only if it was dirty.
|
||||
if (!serializations.ContainsKey(identity))
|
||||
{
|
||||
// serialize all the dirty components.
|
||||
// one version for owner, one for observers.
|
||||
PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter();
|
||||
PooledNetworkWriter observersWriter = NetworkWriterPool.GetWriter();
|
||||
identity.OnSerializeAllSafely(false, ownerWriter, out int ownerWritten, observersWriter, out int observersWritten);
|
||||
serializations[identity] = new Serialization
|
||||
{
|
||||
ownerWriter = ownerWriter,
|
||||
observersWriter = observersWriter,
|
||||
ownerWritten = ownerWritten,
|
||||
observersWritten = observersWritten
|
||||
};
|
||||
|
||||
// clear dirty bits only for the components that we serialized
|
||||
// DO NOT clean ALL component's dirty bits, because
|
||||
// components can have different syncIntervals and we don't
|
||||
// want to reset dirty bits for the ones that were not
|
||||
// synced yet.
|
||||
// (we serialized only the IsDirty() components, or all of
|
||||
// them if initialState. clearing the dirty ones is enough.)
|
||||
//
|
||||
// NOTE: this is what we did before push->pull
|
||||
// broadcasting. let's keep doing this for
|
||||
// feature parity to not break anyone's project.
|
||||
// TODO make this more simple / unnecessary later.
|
||||
identity.ClearDirtyComponentsDirtyBits();
|
||||
}
|
||||
|
||||
// return the serialization
|
||||
return serializations[identity];
|
||||
}
|
||||
|
||||
// helper function to get the right serialization for a connection
|
||||
static NetworkWriter GetEntitySerializationForConnection(NetworkIdentity identity, NetworkConnectionToClient connection)
|
||||
{
|
||||
// get serialization for this entity (cached)
|
||||
Serialization serialization = GetEntitySerialization(identity);
|
||||
NetworkIdentitySerialization serialization = identity.GetSerializationAtTick(Time.time);
|
||||
|
||||
// is this entity owned by this connection?
|
||||
bool owned = identity.connectionToClient == connection;
|
||||
@ -1458,20 +1399,6 @@ static NetworkWriter GetEntitySerializationForConnection(NetworkIdentity identit
|
||||
return null;
|
||||
}
|
||||
|
||||
// helper function to clean up cached serializations
|
||||
static void CleanupSerializations()
|
||||
{
|
||||
// return serialized writers to pool, clear set
|
||||
// TODO this is for feature parity before push->pull change.
|
||||
// make this more simple / unnecessary later.
|
||||
foreach (Serialization entry in serializations.Values)
|
||||
{
|
||||
NetworkWriterPool.Recycle(entry.ownerWriter);
|
||||
NetworkWriterPool.Recycle(entry.observersWriter);
|
||||
}
|
||||
serializations.Clear();
|
||||
}
|
||||
|
||||
// helper function to clear dirty bits of all spawned entities
|
||||
static void ClearSpawnedDirtyBits()
|
||||
{
|
||||
@ -1517,6 +1444,20 @@ static void BroadcastToConnection(NetworkConnectionToClient connection)
|
||||
};
|
||||
connection.Send(message);
|
||||
}
|
||||
|
||||
// clear dirty bits only for the components that we serialized
|
||||
// DO NOT clean ALL component's dirty bits, because
|
||||
// components can have different syncIntervals and we don't
|
||||
// want to reset dirty bits for the ones that were not
|
||||
// synced yet.
|
||||
// (we serialized only the IsDirty() components, or all of
|
||||
// them if initialState. clearing the dirty ones is enough.)
|
||||
//
|
||||
// NOTE: this is what we did before push->pull
|
||||
// broadcasting. let's keep doing this for
|
||||
// feature parity to not break anyone's project.
|
||||
// TODO make this more simple / unnecessary later.
|
||||
identity.ClearDirtyComponentsDirtyBits();
|
||||
}
|
||||
// spawned list should have no null entries because we
|
||||
// always call Remove in OnObjectDestroy everywhere.
|
||||
@ -1582,9 +1523,6 @@ static void Broadcast()
|
||||
connection.Update();
|
||||
}
|
||||
|
||||
// return serialized writers to pool
|
||||
CleanupSerializations();
|
||||
|
||||
// TODO we already clear the serialized component's dirty bits above
|
||||
// might as well clear everything???
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user