-
-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support gameobjects in syncvars (#513)
GameObjects can now be synchronized from server to client. They also support lazy loading, so if a gameobject has not been spawned yet it will still return the correct GO when it is eventually spawned
- Loading branch information
Showing
7 changed files
with
205 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using UnityEngine; | ||
|
||
namespace Mirror | ||
{ | ||
|
||
/// <summary> | ||
/// backing struct for a NetworkIdentity when used as a syncvar | ||
/// the weaver will replace the syncvar with this struct. | ||
/// </summary> | ||
public struct GameObjectSyncvar | ||
{ | ||
/// <summary> | ||
/// The network client that spawned the parent object | ||
/// used to lookup the identity if it exists | ||
/// </summary> | ||
internal NetworkClient client; | ||
internal uint netId; | ||
|
||
internal GameObject gameObject; | ||
|
||
internal uint NetId => gameObject != null ? gameObject.GetComponent<NetworkIdentity>().NetId : netId; | ||
|
||
public GameObject Value | ||
{ | ||
get | ||
{ | ||
if (gameObject != null) | ||
return gameObject; | ||
|
||
if (client != null) | ||
{ | ||
client.Spawned.TryGetValue(netId, out NetworkIdentity result); | ||
if (result != null) | ||
return result.gameObject; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
set | ||
{ | ||
if (value == null) | ||
netId = 0; | ||
gameObject = value; | ||
} | ||
} | ||
} | ||
|
||
|
||
public static class GameObjectSerializers | ||
{ | ||
public static void WriteNetworkIdentity(this NetworkWriter writer, GameObjectSyncvar id) | ||
{ | ||
writer.WritePackedUInt32(id.NetId); | ||
} | ||
|
||
public static GameObjectSyncvar ReadNetworkIdentity(this NetworkReader reader) | ||
{ | ||
uint netId = reader.ReadPackedUInt32(); | ||
|
||
NetworkIdentity identity = null; | ||
if (!(reader.Client is null)) | ||
reader.Client.Spawned.TryGetValue(netId, out identity); | ||
|
||
if (!(reader.Server is null)) | ||
reader.Server.Spawned.TryGetValue(netId, out identity); | ||
|
||
|
||
return new GameObjectSyncvar | ||
{ | ||
client = reader.Client, | ||
netId = netId, | ||
gameObject = identity != null ? identity.gameObject : null | ||
}; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
Assets/Tests/Editor/Weaver/WeaverSyncVarHookTests~/FindsHookWithGameObject.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Mirror; | ||
using UnityEngine; | ||
|
||
namespace WeaverSyncVarHookTests.FindsHookWithGameObject | ||
{ | ||
class FindsHookWithGameObject : NetworkBehaviour | ||
{ | ||
[SyncVar(hook = nameof(onTargetChanged))] | ||
GameObject target; | ||
|
||
void onTargetChanged(GameObject oldValue, GameObject newValue) | ||
{ | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System.Collections; | ||
using Cysharp.Threading.Tasks; | ||
using NUnit.Framework; | ||
using UnityEngine; | ||
using UnityEngine.TestTools; | ||
|
||
namespace Mirror.Tests | ||
{ | ||
public class SampleBehaviorWithGO : NetworkBehaviour | ||
{ | ||
[SyncVar] | ||
public GameObject target; | ||
} | ||
|
||
public class GameObjectSyncvarTest : ClientServerSetup<SampleBehaviorWithGO> | ||
{ | ||
[Test] | ||
public void IsNullByDefault() | ||
{ | ||
// out of the box, target should be null in the client | ||
|
||
Assert.That(clientComponent.target, Is.Null); | ||
} | ||
|
||
[UnityTest] | ||
public IEnumerator ChangeTarget() => UniTask.ToCoroutine(async () => | ||
{ | ||
serverComponent.target = serverPlayerGO; | ||
await UniTask.WaitUntil(() => clientComponent.target != null); | ||
Assert.That(clientComponent.target, Is.SameAs(clientPlayerGO)); | ||
}); | ||
|
||
[Test] | ||
public void UpdateAfterSpawn() | ||
{ | ||
// this situation can happen when the client does nto see an object | ||
// but the object is assigned in a syncvar. | ||
// this can easily happen during spawn if spawning in an unexpected order | ||
// or if there is AOI in play. | ||
// in this case we would have a valid net id, but we would not | ||
// find the object at spawn time | ||
|
||
var goSyncvar = new GameObjectSyncvar | ||
{ | ||
client = client, | ||
netId = serverIdentity.NetId, | ||
gameObject = null, | ||
}; | ||
|
||
Assert.That(goSyncvar.Value, Is.SameAs(clientPlayerGO)); | ||
} | ||
|
||
[UnityTest] | ||
public IEnumerator SpawnWithTarget() => UniTask.ToCoroutine(async () => | ||
{ | ||
// create an object, set the target and spawn it | ||
UnityEngine.GameObject newObject = UnityEngine.Object.Instantiate(playerPrefab); | ||
SampleBehaviorWithGO newBehavior = newObject.GetComponent<SampleBehaviorWithGO>(); | ||
newBehavior.target = serverPlayerGO; | ||
serverObjectManager.Spawn(newObject); | ||
// wait until the client spawns it | ||
uint newObjectId = newBehavior.NetId; | ||
await UniTask.WaitUntil(() => client.Spawned.ContainsKey(newObjectId)); | ||
// check if the target was set correctly in the client | ||
var newClientObject = client.Spawned[newObjectId]; | ||
var newClientBehavior = newClientObject.GetComponent<SampleBehaviorWithGO>(); | ||
Assert.That(newClientBehavior.target, Is.SameAs(clientPlayerGO)); | ||
// cleanup | ||
serverObjectManager.Destroy(newObject); | ||
}); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.