Skip to content

Commit

Permalink
fix!: fixing spawning for unauthenticated code
Browse files Browse the repository at this point in the history
OnlySpawnOnAuthenticated field removed because the previous authentication changes mean that spawnMessage can only be received when authenticated

BREAKING CHANGE: removing ServerObjectManager.OnlySpawnOnAuthenticated field
  • Loading branch information
James-Frowen committed Jan 31, 2024
1 parent 2736fe0 commit b25f2f9
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 26 deletions.
20 changes: 7 additions & 13 deletions Assets/Mirage/Runtime/INetworkVisibility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ public interface INetworkVisibility
/// </summary>
internal class AlwaysVisible : INetworkVisibility
{
private readonly ServerObjectManager _objectManager;
private readonly NetworkServer _server;

public AlwaysVisible(ServerObjectManager serverObjectManager)
public AlwaysVisible(NetworkServer server)
{
_objectManager = serverObjectManager;
_server = serverObjectManager.Server;
_server = server;
}

public bool OnCheckObserver(INetworkPlayer player) => true;
Expand All @@ -29,21 +27,17 @@ public void OnRebuildObservers(HashSet<INetworkPlayer> observers, bool initializ
// add all server connections
foreach (var player in _server.Players)
{
if (!player.SceneIsReady)
// skip players that have not authenticated
// this is needed because Message by default (including scene message) check IsAuthenticated
if (!player.IsAuthenticated)
continue;

// todo replace this with a better visibility system (where default checks auth/scene ready)
if (_objectManager.OnlySpawnOnAuthenticated && !player.IsAuthenticated)
// skip players that are loading a scene
if (!player.SceneIsReady)
continue;

observers.Add(player);
}

// add local host connection (if any)
if (_server.LocalPlayer != null && _server.LocalPlayer.SceneIsReady)
{
observers.Add(_server.LocalPlayer);
}
}
}
}
2 changes: 1 addition & 1 deletion Assets/Mirage/Runtime/NetworkServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public class NetworkServer : MonoBehaviour
public bool IsHost => LocalClient != null && LocalClient.Active;

/// <summary>
/// A list of local connections on the server.
/// A list of connections on the server.
/// </summary>
public IReadOnlyCollection<INetworkPlayer> Players => _connections.Values;

Expand Down
10 changes: 3 additions & 7 deletions Assets/Mirage/Runtime/ServerObjectManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ public class ServerObjectManager : MonoBehaviour
private NetworkServer _server;
public NetworkServer Server => _server;

[Header("Authentication")]
[Tooltip("Will only send spawn message to Players who are Authenticated. Checks the Player.IsAuthenticated property")]
public bool OnlySpawnOnAuthenticated;

public INetIdGenerator NetIdGenerator;
private uint _nextNetworkId = 1;

Expand All @@ -52,7 +48,7 @@ internal void ServerStarted(NetworkServer server)
_server = server;
_server.Stopped.AddListener(OnServerStopped);

DefaultVisibility = new AlwaysVisible(this);
DefaultVisibility = new AlwaysVisible(server);

_rpcHandler = new RpcHandler(_server.MessageHandler, _server.World, RpcInvokeType.ServerRpc);
}
Expand Down Expand Up @@ -383,8 +379,8 @@ public void Spawn(NetworkIdentity identity)

internal void SendSpawnMessage(NetworkIdentity identity, INetworkPlayer player)
{
logger.Assert(!OnlySpawnOnAuthenticated || player.IsAuthenticated || identity.Visibility != DefaultVisibility,
"SendSpawnMessage should only be called if OnlySpawnOnAuthenticated is false, player is authenticated, or there is custom visibility");
logger.Assert(player.IsAuthenticated || identity.Visibility is not AlwaysVisible,
"SendSpawnMessage should only be called if player is authenticated, or there is custom visibility");
if (logger.LogEnabled()) logger.Log($"Server SendSpawnMessage: name={identity.name} sceneId={identity.SceneId:X} netId={identity.NetId}");

// one writer for owner, one for observers
Expand Down
15 changes: 11 additions & 4 deletions Assets/Tests/Common/TestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,21 @@ public static void SetSceneId(this NetworkIdentity identity, int id, int hash =

public static class NetworkServerTestExtensions
{
public static void AddTestPlayer(this NetworkServer server, INetworkPlayer player)
public static void AddTestPlayer(this NetworkServer server, INetworkPlayer player, bool authenticated = true)
{
var info = typeof(NetworkServer).GetField("_connections", BindingFlags.Instance | BindingFlags.NonPublic);
var connections = (Dictionary<IConnection, INetworkPlayer>)info.GetValue(server);

var connectiion = Substitute.For<IConnection>();
player.Connection.Returns(connectiion);
connections.Add(connectiion, player);
var connection = Substitute.For<IConnection>();
player.Connection.Returns(connection);
if (authenticated)
{
player.IsAuthenticated.Returns(true);
var auth = new Authentication.PlayerAuthentication(null, null);
player.Authentication.Returns(auth);
}

connections.Add(connection, player);
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public override void OnRebuildObservers(HashSet<INetworkPlayer> observers, bool
private NetworkIdentity identity;
private INetworkPlayer player1;
private INetworkPlayer player2;
private INetworkPlayer player3;

protected override async UniTask ExtraSetup()
{
Expand All @@ -33,32 +34,38 @@ protected override async UniTask ExtraSetup()

player1 = Substitute.For<INetworkPlayer>();
player2 = Substitute.For<INetworkPlayer>();
player3 = Substitute.For<INetworkPlayer>();
}

[Test]
public void AlwaysVisibleAddsAllReadyPlayers()
{
player1.SceneIsReady.Returns(true);
player2.SceneIsReady.Returns(false);
player3.SceneIsReady.Returns(true);

// add some server connections
server.AddTestPlayer(player1);
server.AddTestPlayer(player2);
server.AddTestPlayer(player3, false);

// add a host connection
server.AddLocalConnection(client, Substitute.For<SocketLayer.IConnection>());
server.Connected.Invoke(server.LocalPlayer);
server.LocalPlayer.SceneIsReady = true;
server.LocalPlayer.SetAuthentication(new Mirage.Authentication.PlayerAuthentication(null, null));

// call OnStartServer so that observers dict is created
identity.StartServer();

var alwaysVisible = new AlwaysVisible(serverObjectManager);
var alwaysVisible = new AlwaysVisible(server);

// add all to observers. should have the two ready connections then.
var newObservers = new HashSet<INetworkPlayer>();
alwaysVisible.OnRebuildObservers(newObservers, true);

// player2 should be missing, because not ready
// player3 should be missing, because not authenticated
Assert.That(newObservers, Is.EquivalentTo(new[] { player1, server.LocalPlayer, serverPlayer }));
}

Expand Down

0 comments on commit b25f2f9

Please sign in to comment.