diff --git a/Assets/Mirror/Components/NetworkRoomManager.cs b/Assets/Mirror/Components/NetworkRoomManager.cs index 4df69c6fa..698b190b3 100644 --- a/Assets/Mirror/Components/NetworkRoomManager.cs +++ b/Assets/Mirror/Components/NetworkRoomManager.cs @@ -146,7 +146,7 @@ void SceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer) return; // replace room player with game player - NetworkServer.ReplacePlayerForConnection(conn, gamePlayer, true); + NetworkServer.ReplacePlayerForConnection(conn, gamePlayer, ReplacePlayerOptions.KeepAuthority); } internal void CallOnClientEnterRoom() @@ -342,7 +342,7 @@ public override void ServerChangeScene(string newSceneName) { // re-add the room object roomPlayer.GetComponent().readyToBegin = false; - NetworkServer.ReplacePlayerForConnection(identity.connectionToClient, roomPlayer.gameObject); + NetworkServer.ReplacePlayerForConnection(identity.connectionToClient, roomPlayer.gameObject, ReplacePlayerOptions.KeepAuthority); } } diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index 0bc608325..ccb30289c 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -6,6 +6,18 @@ namespace Mirror { + public enum ReplacePlayerOptions + { + /// Player Object remains active on server and clients. Ownership is not removed + KeepAuthority, + /// Player Object remains active on server and clients. Only ownership is removed + KeepActive, + /// Player Object is unspawned on clients but remains on server + Unspawn, + /// Player Object is destroyed on server and clients + Destroy + } + public enum RemovePlayerOptions { /// Player Object remains active on server and clients. Only ownership is removed @@ -1100,10 +1112,36 @@ public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameOb return true; } - /// Replaces connection's player object. The old object is not destroyed. - // This does NOT change the ready state of the connection, so it can - // safely be used while changing scenes. + // Deprecated 2024-008-09 + [Obsolete("Use ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, uint assetId, ReplacePlayerOptions replacePlayerOptions) instead")] + 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) + { + return ReplacePlayerForConnection(conn, player, keepAuthority ? ReplacePlayerOptions.KeepAuthority : ReplacePlayerOptions.KeepActive); + } + + /// Replaces connection's player object. The old object is not destroyed. + // 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); + } + + /// Replaces connection's player object. The old object is not destroyed. + // 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)) { @@ -1145,33 +1183,28 @@ public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, Ga Respawn(identity); - if (keepAuthority) + switch (replacePlayerOptions) { - // This needs to be sent to clear isLocalPlayer on - // client while keeping hasAuthority true - SendChangeOwnerMessage(previousPlayer, conn); - } - else - { - // This clears both isLocalPlayer and hasAuthority on client - previousPlayer.RemoveClientAuthority(); + case ReplacePlayerOptions.KeepAuthority: + // This needs to be sent to clear isLocalPlayer on + // client while keeping hasAuthority true + SendChangeOwnerMessage(previousPlayer, conn); + break; + case ReplacePlayerOptions.KeepActive: + // This clears both isLocalPlayer and hasAuthority on client + previousPlayer.RemoveClientAuthority(); + break; + case ReplacePlayerOptions.Unspawn: + UnSpawn(previousPlayer.gameObject); + break; + case ReplacePlayerOptions.Destroy: + Destroy(previousPlayer.gameObject); + break; } return true; } - /// Replaces connection's player object. The old object is not destroyed. - // 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); - } - /// Removes the player object from the connection // destroyServerObject: Indicates whether the server object should be destroyed // Deprecated 2024-06-06 diff --git a/Assets/Mirror/Examples/CharacterSelection/Scripts/NetworkManagerCharacterSelection.cs b/Assets/Mirror/Examples/CharacterSelection/Scripts/NetworkManagerCharacterSelection.cs index 3a9100709..7fe1efc53 100644 --- a/Assets/Mirror/Examples/CharacterSelection/Scripts/NetworkManagerCharacterSelection.cs +++ b/Assets/Mirror/Examples/CharacterSelection/Scripts/NetworkManagerCharacterSelection.cs @@ -113,8 +113,7 @@ void OnReplaceCharacterMessage(NetworkConnectionToClient conn, ReplaceCharacterM GameObject playerObject = Instantiate(characterData.characterPrefabs[message.createCharacterMessage.characterNumber], oldPlayer.transform.position, oldPlayer.transform.rotation); // Instantiate the new player object and broadcast to clients - // Include true for keepAuthority paramater to prevent ownership change - NetworkServer.ReplacePlayerForConnection(conn, playerObject, true); + NetworkServer.ReplacePlayerForConnection(conn, playerObject, ReplacePlayerOptions.KeepActive); // Apply data from the message however appropriate for your game // Typically Player would be a component you write with syncvars or properties @@ -133,4 +132,4 @@ public void ReplaceCharacter(ReplaceCharacterMessage message) NetworkClient.Send(message); } } -} \ No newline at end of file +} diff --git a/Assets/Mirror/Examples/TankTheftAuto/Scripts/TankAuthority.cs b/Assets/Mirror/Examples/TankTheftAuto/Scripts/TankAuthority.cs index adf7b8480..f26f103ac 100644 --- a/Assets/Mirror/Examples/TankTheftAuto/Scripts/TankAuthority.cs +++ b/Assets/Mirror/Examples/TankTheftAuto/Scripts/TankAuthority.cs @@ -92,17 +92,7 @@ void CmdTakeControl(NetworkConnectionToClient conn = null) isControlled = true; - // set the player object to be the tank, keep ownership of - // 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); + NetworkServer.ReplacePlayerForConnection(conn, gameObject, ReplacePlayerOptions.Unspawn); } [Command] @@ -123,7 +113,7 @@ void CmdReleaseControl() // clear the player object connectionToClient.authenticationData = null; - NetworkServer.ReplacePlayerForConnection(connectionToClient, player); + NetworkServer.ReplacePlayerForConnection(connectionToClient, player, ReplacePlayerOptions.KeepActive); } } diff --git a/Assets/Mirror/Tests/Editor/NetworkServer/NetworkServerTest.cs b/Assets/Mirror/Tests/Editor/NetworkServer/NetworkServerTest.cs index 8fb85a821..25b1ccf99 100644 --- a/Assets/Mirror/Tests/Editor/NetworkServer/NetworkServerTest.cs +++ b/Assets/Mirror/Tests/Editor/NetworkServer/NetworkServerTest.cs @@ -1342,7 +1342,7 @@ public void ReplacePlayerForConnection_CallsOnStartLocalPlayer() clientNextIdentity.name = nameof(clientNextIdentity); // replace connection's player from 'previous' to 'next' - NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject); + NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject, ReplacePlayerOptions.KeepActive); ProcessMessages(); // should call OnStartLocalPlayer on 'next' since it became the new local player. @@ -1372,7 +1372,7 @@ public void ReplacePlayerForConnection_CallsOnStopLocalPlayer() clientNextIdentity.name = nameof(clientNextIdentity); // replace connection's player from 'previous' to 'next' - NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject); + NetworkServer.ReplacePlayerForConnection(connectionToClient, serverNextIdentity.gameObject, ReplacePlayerOptions.KeepActive); ProcessMessages(); // should call OnStopLocalPlayer on 'previous' since it's not owned anymore now.