feat: SyncToOwner now works with authority (#1204)

* WIP redoing authority

* Local client also handles authority

* Remove unused callback

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: MrGadget <chris@clevertech.net>

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: vis2k <info@noobtuts.com>

* Rename pendingOwner

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: vis2k <info@noobtuts.com>

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: vis2k <info@noobtuts.com>

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: vis2k <info@noobtuts.com>

* Update Assets/Mirror/Runtime/NetworkIdentity.cs

Co-Authored-By: vis2k <info@noobtuts.com>
This commit is contained in:
Paul Pacheco 2019-11-05 06:55:32 -06:00 committed by vis2k
parent 24d8368b38
commit 92d0df7b39
5 changed files with 47 additions and 99 deletions

View File

@ -490,12 +490,14 @@ static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage msg)
identity.netId = msg.netId; identity.netId = msg.netId;
NetworkIdentity.spawned[msg.netId] = identity; NetworkIdentity.spawned[msg.netId] = identity;
identity.pendingAuthority = msg.isOwner;
// objects spawned as part of initial state are started on a second pass // objects spawned as part of initial state are started on a second pass
if (isSpawnFinished) if (isSpawnFinished)
{ {
identity.OnStartClient(); identity.OnStartClient();
CheckForLocalPlayer(identity); CheckForLocalPlayer(identity);
identity.hasAuthority = identity.pendingAuthority;
} }
} }
@ -599,6 +601,7 @@ internal static void OnObjectSpawnFinished(ObjectSpawnFinishedMessage _)
// use data from scene objects // use data from scene objects
foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values.OrderBy(uv => uv.netId)) foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values.OrderBy(uv => uv.netId))
{ {
identity.hasAuthority = identity.pendingAuthority;
if (!identity.isClient) if (!identity.isClient)
{ {
identity.OnStartClient(); identity.OnStartClient();
@ -712,16 +715,6 @@ internal static void OnSyncEventMessage(SyncEventMessage msg)
} }
} }
internal static void OnClientAuthority(ClientAuthorityMessage msg)
{
if (LogFilter.Debug) Debug.Log("ClientScene.OnClientAuthority for netId: " + msg.netId);
if (NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity identity))
{
identity.ForceAuthority(msg.authority);
}
}
// called for the one object in the spawn message which is the local player! // called for the one object in the spawn message which is the local player!
internal static void OnSpawnMessageForLocalPlayer(uint netId) internal static void OnSpawnMessageForLocalPlayer(uint netId)
{ {

View File

@ -340,6 +340,7 @@ public struct SpawnMessage : IMessageBase
{ {
public uint netId; public uint netId;
public bool isLocalPlayer; public bool isLocalPlayer;
public bool isOwner;
public ulong sceneId; public ulong sceneId;
public Guid assetId; public Guid assetId;
public Vector3 position; public Vector3 position;
@ -353,6 +354,7 @@ public void Deserialize(NetworkReader reader)
{ {
netId = reader.ReadPackedUInt32(); netId = reader.ReadPackedUInt32();
isLocalPlayer = reader.ReadBoolean(); isLocalPlayer = reader.ReadBoolean();
isOwner = reader.ReadBoolean();
sceneId = reader.ReadPackedUInt64(); sceneId = reader.ReadPackedUInt64();
if (sceneId == 0) if (sceneId == 0)
{ {
@ -368,6 +370,7 @@ public void Serialize(NetworkWriter writer)
{ {
writer.WritePackedUInt32(netId); writer.WritePackedUInt32(netId);
writer.WriteBoolean(isLocalPlayer); writer.WriteBoolean(isLocalPlayer);
writer.WriteBoolean(isOwner);
writer.WritePackedUInt64(sceneId); writer.WritePackedUInt64(sceneId);
if (sceneId == 0) if (sceneId == 0)
{ {
@ -424,24 +427,6 @@ public void Serialize(NetworkWriter writer)
} }
} }
public struct ClientAuthorityMessage : IMessageBase
{
public uint netId;
public bool authority;
public void Deserialize(NetworkReader reader)
{
netId = reader.ReadPackedUInt32();
authority = reader.ReadBoolean();
}
public void Serialize(NetworkWriter writer)
{
writer.WritePackedUInt32(netId);
writer.WriteBoolean(authority);
}
}
public struct UpdateVarsMessage : IMessageBase public struct UpdateVarsMessage : IMessageBase
{ {
public uint netId; public uint netId;

View File

@ -359,7 +359,6 @@ internal static void RegisterSystemHandlers(bool hostMode)
RegisterHandler<ObjectSpawnFinishedMessage>(ClientScene.OnObjectSpawnFinished); RegisterHandler<ObjectSpawnFinishedMessage>(ClientScene.OnObjectSpawnFinished);
RegisterHandler<UpdateVarsMessage>(ClientScene.OnUpdateVarsMessage); RegisterHandler<UpdateVarsMessage>(ClientScene.OnUpdateVarsMessage);
} }
RegisterHandler<ClientAuthorityMessage>(ClientScene.OnClientAuthority);
RegisterHandler<RpcMessage>(ClientScene.OnRPCMessage); RegisterHandler<RpcMessage>(ClientScene.OnRPCMessage);
RegisterHandler<SyncEventMessage>(ClientScene.OnSyncEventMessage); RegisterHandler<SyncEventMessage>(ClientScene.OnSyncEventMessage);
} }

View File

@ -82,7 +82,31 @@ public bool isServer
/// <para>This value is determined at runtime. For most objects, authority is held by the server.</para> /// <para>This value is determined at runtime. For most objects, authority is held by the server.</para>
/// <para>For objects that had their authority set by AssignClientAuthority on the server, this will be true on the client that owns the object. NOT on other clients.</para> /// <para>For objects that had their authority set by AssignClientAuthority on the server, this will be true on the client that owns the object. NOT on other clients.</para>
/// </summary> /// </summary>
public bool hasAuthority { get; private set; } bool isOwner;
public bool hasAuthority
{
get => isOwner;
set
{
bool previous = isOwner;
isOwner = value;
if (previous && !isOwner)
{
OnStopAuthority();
}
if (!previous && isOwner)
{
OnStartAuthority();
}
}
}
// whether this object has been spawned with authority
// we need hasAuthority and pendingOwner because
// we need to wait until all of them spawn before updating hasAuthority
internal bool pendingAuthority { get; set; }
/// <summary> /// <summary>
/// The set of network connections (players) that can see this object. /// The set of network connections (players) that can see this object.
@ -194,24 +218,6 @@ internal void SetClientOwner(NetworkConnection conn)
connectionToClient.AddOwnedObject(this); connectionToClient.AddOwnedObject(this);
} }
internal void ForceAuthority(bool authority)
{
if (hasAuthority == authority)
{
return;
}
hasAuthority = authority;
if (authority)
{
OnStartAuthority();
}
else
{
OnStopAuthority();
}
}
static uint nextNetworkId = 1; static uint nextNetworkId = 1;
internal static uint GetNextNetworkId() => nextNetworkId++; internal static uint GetNextNetworkId() => nextNetworkId++;
@ -220,23 +226,6 @@ internal void ForceAuthority(bool authority)
/// </summary> /// </summary>
public static void ResetNextNetworkId() => nextNetworkId = 1; public static void ResetNextNetworkId() => nextNetworkId = 1;
/// <summary>
/// Obsolete: Host Migration was removed
/// </summary>
/// <param name="conn">The network connection that is gaining or losing authority.</param>
/// <param name="identity">The object whose client authority status is being changed.</param>
/// <param name="authorityState">The new state of client authority of the object for the connection.</param>
[EditorBrowsable(EditorBrowsableState.Never), Obsolete("Host Migration was removed")]
public delegate void ClientAuthorityCallback(NetworkConnection conn, NetworkIdentity identity, bool authorityState);
/// <summary>
/// Obsolete: Host Migration was removed
/// <para>Whenever an object is spawned using SpawnWithClientAuthority, or the client authority status of an object is changed with AssignClientAuthority or RemoveClientAuthority, then this callback will be invoked.</para>
/// <para>This callback is used by the NetworkMigrationManager to distribute client authority state to peers for host migration. If the NetworkMigrationManager is not being used, this callback does not need to be populated.</para>
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never), Obsolete("Host Migration was removed")]
public static ClientAuthorityCallback clientAuthorityCallback;
// used when the player object for a connection changes // used when the player object for a connection changes
internal void SetNotLocalPlayer() internal void SetNotLocalPlayer()
{ {
@ -901,17 +890,11 @@ internal void SetLocalPlayer()
// OnStartAuthority should only be called if hasAuthority was false when this function began, // OnStartAuthority should only be called if hasAuthority was false when this function began,
// or it will be called twice for this object, but that state is lost by the time OnStartAuthority // or it will be called twice for this object, but that state is lost by the time OnStartAuthority
// is called below, so the original value is cached here to be checked below. // is called below, so the original value is cached here to be checked below.
bool originAuthority = hasAuthority;
hasAuthority = true; hasAuthority = true;
foreach (NetworkBehaviour comp in networkBehavioursCache) foreach (NetworkBehaviour comp in networkBehavioursCache)
{ {
comp.OnStartLocalPlayer(); comp.OnStartLocalPlayer();
if (!originAuthority)
{
comp.OnStartAuthority();
}
} }
} }
@ -1096,24 +1079,19 @@ public void RemoveClientAuthority()
if (connectionToClient != null) if (connectionToClient != null)
{ {
// send msg to that client NetworkConnectionToClient previousOwner = connectionToClient;
ClientAuthorityMessage msg = new ClientAuthorityMessage
{
netId = netId,
authority = false
};
connectionToClient.Send(msg);
#pragma warning disable CS0618 // Type or member is obsolete
clientAuthorityCallback?.Invoke(connectionToClient, this, false);
#pragma warning restore CS0618 // Type or member is obsolete
connectionToClient.RemoveOwnedObject(this); connectionToClient.RemoveOwnedObject(this);
connectionToClient = null; connectionToClient = null;
}
// server now has authority (this is only called on server) // we need to resynchronize the entire object
ForceAuthority(false); // so just spawn it again,
// the client will not create a new instance, it will simply
// reset all variables and remove authority
NetworkServer.SendSpawnMessage(this, previousOwner);
connectionToClient = null;
}
} }
/// <summary> /// <summary>
@ -1145,17 +1123,9 @@ public bool AssignClientAuthority(NetworkConnection conn)
SetClientOwner(conn); SetClientOwner(conn);
// send msg to that client // The client will match to the existing object
ClientAuthorityMessage msg = new ClientAuthorityMessage // update all variables and assign authority
{ NetworkServer.SendSpawnMessage(this, conn);
netId = netId,
authority = true
};
conn.Send(msg);
#pragma warning disable CS0618 // Type or member is obsolete
clientAuthorityCallback?.Invoke(conn, this, true);
#pragma warning restore CS0618 // Type or member is obsolete
return true; return true;
} }
@ -1173,7 +1143,6 @@ internal void Reset()
m_Reset = false; m_Reset = false;
m_IsServer = false; m_IsServer = false;
isClient = false; isClient = false;
hasAuthority = false;
netId = 0; netId = 0;
isLocalPlayer = false; isLocalPlayer = false;

View File

@ -1090,6 +1090,7 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio
{ {
netId = identity.netId, netId = identity.netId,
isLocalPlayer = conn?.identity == identity, isLocalPlayer = conn?.identity == identity,
isOwner = identity.connectionToClient == conn && conn != null,
sceneId = identity.sceneId, sceneId = identity.sceneId,
assetId = identity.assetId, assetId = identity.assetId,
// use local values for VR support // use local values for VR support
@ -1103,8 +1104,7 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio
{ {
// use owner segment if 'conn' owns this identity, otherwise // use owner segment if 'conn' owns this identity, otherwise
// use observers segment // use observers segment
bool isOwner = identity.connectionToClient == conn; msg.payload = msg.isOwner ? ownerSegment : observersSegment;
msg.payload = isOwner ? ownerSegment : observersSegment;
conn.Send(msg); conn.Send(msg);
} }
@ -1116,6 +1116,7 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio
// serialized because the spawn message contains more data. // serialized because the spawn message contains more data.
// components might still be updated later on.) // components might still be updated later on.)
msg.payload = ownerSegment; msg.payload = ownerSegment;
msg.isOwner = true;
SendToClientOfPlayer(identity, msg); SendToClientOfPlayer(identity, msg);
// send observersWriter to everyone but owner // send observersWriter to everyone but owner
@ -1123,6 +1124,7 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio
// serialized because the spawn message contains more data. // serialized because the spawn message contains more data.
// components might still be updated later on.) // components might still be updated later on.)
msg.payload = observersSegment; msg.payload = observersSegment;
msg.isOwner = false;
SendToReady(identity, msg, false); SendToReady(identity, msg, false);
} }