Skip to content

Commit

Permalink
feat: adding support for multiple Behaviours per gameobject
Browse files Browse the repository at this point in the history
needs to be enabled in syncSettings
  • Loading branch information
James-Frowen committed Sep 25, 2022
1 parent cd3ef30 commit ce1fe4b
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "JamesFrowen.PositionSync",
"references": [
"Mirage"
"Mirage",
"Mirage.SocketLayer"
],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
150 changes: 87 additions & 63 deletions Assets/NetworkPositionSync/Runtime/SyncPositionSystem.cs

Large diffs are not rendered by default.

42 changes: 33 additions & 9 deletions Assets/NetworkPositionSync/Runtime/SyncSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

using System;
using Mirage;
using Mirage.Serialization;
using UnityEngine;

Expand All @@ -31,6 +32,10 @@ namespace JamesFrowen.PositionSync
[Serializable]
public class SyncSettings
{
[Header("Object Settings")]
[Tooltip("Required if using multiple SyncPositionBehaviour per NetworkIdentity, but increase bandwidth")]
public bool IncludeComponentIndex;

[Header("Timer Compression")]
// public float maxTime = 60 * 60 * 24;
// 0.1ms
Expand Down Expand Up @@ -67,17 +72,19 @@ public QuaternionPacker CreateRotationPacker()
public class SyncPacker
{
// packers
readonly VarFloatPacker timePacker;
readonly VarVector3Packer positionPacker;
readonly QuaternionPacker rotationPacker;
readonly int blockSize;
private readonly VarFloatPacker timePacker;
private readonly VarVector3Packer positionPacker;
private readonly QuaternionPacker rotationPacker;
private readonly int blockSize;
private readonly bool includeCompId;

public SyncPacker(SyncSettings settings)
{
timePacker = settings.CreateTimePacker();
positionPacker = settings.CreatePositionPacker();
rotationPacker = settings.CreateRotationPacker();
blockSize = settings.blockSize;
includeCompId = settings.IncludeComponentIndex;
}

public void PackTime(NetworkWriter writer, float time)
Expand All @@ -87,10 +94,17 @@ public void PackTime(NetworkWriter writer, float time)

public void PackNext(NetworkWriter writer, SyncPositionBehaviour behaviour)
{
uint id = behaviour.NetId;
TransformState state = behaviour.TransformState;
var id = behaviour.NetId;
var state = behaviour.TransformState;


VarIntBlocksPacker.Pack(writer, id, blockSize);

if (includeCompId)
{
VarIntBlocksPacker.Pack(writer, (uint)behaviour.ComponentIndex, blockSize);
}

positionPacker.Pack(writer, state.position);
rotationPacker.Pack(writer, state.rotation);
}
Expand All @@ -101,14 +115,24 @@ public float UnpackTime(NetworkReader reader)
return timePacker.Unpack(reader);
}

public void UnpackNext(NetworkReader reader, out uint id, out Vector3 pos, out Quaternion rot)
public void UnpackNext(NetworkReader reader, out NetworkBehaviour.Id id, out Vector3 pos, out Quaternion rot)
{
id = (uint)VarIntBlocksPacker.Unpack(reader, blockSize);
var netId = (uint)VarIntBlocksPacker.Unpack(reader, blockSize);
if (includeCompId)
{
var componentIndex = (int)VarIntBlocksPacker.Unpack(reader, blockSize);
id = new NetworkBehaviour.Id(netId, componentIndex);
}
else
{
id = new NetworkBehaviour.Id(netId, 0);
}

pos = positionPacker.Unpack(reader);
rot = rotationPacker.Unpack(reader);
}

internal bool TryUnpackNext(PooledNetworkReader reader, out uint id, out Vector3 pos, out Quaternion rot)
internal bool TryUnpackNext(PooledNetworkReader reader, out NetworkBehaviour.Id id, out Vector3 pos, out Quaternion rot)
{
// assume 1 state is atleast 3 bytes
// (it should be more, but there shouldn't be random left over bits in reader so 3 is enough for check)
Expand Down
32 changes: 3 additions & 29 deletions Assets/NetworkPositionSync/Tests/Mirage.Tests/AsyncUtil.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;

namespace Mirage.Tests
Expand All @@ -10,10 +7,11 @@ public class AssertionMethodAttribute : Attribute { }

public static class AsyncUtil
{
public static async UniTask<NetworkIdentity> WaitUntilSpawn(NetworkWorld world, uint netId)
[AssertionMethod]
public static async UniTask<NetworkIdentity> WaitUntilSpawn(NetworkWorld world, uint netId, double timeoutSeconds = 2)
{
NetworkIdentity identity = null;
await UniTask.WaitUntil(() => world.TryGetIdentity(netId, out identity));
await WaitUntilWithTimeout(() => world.TryGetIdentity(netId, out identity), timeoutSeconds);

return identity;
}
Expand All @@ -23,29 +21,5 @@ public static UniTask WaitUntilWithTimeout(Func<bool> predicate, double seconds
{
return UniTask.WaitUntil(predicate).Timeout(TimeSpan.FromSeconds(seconds));
}

// Unity's nunit does not support async tests
// so we do this boilerplate to run our async methods
public static IEnumerator RunAsync(Func<Task> block)
{
var task = block();

while (!task.IsCompleted) { yield return null; }
if (task.IsFaulted) { throw task.Exception; }
}

public static async Task WaitFor(Func<bool> predicate, float timeout = 2f)
{
var stopWatch = new Stopwatch();
stopWatch.Start();

while (!predicate())
{
await Task.Delay(1);

if (stopWatch.ElapsedMilliseconds > timeout * 1000)
throw new TimeoutException("Task did not complete in time");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using NUnit.Framework;
using System.Collections;
using Cysharp.Threading.Tasks;
using Mirage;
using Mirage.Tests;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

Expand All @@ -8,42 +11,11 @@ namespace JamesFrowen.PositionSync.Tests.Runtime
[Category("NetworkPositionSync")]
public class NetworkTransformSnapshotInterpolationTest : ClientServerSetup<SyncPositionBehaviour>
{
//protected override bool AutoAddPlayer => false;

//protected override void afterStartHost()
//{
// var serverGO = new GameObject("server object");
// var clientGO = new GameObject("client object");
// spawned.Add(serverGO);
// spawned.Add(clientGO);

// var serverNI = serverGO.AddComponent<NetworkIdentity>();
// var clientNI = clientGO.AddComponent<NetworkIdentity>();

// serverNT = serverGO.AddComponent<SyncPositionBehaviour>();
// clientNT = clientGO.AddComponent<SyncPositionBehaviour>();

// // set up Identitys so that server object can send message to client object in host mode
// FakeSpawnServerClientIdentity(serverNI, clientNI);

// // reset both transforms
// serverGO.transform.position = Vector3.zero;
// clientGO.transform.position = Vector3.zero;
//}

//protected override void beforeStopHost()
//{
// foreach (GameObject obj in spawned)
// {
// Object.Destroy(obj);
// }
//}

public override void ExtraSetup()
{
base.ExtraSetup();
SyncPositionSystem serverSystem = serverGo.AddComponent<SyncPositionSystem>();
SyncPositionSystem clientSystem = clientGo.AddComponent<SyncPositionSystem>();
var serverSystem = serverGo.AddComponent<SyncPositionSystem>();
var clientSystem = clientGo.AddComponent<SyncPositionSystem>();

serverSystem.Server = server;
serverSystem.Awake();
Expand All @@ -52,17 +24,18 @@ public override void ExtraSetup()
clientSystem.Awake();
}


[UnityTest]
public IEnumerator SyncPositionFromServerToClient()
{
Vector3[] positions = new Vector3[] {
var positions = new Vector3[] {
new Vector3(1, 2, 3),
new Vector3(2, 2, 3),
new Vector3(2, 3, 5),
new Vector3(2, 3, 5),
};

foreach (Vector3 position in positions)
foreach (var position in positions)
{
serverComponent.transform.position = position;
// wait more than needed to check end position is reached
Expand All @@ -72,4 +45,73 @@ public IEnumerator SyncPositionFromServerToClient()
}
}
}

[Category("NetworkPositionSync")]
public class MultipleBehavioursTest : ClientServerSetup<SyncPositionBehaviour>
{
private NetworkIdentity prefabWithMultiple;
private NetworkIdentity serverObj;
private NetworkIdentity clientObj;


public override void ExtraSetup()
{
base.ExtraSetup();
var serverSystem = serverGo.AddComponent<SyncPositionSystem>();
var clientSystem = clientGo.AddComponent<SyncPositionSystem>();
serverSystem.PackSettings.IncludeComponentIndex = true;
clientSystem.PackSettings.IncludeComponentIndex = true;

serverSystem.Server = server;
serverSystem.Awake();

clientSystem.Client = client;
clientSystem.Awake();
}

public override async UniTask LateSetup()
{
prefabWithMultiple = CreateNetworkIdentity();
prefabWithMultiple.gameObject.SetActive(false); // disable to stop awake being called
var child1 = new GameObject("Child 1");
var child2 = new GameObject("Child 2");
child1.transform.parent = prefabWithMultiple.transform;
child2.transform.parent = prefabWithMultiple.transform;

prefabWithMultiple.gameObject.AddComponent<SyncPositionBehaviour>();
child1.AddComponent<SyncPositionBehaviour>();
child2.AddComponent<SyncPositionBehaviour>();

const int PrefabHash = 1000;
clientObjectManager.RegisterPrefab(prefabWithMultiple, PrefabHash);

serverObj = GameObject.Instantiate(prefabWithMultiple);
serverObj.gameObject.SetActive(true);
serverObjectManager.Spawn(serverObj, PrefabHash);
var netId = serverObj.NetId;

clientObj = await AsyncUtil.WaitUntilSpawn(client.World, netId);
}

[UnityTest]
public IEnumerator SyncsAllPositions()
{
var rootPos = new Vector3(30, 10, 10);
var child1Pos = new Vector3(40, 10, 10);
var child2Pos = new Vector3(50, 10, 10);
var serverChild1 = serverObj.transform.Find($"Child 1");
var serverChild2 = serverObj.transform.Find($"Child 2");
serverObj.transform.localPosition = rootPos;
serverChild1.localPosition = child1Pos;
serverChild2.localPosition = child2Pos;

yield return new WaitForSeconds(0.5f);

var clientChild1 = clientObj.transform.Find($"Child 1");
var clientChild2 = clientObj.transform.Find($"Child 2");
Assert.That(clientObj.transform.localPosition, Is.EqualTo(rootPos));
Assert.That(clientChild1.localPosition, Is.EqualTo(child1Pos));
Assert.That(clientChild2.localPosition, Is.EqualTo(child2Pos));
}
}
}
2 changes: 1 addition & 1 deletion Assets/NetworkPositionSync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"url": "https://github.com/James-Frowen/NetworkPositionSync.git"
},
"dependencies": {
"com.miragenet.mirage": "128.3.1"
"com.miragenet.mirage": "128.4.0"
},
"samples": []
}
2 changes: 1 addition & 1 deletion Packages/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"dependencies": {
"com.miragenet.mirage": "128.3.1",
"com.miragenet.mirage": "128.4.0",
"com.unity.collab-proxy": "1.2.16",
"com.unity.ide.rider": "1.1.4",
"com.unity.ide.vscode": "1.2.2",
Expand Down
2 changes: 1 addition & 1 deletion Packages/packages-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"url": "https://package.openupm.com"
},
"com.miragenet.mirage": {
"version": "128.3.1",
"version": "128.4.0",
"depth": 0,
"source": "registry",
"dependencies": {
Expand Down

0 comments on commit ce1fe4b

Please sign in to comment.