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;
// 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<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
{
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
{
/// <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;
}
/// <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.
// 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);
}
/// <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))
{
@ -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;
}
/// <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>
// destroyServerObject: Indicates whether the server object should be destroyed
// 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);
// 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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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.