feat(NetworkServer): Improved ReplacePlayerForConnection (#3885)

- ReplacePlayerOptions: KeepAuthority, KeepActive, Unspawn, Destroy
- NetworkServer tests updated
- Examples updated and work correctly

Eliminates need to make a coroutine to replace & destroy player.
This commit is contained in:
MrGadget 2024-08-10 05:40:52 -04:00 committed by GitHub
parent 46010ca49c
commit d8c7c1e815
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 43 deletions

View File

@ -146,7 +146,7 @@ void SceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer)
return; return;
// replace room player with game player // replace room player with game player
NetworkServer.ReplacePlayerForConnection(conn, gamePlayer, true); NetworkServer.ReplacePlayerForConnection(conn, gamePlayer, ReplacePlayerOptions.KeepAuthority);
} }
internal void CallOnClientEnterRoom() internal void CallOnClientEnterRoom()
@ -342,7 +342,7 @@ public override void ServerChangeScene(string newSceneName)
{ {
// re-add the room object // re-add the room object
roomPlayer.GetComponent<NetworkRoomPlayer>().readyToBegin = false; roomPlayer.GetComponent<NetworkRoomPlayer>().readyToBegin = false;
NetworkServer.ReplacePlayerForConnection(identity.connectionToClient, roomPlayer.gameObject); NetworkServer.ReplacePlayerForConnection(identity.connectionToClient, roomPlayer.gameObject, ReplacePlayerOptions.KeepAuthority);
} }
} }

View File

@ -6,6 +6,18 @@
namespace Mirror namespace Mirror
{ {
public enum ReplacePlayerOptions
{
/// <summary>Player Object remains active on server and clients. Ownership is not removed</summary>
KeepAuthority,
/// <summary>Player Object remains active on server and clients. Only ownership is removed</summary>
KeepActive,
/// <summary>Player Object is unspawned on clients but remains on server</summary>
Unspawn,
/// <summary>Player Object is destroyed on server and clients</summary>
Destroy
}
public enum RemovePlayerOptions public enum RemovePlayerOptions
{ {
/// <summary>Player Object remains active on server and clients. Only ownership is removed</summary> /// <summary>Player Object remains active on server and clients. Only ownership is removed</summary>
@ -1100,10 +1112,36 @@ public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameOb
return true; return true;
} }
/// <summary>Replaces connection's player object. The old object is not destroyed.</summary> // Deprecated 2024-008-09
// This does NOT change the ready state of the connection, so it can [Obsolete("Use ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, uint assetId, ReplacePlayerOptions replacePlayerOptions) instead")]
// safely be used while changing scenes. public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, uint assetId, bool keepAuthority = false)
{
if (GetNetworkIdentity(player, out NetworkIdentity identity))
identity.assetId = assetId;
return ReplacePlayerForConnection(conn, player, keepAuthority ? ReplacePlayerOptions.KeepAuthority : ReplacePlayerOptions.KeepActive);
}
// Deprecated 2024-008-09
[Obsolete("Use ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, ReplacePlayerOptions replacePlayerOptions) instead")]
public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, bool keepAuthority = false) public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, bool keepAuthority = false)
{
return ReplacePlayerForConnection(conn, player, keepAuthority ? ReplacePlayerOptions.KeepAuthority : ReplacePlayerOptions.KeepActive);
}
/// <summary>Replaces connection's player object. The old object is not destroyed.</summary>
// This does NOT change the ready state of the connection, so it can safely be used while changing scenes.
public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, uint assetId, ReplacePlayerOptions replacePlayerOptions)
{
if (GetNetworkIdentity(player, out NetworkIdentity identity))
identity.assetId = assetId;
return ReplacePlayerForConnection(conn, player, replacePlayerOptions);
}
/// <summary>Replaces connection's player object. The old object is not destroyed.</summary>
// This does NOT change the ready state of the connection, so it can safely be used while changing scenes.
public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, ReplacePlayerOptions replacePlayerOptions)
{ {
if (!player.TryGetComponent(out NetworkIdentity identity)) if (!player.TryGetComponent(out NetworkIdentity identity))
{ {
@ -1145,33 +1183,28 @@ public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, Ga
Respawn(identity); Respawn(identity);
if (keepAuthority) switch (replacePlayerOptions)
{ {
// This needs to be sent to clear isLocalPlayer on case ReplacePlayerOptions.KeepAuthority:
// client while keeping hasAuthority true // This needs to be sent to clear isLocalPlayer on
SendChangeOwnerMessage(previousPlayer, conn); // client while keeping hasAuthority true
} SendChangeOwnerMessage(previousPlayer, conn);
else break;
{ case ReplacePlayerOptions.KeepActive:
// This clears both isLocalPlayer and hasAuthority on client // This clears both isLocalPlayer and hasAuthority on client
previousPlayer.RemoveClientAuthority(); previousPlayer.RemoveClientAuthority();
break;
case ReplacePlayerOptions.Unspawn:
UnSpawn(previousPlayer.gameObject);
break;
case ReplacePlayerOptions.Destroy:
Destroy(previousPlayer.gameObject);
break;
} }
return true; return true;
} }
/// <summary>Replaces connection's player object. The old object is not destroyed.</summary>
// This does NOT change the ready state of the connection, so it can
// safely be used while changing scenes.
public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, uint assetId, bool keepAuthority = false)
{
if (GetNetworkIdentity(player, out NetworkIdentity identity))
{
identity.assetId = assetId;
}
return ReplacePlayerForConnection(conn, player, keepAuthority);
}
/// <summary>Removes the player object from the connection</summary> /// <summary>Removes the player object from the connection</summary>
// destroyServerObject: Indicates whether the server object should be destroyed // destroyServerObject: Indicates whether the server object should be destroyed
// Deprecated 2024-06-06 // Deprecated 2024-06-06

View File

@ -113,8 +113,7 @@ void OnReplaceCharacterMessage(NetworkConnectionToClient conn, ReplaceCharacterM
GameObject playerObject = Instantiate(characterData.characterPrefabs[message.createCharacterMessage.characterNumber], oldPlayer.transform.position, oldPlayer.transform.rotation); GameObject playerObject = Instantiate(characterData.characterPrefabs[message.createCharacterMessage.characterNumber], oldPlayer.transform.position, oldPlayer.transform.rotation);
// Instantiate the new player object and broadcast to clients // Instantiate the new player object and broadcast to clients
// Include true for keepAuthority paramater to prevent ownership change NetworkServer.ReplacePlayerForConnection(conn, playerObject, ReplacePlayerOptions.KeepActive);
NetworkServer.ReplacePlayerForConnection(conn, playerObject, true);
// Apply data from the message however appropriate for your game // Apply data from the message however appropriate for your game
// Typically Player would be a component you write with syncvars or properties // Typically Player would be a component you write with syncvars or properties
@ -133,4 +132,4 @@ public void ReplaceCharacter(ReplaceCharacterMessage message)
NetworkClient.Send(message); NetworkClient.Send(message);
} }
} }
} }

View File

@ -92,17 +92,7 @@ void CmdTakeControl(NetworkConnectionToClient conn = null)
isControlled = true; isControlled = true;
// set the player object to be the tank, keep ownership of NetworkServer.ReplacePlayerForConnection(conn, gameObject, ReplacePlayerOptions.Unspawn);
// the original player object to avoid ChangeOwner message.
NetworkServer.ReplacePlayerForConnection(conn, gameObject);
StartCoroutine(UnspawnOldPlayer((GameObject)conn.authenticationData));
}
IEnumerator UnspawnOldPlayer(GameObject player)
{
yield return new WaitForSeconds(0.1f);
NetworkServer.UnSpawn(player);
} }
[Command] [Command]
@ -123,7 +113,7 @@ void CmdReleaseControl()
// clear the player object // clear the player object
connectionToClient.authenticationData = null; connectionToClient.authenticationData = null;
NetworkServer.ReplacePlayerForConnection(connectionToClient, player); NetworkServer.ReplacePlayerForConnection(connectionToClient, player, ReplacePlayerOptions.KeepActive);
} }
} }

View File

@ -1342,7 +1342,7 @@ public void ReplacePlayerForConnection_CallsOnStartLocalPlayer()
clientNextIdentity.name = nameof(clientNextIdentity); clientNextIdentity.name = nameof(clientNextIdentity);
// replace connection's player from 'previous' to 'next' // replace connection's player from 'previous' to 'next'
NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject); NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject, ReplacePlayerOptions.KeepActive);
ProcessMessages(); ProcessMessages();
// should call OnStartLocalPlayer on 'next' since it became the new local player. // should call OnStartLocalPlayer on 'next' since it became the new local player.
@ -1372,7 +1372,7 @@ public void ReplacePlayerForConnection_CallsOnStopLocalPlayer()
clientNextIdentity.name = nameof(clientNextIdentity); clientNextIdentity.name = nameof(clientNextIdentity);
// replace connection's player from 'previous' to 'next' // replace connection's player from 'previous' to 'next'
NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject); NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject, ReplacePlayerOptions.KeepActive);
ProcessMessages(); ProcessMessages();
// should call OnStopLocalPlayer on 'previous' since it's not owned anymore now. // should call OnStopLocalPlayer on 'previous' since it's not owned anymore now.