Merged master

This commit is contained in:
MrGadget1024 2023-01-30 07:31:42 -05:00
commit d6e81dc687
20 changed files with 144 additions and 124 deletions

View File

@ -57,12 +57,6 @@ void Update()
void UpdateServer()
{
// set dirty to trigger OnSerialize. either always, or only if changed.
// technically snapshot interpolation requires constant sending.
// however, with reliable it should be fine without constant sends.
if (!onlySyncOnChange || Changed(Construct()))
SetDirty();
// apply buffered snapshots IF client authority
// -> in server authority, server moves the object
// so no need to apply any snapshots there.
@ -91,6 +85,14 @@ void UpdateServer()
Apply(computed);
}
}
// set dirty to trigger OnSerialize. either always, or only if changed.
// technically snapshot interpolation requires constant sending.
// however, with reliable it should be fine without constant sends.
//
// detect changes _after_ all changes were applied above.
if (!onlySyncOnChange || Changed(Construct()))
SetDirty();
}
void UpdateClient()

View File

@ -1,6 +1,8 @@
// host mode related helper functions.
// usually they set up both server & client.
// it's cleaner to keep them in one place, instead of only in server / client.
using System;
namespace Mirror
{
public static class HostMode
@ -39,19 +41,8 @@ public static void InvokeOnConnected()
((LocalConnectionToServer)NetworkClient.connection).QueueConnectedEvent();
}
// calls OnStartClient for all SERVER objects in host mode once.
// client doesn't get spawn messages for those, so need to call manually.
// public because NetworkServer.ActivateHostScene was public before too.
public static void ActivateHostScene()
{
foreach (NetworkIdentity identity in NetworkServer.spawned.Values)
{
if (!identity.isClient)
{
// Debug.Log($"ActivateHostScene {identity.netId} {identity}");
NetworkClient.CheckForStartClient(identity);
}
}
}
// DEPRECATED 2023-01-28
[Obsolete("ActivateHostScene did nothing, since identities all had .isClient set in NetworkServer.SpawnObjects.")]
public static void ActivateHostScene() {}
}
}

View File

@ -598,7 +598,7 @@ public static bool SyncVarGameObjectEqual(GameObject newGameObject, uint netIdFi
uint newNetId = 0;
if (newGameObject != null)
{
if (newGameObject.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (newGameObject.TryGetComponent(out NetworkIdentity identity))
{
newNetId = identity.netId;
if (newNetId == 0)
@ -621,7 +621,7 @@ protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gam
uint newNetId = 0;
if (newGameObject != null)
{
if (newGameObject.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (newGameObject.TryGetComponent(out NetworkIdentity identity))
{
newNetId = identity.netId;
if (newNetId == 0)

View File

@ -383,7 +383,13 @@ internal static void OnTransportDisconnected()
// Raise the event before changing ConnectState
// because 'active' depends on this during shutdown
if (connection != null) OnDisconnectedEvent?.Invoke();
//
// previously OnDisconnected was only invoked if connection != null.
// however, if DNS resolve fails in Transport.Connect(),
// OnDisconnected would never be called because 'connection' is only
// created after the Transport.Connect() call.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3365
OnDisconnectedEvent?.Invoke();
connectState = ConnectState.Disconnected;
ready = false;
@ -572,7 +578,7 @@ public static void RegisterPrefab(GameObject prefab, uint newAssetId)
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -598,7 +604,7 @@ public static void RegisterPrefab(GameObject prefab)
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -634,7 +640,7 @@ public static void RegisterPrefab(GameObject prefab, SpawnDelegate spawnHandler,
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -681,7 +687,7 @@ public static void RegisterPrefab(GameObject prefab, uint newAssetId, SpawnHandl
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -747,7 +753,7 @@ public static void RegisterPrefab(GameObject prefab, SpawnHandlerDelegate spawnH
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -811,7 +817,7 @@ public static void UnregisterPrefab(GameObject prefab)
return;
}
if (!prefab.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!prefab.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Could not unregister '{prefab.name}' since it contains no NetworkIdentity component");
return;
@ -1011,12 +1017,25 @@ internal static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage me
identity.transform.localPosition = message.position;
identity.transform.localRotation = message.rotation;
identity.transform.localScale = message.scale;
// configure flags
// the below DeserializeClient call invokes SyncVarHooks.
// flags always need to be initialized before that.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3259
identity.isOwned = message.isOwner;
identity.netId = message.netId;
if (message.isLocalPlayer)
InternalAddPlayer(identity);
// configure isClient/isLocalPlayer flags.
// => after InternalAddPlayer. can't initialize .isLocalPlayer
// before InternalAddPlayer sets .localPlayer
// => before DeserializeClient, otherwise SyncVar hooks wouldn't
// have isClient/isLocalPlayer set yet.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3259
InitializeIdentityFlags(identity);
// deserialize components if any payload
// (Count is 0 if there were no components)
if (message.payload.Count > 0)
@ -1038,9 +1057,7 @@ internal static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage me
// here immediately since there won't be another OnObjectSpawnFinished.
if (isSpawnFinished)
{
identity.NotifyAuthority();
CheckForStartClient(identity);
CheckForLocalPlayer(identity);
InvokeIdentityCallbacks(identity);
}
}
@ -1097,7 +1114,7 @@ static NetworkIdentity SpawnPrefab(SpawnMessage message)
return null;
}
if (!obj.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!obj.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"Object Spawned by handler did not have a NetworkIdentity, Handler assetId '{message.assetId}'");
return null;
@ -1193,9 +1210,7 @@ internal static void OnObjectSpawnFinished(ObjectSpawnFinishedMessage _)
// they are destroyed. for safety, let's double check here.
if (identity != null)
{
identity.NotifyAuthority();
CheckForStartClient(identity);
CheckForLocalPlayer(identity);
BootstrapIdentity(identity);
}
else Debug.LogWarning("Found null entry in NetworkClient.spawned. This is unexpected. Was the NetworkIdentity not destroyed properly?");
}
@ -1238,14 +1253,12 @@ internal static void OnHostClientSpawn(SpawnMessage message)
if (message.isLocalPlayer)
InternalAddPlayer(identity);
identity.isOwned = message.isOwner;
identity.NotifyAuthority();
CheckForStartClient(identity);
// set visibility before invoking OnStartClient etc. callbacks
if (aoi != null)
aoi.SetHostVisibility(identity, true);
CheckForLocalPlayer(identity);
identity.isOwned = message.isOwner;
BootstrapIdentity(identity);
}
}
@ -1340,43 +1353,58 @@ internal static void ChangeOwner(NetworkIdentity identity, ChangeOwnerMessage me
if (identity.isLocalPlayer)
{
localPlayer = identity;
identity.connectionToServer = connection;
identity.OnStartLocalPlayer();
}
// identity's isLocalPlayer was set to false.
// clear our static localPlayer IF (and only IF) it was that one before.
else if (localPlayer == identity)
{
localPlayer = null;
// TODO set .connectionToServer to null for old local player?
// since we set it in the above 'if' case too.
}
// call OnStartLocalPlayer if it's the local player now.
CheckForLocalPlayer(identity);
}
// OnStartClient used to initialize isClient / isLocalPlayer.
// it's cleaner to do this from NetworkClient.
internal static void CheckForStartClient(NetworkIdentity identity)
// set up NetworkIdentity flags on the client.
// needs to be separate from invoking callbacks.
// cleaner, and some places need to set flags first.
static void InitializeIdentityFlags(NetworkIdentity identity)
{
// OnStartLocalPlayer is called after OnStartClient.
// but we want the flag to be set in OnStartClient already.
identity.isLocalPlayer = localPlayer == identity;
// initialize flags before invoking callbacks.
// this way isClient/isLocalPlayer is correct during callbacks.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3362
identity.isClient = true;
identity.OnStartClient();
identity.isLocalPlayer = localPlayer == identity;
// .connectionToServer is only available for local players.
// set it here, before invoking any callbacks.
// this way it's available in _all_ callbacks.
if (identity.isLocalPlayer)
identity.connectionToServer = connection;
}
internal static void CheckForLocalPlayer(NetworkIdentity identity)
// invoke NetworkIdentity callbacks on the client.
// needs to be separate from configuring flags.
// cleaner, and some places need to set flags first.
static void InvokeIdentityCallbacks(NetworkIdentity identity)
{
if (identity == localPlayer)
{
// Set isLocalPlayer to true on this NetworkIdentity and trigger
// OnStartLocalPlayer in all scripts on the same GO
identity.connectionToServer = connection;
// invoke OnStartAuthority
identity.NotifyAuthority();
// isLocalPlayer is already set by CheckForStartPlayer.
// however, let's simply move it out of OnStartLocalPlayer for now.
identity.isLocalPlayer = true;
// invoke OnStartClient
identity.OnStartClient();
// invoke OnStartLocalPlayer
if (identity.isLocalPlayer)
identity.OnStartLocalPlayer();
// Debug.Log($"NetworkClient.OnOwnerMessage player:{identity.name}");
}
}
// configure flags & invoke callbacks
static void BootstrapIdentity(NetworkIdentity identity)
{
InitializeIdentityFlags(identity);
InvokeIdentityCallbacks(identity);
}
// broadcast ///////////////////////////////////////////////////////////
@ -1473,13 +1501,10 @@ internal static void NetworkLateUpdate()
// also important for syncInterval=0 components like
// NetworkTransform, so they can sync on same interval as time
// snapshots _but_ not every single tick.
//
// Unity 2019 doesn't have Time.timeAsDouble yet
if (!Application.isPlaying ||
#if !UNITY_2020_3_OR_NEWER
// Unity 2019 doesn't have Time.timeAsDouble yet
AccurateInterval.Elapsed(NetworkTime.localTime, sendInterval, ref lastSendTime))
#else
AccurateInterval.Elapsed(Time.timeAsDouble, sendInterval, ref lastSendTime))
#endif
{
Broadcast();
}

View File

@ -523,7 +523,6 @@ void FinishStartHost()
SetupClient();
networkAddress = "localhost";
HostMode.ActivateHostScene();
RegisterClientMessages();
// call OnConencted needs to be called AFTER RegisterClientMessages

View File

@ -821,7 +821,7 @@ public static void UnregisterHandler<T>()
internal static bool GetNetworkIdentity(GameObject go, out NetworkIdentity identity)
{
if (!go.TryGetComponent<NetworkIdentity>(out identity))
if (!go.TryGetComponent(out identity))
{
Debug.LogError($"GameObject {go.name} doesn't have NetworkIdentity.");
return false;
@ -898,7 +898,7 @@ public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameOb
// on this playerControllerId for this connection, this will fail.
public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player)
{
if (!player.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!player.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogWarning($"AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}");
return false;
@ -939,7 +939,7 @@ public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameOb
// safely be used while changing scenes.
public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, bool keepAuthority = false)
{
if (!player.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!player.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"ReplacePlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}");
return false;
@ -1288,7 +1288,7 @@ public static bool SpawnObjects()
// This is the same as calling NetworkIdentity.AssignClientAuthority on the spawned object.
public static void Spawn(GameObject obj, GameObject ownerPlayer)
{
if (!ownerPlayer.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!ownerPlayer.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError("Player object has no NetworkIdentity");
return;
@ -1351,7 +1351,7 @@ static void SpawnObject(GameObject obj, NetworkConnection ownerConnection)
return;
}
if (!obj.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!obj.TryGetComponent(out NetworkIdentity identity))
{
Debug.LogError($"SpawnObject {obj} has no NetworkIdentity. Please add a NetworkIdentity to {obj}", obj);
return;
@ -1385,6 +1385,8 @@ static void SpawnObject(GameObject obj, NetworkConnection ownerConnection)
if (!identity.isServer && identity.netId == 0)
{
// configure NetworkIdentity
// this may be called in host mode, so we need to initialize
// isLocalPlayer/isClient flags too.
identity.isLocalPlayer = NetworkClient.localPlayer == identity;
identity.isClient = NetworkClient.active;
identity.isServer = true;

View File

@ -239,7 +239,7 @@ public static void WriteTransform(this NetworkWriter writer, Transform value)
writer.WriteUInt(0);
return;
}
if (value.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (value.TryGetComponent(out NetworkIdentity identity))
{
writer.WriteUInt(identity.netId);
}
@ -259,7 +259,7 @@ public static void WriteGameObject(this NetworkWriter writer, GameObject value)
}
// warn if the GameObject doesn't have a NetworkIdentity,
if (!value.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (!value.TryGetComponent(out NetworkIdentity identity))
Debug.LogWarning($"NetworkWriter {value} has no NetworkIdentity");
// serialize the correct amount of data in any case to make sure

View File

@ -1,5 +1,4 @@
// API consistent with Microsoft's ObjectPool<T>.
using System;
using System.Runtime.CompilerServices;
namespace Mirror

View File

@ -72,7 +72,7 @@ IEnumerator LoadAdditive(string sceneName)
isInTransition = true;
// This will return immediately if already faded in
// e.g. by UnloadAdditive above or by default startup state
// e.g. by UnloadAdditive or by default startup state
yield return fadeInOut.FadeIn();
// host client is on server...don't load the additive scene again
@ -91,6 +91,7 @@ IEnumerator LoadAdditive(string sceneName)
OnClientSceneChanged();
// Reveal the new scene content.
yield return fadeInOut.FadeOut();
}
@ -99,9 +100,10 @@ IEnumerator UnloadAdditive(string sceneName)
isInTransition = true;
// This will return immediately if already faded in
// e.g. by LoadAdditive above or by default startup state
// e.g. by LoadAdditive above or by default startup state.
yield return fadeInOut.FadeIn();
// host client is on server...don't unload the additive scene here.
if (mode == NetworkManagerMode.ClientOnly)
{
yield return SceneManager.UnloadSceneAsync(sceneName);
@ -115,8 +117,8 @@ IEnumerator UnloadAdditive(string sceneName)
OnClientSceneChanged();
// There is no call to FadeOut here on purpose.
// Expectation is that a LoadAdditive will follow
// that will call FadeOut after that scene loads.
// Expectation is that a LoadAdditive or full scene change
// will follow that will call FadeOut after that scene loads.
}
/// <summary>

View File

@ -46,7 +46,7 @@ void OnTriggerEnter(Collider other)
//Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} Portal::OnTriggerEnter {gameObject.name} in {gameObject.scene.name}");
// applies to host client on server and remote clients
if (other.TryGetComponent<PlayerController>(out PlayerController playerController))
if (other.TryGetComponent(out PlayerController playerController))
playerController.enabled = false;
if (isServer)
@ -56,7 +56,7 @@ void OnTriggerEnter(Collider other)
[ServerCallback]
IEnumerator SendPlayerToNewScene(GameObject player)
{
if (player.TryGetComponent<NetworkIdentity>(out NetworkIdentity identity))
if (player.TryGetComponent(out NetworkIdentity identity))
{
NetworkConnectionToClient conn = identity.connectionToClient;
if (conn == null) yield break;
@ -83,7 +83,7 @@ IEnumerator SendPlayerToNewScene(GameObject player)
NetworkServer.AddPlayerForConnection(conn, player);
// host client would have been disabled by OnTriggerEnter above
if (NetworkClient.localPlayer != null && NetworkClient.localPlayer.TryGetComponent<PlayerController>(out PlayerController playerController))
if (NetworkClient.localPlayer != null && NetworkClient.localPlayer.TryGetComponent(out PlayerController playerController))
playerController.enabled = true;
}
}

View File

@ -27,6 +27,9 @@ void Update()
// always update health bar.
// (SyncVar hook would only update on clients, not on server)
healthBar.text = new string('-', health);
// take input from focused window only
if(!Application.isFocused) return;
// movement for local player
if (isLocalPlayer)

View File

@ -21,7 +21,7 @@ public void TestIntReaderNotNull()
public void TestAccessingCustomWriterAndReader()
{
NetworkWriter writer = new NetworkWriter();
writer.Write<int>(3);
writer.Write(3);
NetworkReader reader = new NetworkReader(writer.ToArray());
int copy = reader.Read<int>();

View File

@ -839,28 +839,6 @@ public void SendCommand_RequiresAuthority()
Assert.That(comp.called, Is.EqualTo(0));
}
[Test]
public void ActivateHostSceneCallsOnStartClient()
{
// listen & connect
NetworkServer.Listen(1);
ConnectClientBlockingAuthenticatedAndReady(out _);
// spawn identity with a networkbehaviour.
// (needs to be in .spawned for ActivateHostScene)
CreateNetworkedAndSpawn(
out _, out NetworkIdentity serverIdentity, out OnStartClientTestNetworkBehaviour serverComp,
out _, out _, out _);
// ActivateHostScene calls OnStartClient for spawned objects where
// isClient is still false. set it to false first.
serverIdentity.isClient = false;
HostMode.ActivateHostScene();
// was OnStartClient called for all .spawned networkidentities?
Assert.That(serverComp.called, Is.EqualTo(1));
}
[Test]
public void SendToAll()
{

View File

@ -1,3 +1,11 @@
V1.29 [2023-01-28]
- fix: KcpServer.CreateServerSocket now handles NotSupportedException when setting DualMode
https://github.com/MirrorNetworking/Mirror/issues/3358
V1.28 [2023-01-28]
- fix: KcpClient.Connect now resolves hostname before creating peer
https://github.com/MirrorNetworking/Mirror/issues/3361
V1.27 [2023-01-08]
- KcpClient.Connect: invoke own events directly instead of going through peer,
which calls our own events anyway

View File

@ -60,6 +60,16 @@ public void Connect(string address, ushort port, KcpConfig config)
return;
}
// resolve host name before creating peer.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3361
if (!Common.ResolveHostname(address, out IPAddress[] addresses))
{
// pass error to user callback. no need to log it manually.
OnError(ErrorCode.DnsResolve, $"Failed to resolve host: {address}");
OnDisconnected();
return;
}
// create fresh peer for each new session
peer = new KcpPeer(RawSend, OnAuthenticatedWrap, OnData, OnDisconnectedWrap, OnError, config);
@ -83,15 +93,6 @@ void OnDisconnectedWrap()
Log.Info($"KcpClient: connect to {address}:{port}");
// try resolve host name
if (!Common.ResolveHostname(address, out IPAddress[] addresses))
{
// pass error to user callback. no need to log it manually.
OnError(ErrorCode.DnsResolve, $"Failed to resolve host: {address}");
OnDisconnected();
return;
}
// create socket
remoteEndPoint = new IPEndPoint(addresses[0], port);
socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

View File

@ -67,7 +67,18 @@ static Socket CreateServerSocket(bool DualMode, ushort port)
{
// IPv6 socket with DualMode @ "::" : port
Socket socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
socket.DualMode = true;
// settings DualMode may throw:
// https://learn.microsoft.com/en-us/dotnet/api/System.Net.Sockets.Socket.DualMode?view=net-7.0
// attempt it, otherwise log but continue
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3358
try
{
socket.DualMode = true;
}
catch (NotSupportedException e)
{
Log.Warning($"Failed to set Dual Mode, continuing with IPv6 without Dual Mode. Error: {e}");
}
socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
return socket;
}

View File

@ -32,9 +32,8 @@ public class #SCRIPTNAME# : InterestManagement
/// </summary>
/// <param name="identity">Object to be observed (or not) by clients</param>
/// <param name="newObservers">cached hashset to put the result into</param>
/// <param name="initialize">true if being rebuilt for the first time</param>
[ServerCallback]
public override void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnectionToClient> newObservers, bool initialize)
public override void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnectionToClient> newObservers)
{
// Default behaviour of making the identity object visible to all clients.
// Replace this code with your own logic as appropriate.

View File

@ -2,8 +2,8 @@
"dependencies": {
"com.unity.2d.sprite": "1.0.0",
"com.unity.2d.tilemap": "1.0.0",
"com.unity.ide.rider": "3.0.16",
"com.unity.ide.visualstudio": "2.0.16",
"com.unity.ide.rider": "3.0.18",
"com.unity.ide.visualstudio": "2.0.17",
"com.unity.ide.vscode": "1.2.5",
"com.unity.test-framework": "1.1.31",
"com.unity.testtools.codecoverage": "1.2.2",

View File

@ -20,7 +20,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.16",
"version": "3.0.18",
"depth": 0,
"source": "registry",
"dependencies": {
@ -29,7 +29,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.visualstudio": {
"version": "2.0.16",
"version": "2.0.17",
"depth": 0,
"source": "registry",
"dependencies": {

View File

@ -1,2 +1,2 @@
m_EditorVersion: 2021.3.15f1
m_EditorVersionWithRevision: 2021.3.15f1 (e8e88683f834)
m_EditorVersion: 2021.3.17f1
m_EditorVersionWithRevision: 2021.3.17f1 (3e8111cac19d)