void VerifyGhostValues(NetCodeTestWorld testWorld) { var serverEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ServerWorld); var clientEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, serverEntity); Assert.AreNotEqual(Entity.Null, clientEntity); var serverValues = testWorld.ServerWorld.EntityManager.GetComponentData <GhostValueSerializer>(serverEntity); var clientValues = testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity); Assert.AreEqual(serverValues.BoolValue, clientValues.BoolValue); Assert.AreEqual(serverValues.IntValue, clientValues.IntValue); Assert.AreEqual(serverValues.FloatValue, clientValues.FloatValue); Assert.AreEqual(serverValues.UnquantizedFloatValue, clientValues.UnquantizedFloatValue); Assert.AreEqual(serverValues.Float2Value, clientValues.Float2Value); Assert.AreEqual(serverValues.UnquantizedFloat2Value, clientValues.UnquantizedFloat2Value); Assert.AreEqual(serverValues.Float3Value, clientValues.Float3Value); Assert.AreEqual(serverValues.UnquantizedFloat3Value, clientValues.UnquantizedFloat3Value); Assert.AreEqual(serverValues.Float4Value, clientValues.Float4Value); Assert.AreEqual(serverValues.UnquantizedFloat4Value, clientValues.UnquantizedFloat4Value); Assert.Less(math.distance(serverValues.QuaternionValue.value, clientValues.QuaternionValue.value), 0.001f); Assert.AreEqual(serverValues.UnquantizedQuaternionValue, clientValues.UnquantizedQuaternionValue); Assert.AreEqual(serverValues.StringValue32, clientValues.StringValue32); Assert.AreEqual(serverValues.StringValue64, clientValues.StringValue64); Assert.AreEqual(serverValues.StringValue128, clientValues.StringValue128); Assert.AreEqual(serverValues.StringValue512, clientValues.StringValue512); Assert.AreEqual(serverValues.StringValue4096, clientValues.StringValue4096); Assert.AreEqual(serverEntity, serverValues.EntityValue); Assert.AreEqual(clientEntity, clientValues.EntityValue); }
public void CanRecoverFromDeletingGhostOnClient() { using (var testWorld = new NetCodeTestWorld()) { DeleteGhostOnClientSystem.s_DeleteCount = 1; testWorld.Bootstrap(true, typeof(DeleteGhostOnClientSystem)); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new InvalidUsageConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject); var serverEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ServerWorld); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostOwnerComponent { NetworkId = 42 }); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); LogAssert.Expect(LogType.Error, new Regex("Found a ghost in the ghost map which does not have an entity connected to it. This can happen if you delete ghost entities on the client.")); LogAssert.Expect(LogType.Error, new Regex("Ghost ID \\d+ has already been added to the spawned ghost map")); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime); } // Validate that the ghost was deleted on the cliet Assert.AreEqual(0, DeleteGhostOnClientSystem.s_DeleteCount); // Check that the client world has the right thing and value var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); Assert.AreEqual(42, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostOwnerComponent>(clientEnt).NetworkId); // Delete on server testWorld.ServerWorld.EntityManager.DestroyEntity(serverEnt); for (int i = 0; i < 4; ++i) { testWorld.Tick(frameTime); } clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreEqual(Entity.Null, clientEnt); } }
public void EntityMarkedAsChildIsSentAsPartOfGroup() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.name = "ParentGhost"; ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostGroupGhostConverter(); var childGhostGameObject = new GameObject(); childGhostGameObject.name = "ChildGhost"; childGhostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostGroupGhostConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject, childGhostGameObject)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject); testWorld.SpawnOnServer(childGhostGameObject); var serverEnt = testWorld.TryGetSingletonEntity <GhostGroupRoot>(testWorld.ServerWorld); var serverChildEnt = testWorld.TryGetSingletonEntity <GhostChildEntityComponent>(testWorld.ServerWorld); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostOwnerComponent { NetworkId = 42 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverChildEnt, new GhostOwnerComponent { NetworkId = 43 }); testWorld.ServerWorld.EntityManager.GetBuffer <GhostGroup>(serverEnt).Add(new GhostGroup { Value = serverChildEnt }); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime); } // Check that the client world has the right thing and value var clientEnt = testWorld.TryGetSingletonEntity <GhostGroupRoot>(testWorld.ClientWorlds[0]); var clientChildEnt = testWorld.TryGetSingletonEntity <GhostChildEntityComponent>(testWorld.ClientWorlds[0]); Assert.AreEqual(42, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostOwnerComponent>(clientEnt).NetworkId); Assert.AreNotEqual(Entity.Null, clientChildEnt); Assert.AreEqual(43, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostOwnerComponent>(clientChildEnt).NetworkId); } }
public void ChildEntityDataIsReplicated() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new MultiEntityGhostConverter(); var childGhost = new GameObject(); childGhost.transform.parent = ghostGameObject.transform; childGhost.AddComponent <TestNetCodeAuthoring>().Converter = new MultiEntityGhostConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject); var serverEnt = testWorld.TryGetSingletonEntity <TopLevelGhostEntity>(testWorld.ServerWorld); Assert.IsTrue(testWorld.ServerWorld.EntityManager.HasComponent <LinkedEntityGroup>(serverEnt)); var serverEntityGroup = testWorld.ServerWorld.EntityManager.GetBuffer <LinkedEntityGroup>(serverEnt); Assert.AreEqual(2, serverEntityGroup.Length); testWorld.ServerWorld.EntityManager.SetComponentData(serverEntityGroup[0].Value, new GhostOwnerComponent { NetworkId = 42 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEntityGroup[1].Value, new GhostOwnerComponent { NetworkId = 42 }); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime); } // Check that the client world has the right thing and value var clientEnt = testWorld.TryGetSingletonEntity <TopLevelGhostEntity>(testWorld.ClientWorlds[0]); Assert.IsTrue(testWorld.ClientWorlds[0].EntityManager.HasComponent <LinkedEntityGroup>(clientEnt)); var clientEntityGroup = testWorld.ClientWorlds[0].EntityManager.GetBuffer <LinkedEntityGroup>(clientEnt); Assert.AreEqual(2, clientEntityGroup.Length); Assert.AreEqual(42, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostOwnerComponent>(clientEntityGroup[0].Value).NetworkId); Assert.AreEqual(42, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostOwnerComponent>(clientEntityGroup[1].Value).NetworkId); } }
public void GhostCollectionGenerateSameHashOnClientAndServer() { using (var testWorld = new NetCodeTestWorld()) { var ghost1 = new GameObject(); ghost1.AddComponent <TestNetCodeAuthoring>().Converter = new TestConverter(); ghost1.AddComponent <GhostAuthoringComponent>().DefaultGhostMode = GhostAuthoringComponent.GhostMode.Predicted; var ghost2 = new GameObject(); ghost2.AddComponent <TestNetCodeAuthoring>().Converter = new TestConverter(); ghost2.AddComponent <GhostAuthoringComponent>().DefaultGhostMode = GhostAuthoringComponent.GhostMode.Interpolated; testWorld.Bootstrap(true); testWorld.CreateGhostCollection(ghost1, ghost2); testWorld.CreateWorlds(true, 1); float frameTime = 1.0f / 60.0f; var serverCollectionSystem = testWorld.ServerWorld.GetExistingSystem <GhostCollectionSystem>(); var clientCollectionSystem = testWorld.ClientWorlds[0].GetExistingSystem <GhostCollectionSystem>(); //First tick: compute on both client and server the ghost collection hash testWorld.Tick(frameTime); Assert.AreEqual(serverCollectionSystem.CalculateComponentCollectionHash(), clientCollectionSystem.CalculateComponentCollectionHash()); // compare the list of loaded prefabs var serverCollectionSingleton = testWorld.TryGetSingletonEntity <GhostCollection>(testWorld.ServerWorld); var clientCollectionSingleton = testWorld.TryGetSingletonEntity <GhostCollection>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, serverCollectionSingleton); Assert.AreNotEqual(Entity.Null, clientCollectionSingleton); var serverCollection = testWorld.ServerWorld.EntityManager.GetBuffer <GhostCollectionPrefab>(serverCollectionSingleton); var clientCollection = testWorld.ClientWorlds[0].EntityManager.GetBuffer <GhostCollectionPrefab>(clientCollectionSingleton); Assert.AreEqual(serverCollection.Length, clientCollection.Length); for (int i = 0; i < serverCollection.Length; ++i) { Assert.AreEqual(serverCollection[i].GhostType, clientCollection[i].GhostType); Assert.AreEqual(serverCollection[i].Hash, clientCollection[i].Hash); } //Check that and server can connect (same component hash) Assert.IsTrue(testWorld.Connect(frameTime, 4)); testWorld.GoInGame(); for (int i = 0; i < 10; ++i) { testWorld.Tick(frameTime); } Assert.IsTrue(testWorld.ClientWorlds[0].GetExistingSystem <NetworkStreamReceiveSystem>() .HasSingleton <NetworkIdComponent>()); } }
public void EntityReferenceSetAtSpawnIsResolved() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostValueSerializerConverter(); var referencedGameObject = new GameObject(); referencedGameObject.AddComponent <GhostOwnerComponentAuthoring>(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject, referencedGameObject)); testWorld.CreateWorlds(true, 1); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); for (int i = 0; i < 4; ++i) { testWorld.Tick(frameTime); } var serverRefEntity = testWorld.SpawnOnServer(referencedGameObject); var serverEnt = testWorld.SpawnOnServer(ghostGameObject); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostValueSerializer { EntityValue = serverRefEntity }); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 8; ++i) { testWorld.Tick(frameTime); var clientRefEntity = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); var clientEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ClientWorlds[0]); if (clientEntity != Entity.Null) { // Make sure the reference always exist if the ghost exists Assert.AreEqual(clientRefEntity, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity).EntityValue); } } // Verify that we did get the referenced entity at some point Assert.AreNotEqual(Entity.Null, testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0])); } }
public void StaticGhostsAreNotApplied() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); SetupBasicTest(testWorld); var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); var clientEntityManager = testWorld.ClientWorlds[0].EntityManager; // Write some data to a ghost field and verify that it was not touched by the ghost apply clientEntityManager.SetComponentData(clientEnt, new GhostOwnerComponent { NetworkId = 42 }); // Run a bit longer for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } Assert.AreEqual(42, clientEntityManager.GetComponentData <GhostOwnerComponent>(clientEnt).NetworkId); } }
void SetGhostValues(NetCodeTestWorld testWorld, int baseValue) { var serverEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, serverEntity); testWorld.ServerWorld.EntityManager.SetComponentData(serverEntity, new GhostValueSerializer { BoolValue = (baseValue & 1) != 0, IntValue = baseValue, UIntValue = (uint)baseValue + 1u, FloatValue = baseValue + 2, UnquantizedFloatValue = baseValue + 3, Float2Value = new float2(baseValue + 4, baseValue + 5), UnquantizedFloat2Value = new float2(baseValue + 6, baseValue + 7), Float3Value = new float3(baseValue + 8, baseValue + 9, baseValue + 10), UnquantizedFloat3Value = new float3(baseValue + 11, baseValue + 12, baseValue + 13), Float4Value = new float4(baseValue + 14, baseValue + 15, baseValue + 16, baseValue + 17), UnquantizedFloat4Value = new float4(baseValue + 18, baseValue + 19, baseValue + 20, baseValue + 21), QuaternionValue = math.normalize(new quaternion(baseValue + 22, baseValue + 23, baseValue + 24, baseValue + 25)), UnquantizedQuaternionValue = math.normalize(new quaternion(baseValue + 26, baseValue + 27, baseValue + 28, baseValue + 29)), StringValue32 = new FixedString32($"baseValue = {baseValue}"), StringValue64 = new FixedString64($"baseValue = {baseValue*2}"), StringValue128 = new FixedString128($"baseValue = {baseValue*3}"), StringValue512 = new FixedString512($"baseValue = {baseValue*4}"), StringValue4096 = new FixedString4096($"baseValue = {baseValue*5}"), EntityValue = serverEntity }); }
public void Rpc_UsingConnectionEntityOnClient_Works() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(ClientRcpSendSystem), typeof(ServerRpcReceiveSystem), typeof(NonSerializedRpcCommandRequestSystem)); testWorld.CreateWorlds(true, 1); int SendCount = 10; ClientRcpSendSystem.SendCount = SendCount; ServerRpcReceiveSystem.ReceivedCount = 0; float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); var remote = testWorld.TryGetSingletonEntity <NetworkStreamConnection>(testWorld.ClientWorlds[0]); testWorld.ClientWorlds[0].GetExistingSystem <ClientRcpSendSystem>().Remote = remote; for (int i = 0; i < 33; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(SendCount, ServerRpcReceiveSystem.ReceivedCount); } }
public void StaticGhostsAreSentWhenModified() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(StaticOptimizationTestSystem)); StaticOptimizationTestSystem.s_ModifyNetworkId = 1; SetupBasicTest(testWorld); var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); var clientEntityManager = testWorld.ClientWorlds[0].EntityManager; // Store the last tick we got for this var clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); var clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); var lastSnapshot = clientSnapshot.GetLatestTick(clientSnapshotBuffer); // Run a bit longer for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } // Verify taht we did not get any new snapshot clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); Assert.AreEqual(lastSnapshot, clientSnapshot.GetLatestTick(clientSnapshotBuffer)); // Run one tick with modification StaticOptimizationTestSystem.s_ModifyNetworkId = 0; testWorld.Tick(frameTime); StaticOptimizationTestSystem.s_ModifyNetworkId = 1; for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } // Verify taht we did not get any new snapshot clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); var newLastSnapshot = clientSnapshot.GetLatestTick(clientSnapshotBuffer); Assert.AreNotEqual(lastSnapshot, newLastSnapshot); for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } // Verify that the snapshot stayed static at the new position clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); Assert.AreEqual(newLastSnapshot, clientSnapshot.GetLatestTick(clientSnapshotBuffer)); } }
public void ExtrapolationProduceSmoothValues() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(MoveExtrapolated), typeof(CheckExtrapolate)); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostExtrapolationConverter(); ghostGameObject.AddComponent <GhostAuthoringComponent>(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); var tickRate = testWorld.ServerWorld.EntityManager.CreateEntity(); // Set low net tick rate to make sure interpolation is used testWorld.ServerWorld.EntityManager.AddComponentData(tickRate, new ClientServerTickRate { NetworkTickRate = 1 }); var clientTickRate = testWorld.ClientWorlds[0].EntityManager.CreateEntity(); // Disable interpolation time to make sure extrapolation is used var tr = NetworkTimeSystem.DefaultClientTickRate; tr.InterpolationTimeNetTicks = 0; tr.MaxExtrapolationTimeSimTicks = 120; testWorld.ClientWorlds[0].EntityManager.AddComponentData(clientTickRate, tr); testWorld.SpawnOnServer(ghostGameObject); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); // Let the simulation run for a bit since extrapolation requires two snapshots to be received before it does anything for (int i = 0; i < 256; ++i) { testWorld.Tick(frameTime); } // Enable the checks var clientEnt = testWorld.TryGetSingletonEntity <TestExtrapolated>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); testWorld.ClientWorlds[0].EntityManager.AddComponentData(clientEnt, new ExtrapolateBackup { Value = 0 }); // Let the game run for a bit more and verify that they are extrapolated for (int i = 0; i < 256; ++i) { testWorld.Tick(frameTime); } } }
public void MaxSmoothingDistanceIsUsed() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(MoveExtrapolated), typeof(CheckInterpolationDistance)); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostExtrapolationConverter(); ghostGameObject.AddComponent <GhostAuthoringComponent>(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); var tickRate = testWorld.ServerWorld.EntityManager.CreateEntity(); // Set low net tick rate to make sure interpolation is used testWorld.ServerWorld.EntityManager.AddComponentData(tickRate, new ClientServerTickRate { NetworkTickRate = 30 }); testWorld.SpawnOnServer(ghostGameObject); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); for (int i = 0; i < 8; ++i) { testWorld.Tick(frameTime); } var clientEnt = testWorld.TryGetSingletonEntity <TestExtrapolated>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); Assert.Less(testWorld.ClientWorlds[0].EntityManager.GetComponentData <TestExtrapolated>(clientEnt).Value, 100); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 256; ++i) { testWorld.Tick(frameTime); } Assert.Greater(testWorld.ClientWorlds[0].EntityManager.GetComponentData <TestExtrapolated>(clientEnt).Value, 200); } }
public void StructWithLargeNumberOfFields() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostGenBigStructConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); var serverEntity = testWorld.SpawnOnServer(ghostGameObject); //Use reflection.. just because if is faster var data = default(GhostGenBigStruct); unsafe { var values = (int *)UnsafeUtility.AddressOf(ref data); for (int i = 0; i < 100; ++i) { values[i] = i; } } testWorld.ServerWorld.EntityManager.SetComponentData(serverEntity, data); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } var clientEntity = testWorld.TryGetSingletonEntity <GhostGenBigStruct>(testWorld.ClientWorlds[0]); var clientData = testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostGenBigStruct>(clientEntity); var serverData = testWorld.ServerWorld.EntityManager.GetComponentData <GhostGenBigStruct>(serverEntity); Assert.AreEqual(serverData, clientData); } }
public void EntityReferenceUnavailableGhostIsResolved() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostValueSerializerConverter(); var referencedGameObject = new GameObject(); referencedGameObject.AddComponent <GhostOwnerComponentAuthoring>(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject, referencedGameObject)); testWorld.CreateWorlds(true, 1); var ghostSendSystem = testWorld.ServerWorld.GetExistingSystem <GhostSendSystem>(); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); ghostSendSystem.GhostRelevancyMode = GhostRelevancyMode.SetIsRelevant; // Go in-game testWorld.GoInGame(); for (int i = 0; i < 4; ++i) { testWorld.Tick(frameTime); } var con = testWorld.TryGetSingletonEntity <NetworkIdComponent>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, con); var serverConnectionId = testWorld.ServerWorld.EntityManager.GetComponentData <NetworkIdComponent>(con).Value; var serverRefEntity = testWorld.SpawnOnServer(referencedGameObject); var serverEnt = testWorld.SpawnOnServer(ghostGameObject); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostValueSerializer { EntityValue = serverRefEntity }); testWorld.Tick(frameTime); var serverGhostId = testWorld.ServerWorld.EntityManager.GetComponentData <GhostComponent>(serverEnt).ghostId; var serverRefGhostId = testWorld.ServerWorld.EntityManager.GetComponentData <GhostComponent>(serverRefEntity).ghostId; // only mark the entity with the ref as relevant so that arrived before the referenced entity exists ghostSendSystem.GhostRelevancySet.TryAdd(new RelevantGhostForConnection(serverConnectionId, serverGhostId), 1); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 8; ++i) { testWorld.Tick(frameTime); var clientRefEntity = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); var clientEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ClientWorlds[0]); if (clientEntity != Entity.Null) { // Make sure the reference always exist if the ghost exists Assert.AreEqual(clientRefEntity, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity).EntityValue); } } // Verify that we did not the referenced entity since it is irrelevant Assert.AreEqual(Entity.Null, testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0])); ghostSendSystem.GhostRelevancySet.TryAdd(new RelevantGhostForConnection(serverConnectionId, serverRefGhostId), 1); for (int i = 0; i < 8; ++i) { testWorld.Tick(frameTime); var clientRefEntity = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); var clientEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ClientWorlds[0]); if (clientEntity != Entity.Null) { // Make sure the reference always exist if the ghost exists Assert.AreEqual(clientRefEntity, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity).EntityValue); } } Assert.AreNotEqual(Entity.Null, testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0])); // Delete the referenced entity and make sure the ref is updated testWorld.ServerWorld.EntityManager.DestroyEntity(serverRefEntity); int mismatchFrames = 0; for (int i = 0; i < 8; ++i) { testWorld.Tick(frameTime); var clientRefEntity = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); var clientEntity = testWorld.TryGetSingletonEntity <GhostValueSerializer>(testWorld.ClientWorlds[0]); if (clientEntity != Entity.Null) { // The desapwn order might not be the same between client and server, if the server has despawned the entity there will be no reference, // but the client despawns at the end of the frame it was destroyed so it might still exist for one frame Assert.IsFalse(clientRefEntity == Entity.Null && testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity).EntityValue != Entity.Null); if (clientRefEntity != testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostValueSerializer>(clientEntity).EntityValue) { ++mismatchFrames; } } } Assert.LessOrEqual(mismatchFrames, 1); Assert.AreEqual(Entity.Null, testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0])); } }
public void OverrideComponentSendType_ChildEntity() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var names = new[] { "All", "Interpolated", "Predicted", "None" }; var sendTypes = new[] { GhostSendType.All, GhostSendType.Interpolated, GhostSendType.Predicted, (GhostSendType)0 }; var collection = CreatePrefabs(names); for (int i = 0; i < sendTypes.Length; ++i) { var authoring = collection[i].GetComponent <GhostAuthoringComponent>(); authoring.ComponentOverrides = new List <GhostAuthoringComponent.ComponentOverride> { new GhostAuthoringComponent.ComponentOverride { fullTypeName = typeof(GhostGen_IntStruct).FullName, gameObject = collection[i].transform.GetChild(0).gameObject, PrefabType = (int)GhostPrefabType.All, OwnerPredictedSendType = (int)sendTypes[i], SendToChild = GhostAuthoringComponent.ComponentOverride.UseDefaultValue, ComponentVariant = 0 } }; } Assert.IsTrue(testWorld.CreateGhostCollection(collection)); testWorld.CreateWorlds(true, 1); //Register serializers and setup all the system for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } //In order to get the collection setup I need to enter in game testWorld.Connect(1.0f / 60f, 16); testWorld.GoInGame(); for (int i = 0; i < collection.Length; ++i) { testWorld.SpawnOnServer(collection[i]); } for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } //Then check the expected results var collectionEntity = testWorld.TryGetSingletonEntity <GhostCollection>(testWorld.ServerWorld); var ghostCollection = testWorld.ServerWorld.EntityManager.GetBuffer <GhostCollectionComponentIndex>(collectionEntity); var ghostComponentCollection = testWorld.ServerWorld.EntityManager.GetBuffer <GhostCollectionComponentType>(collectionEntity); var type = TypeManager.GetTypeIndex(typeof(GhostGen_IntStruct)); var index = 0; while (index < ghostCollection.Length && ghostComponentCollection[ghostCollection[index].ComponentIndex].Type.TypeIndex != type) { ++index; } var serializerIndex = ghostCollection[index].SerializerIndex; CheckCollection(testWorld.ServerWorld, serializerIndex, 1); CheckCollection(testWorld.ClientWorlds[0], serializerIndex, 1); } }
public void SendToOwner_Clients_ReceiveTheCorrectData(GhostAuthoringComponent.GhostModeMask modeMask, GhostAuthoringComponent.GhostMode mode) { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(InputSystem)); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new TestComponentConverter(); var ghostConfig = ghostGameObject.AddComponent <GhostAuthoringComponent>(); ghostConfig.SupportedGhostModes = modeMask; ghostConfig.DefaultGhostMode = mode; //Some context about where owner make sense: //interpolated ghost: does even make sense that a ghost has an owner? Yes, it does and it is usually the server. // Can be a player ??? Yes it can. In that case, the player can still control the ghost via command but it will not predict the // ghost movement. Only the server will compute the correct position. The client will always see a delayed and interpolated replica. //Predicted ghost: owner make absolutely sense. //OwnerPredicted: by definition Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 2); //Here I do a trick: I will wait until the CollectionSystem is run and the component collection built. //Then I will change the serializer flags a little to make them behave the way I want. //This is a temporary hack, can be remove whe override per prefab will be available. var queryServer = testWorld.ServerWorld.EntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostCollection>()); var queryClient0 = testWorld.ClientWorlds[0].EntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostCollection>()); var queryClient1 = testWorld.ClientWorlds[1].EntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostCollection>()); while (true) { testWorld.Tick(1.0f / 60f); if (queryServer.IsEmptyIgnoreFilter || queryClient0.IsEmptyIgnoreFilter || queryClient1.IsEmptyIgnoreFilter) { continue; } if (testWorld.ServerWorld.EntityManager.GetBuffer <GhostComponentSerializer.State>(queryServer.GetSingletonEntity()).Length == 0 || testWorld.ServerWorld.EntityManager.GetBuffer <GhostComponentSerializer.State>(queryServer.GetSingletonEntity()).Length == 0 || testWorld.ServerWorld.EntityManager.GetBuffer <GhostComponentSerializer.State>(queryServer.GetSingletonEntity()).Length == 0) { continue; } ChangeSendToOwnerOption(testWorld.ServerWorld); ChangeSendToOwnerOption(testWorld.ClientWorlds[0]); ChangeSendToOwnerOption(testWorld.ClientWorlds[1]); break; } Assert.IsTrue(testWorld.Connect(1.0f / 60.0f, 64)); testWorld.GoInGame(); var net1 = testWorld.TryGetSingletonEntity <NetworkIdComponent>(testWorld.ClientWorlds[0]); var netId1 = testWorld.ClientWorlds[0].EntityManager.GetComponentData <NetworkIdComponent>(net1); var serverEnt = testWorld.SpawnOnServer(ghostGameObject); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostGen_IntStruct { IntValue = 10000 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostTypeIndex { Value = 20000 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostPredictedOnly { Value = 30000 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostInterpolatedOnly { Value = 40000 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostOwnerComponent { NetworkId = netId1.Value }); var serverBuffer1 = testWorld.ServerWorld.EntityManager.GetBuffer <GhostGenBuffer_ByteBuffer>(serverEnt); serverBuffer1.Capacity = 10; for (int i = 0; i < 10; ++i) { serverBuffer1.Add(new GhostGenBuffer_ByteBuffer { Value = (byte)(10 + i) }); } var serverBuffer2 = testWorld.ServerWorld.EntityManager.GetBuffer <GhostGenTest_Buffer>(serverEnt); serverBuffer2.Capacity = 10; for (int i = 0; i < 10; ++i) { serverBuffer2.Add(new GhostGenTest_Buffer()); } for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } serverBuffer1 = testWorld.ServerWorld.EntityManager.GetBuffer <GhostGenBuffer_ByteBuffer>(serverEnt); serverBuffer2 = testWorld.ServerWorld.EntityManager.GetBuffer <GhostGenTest_Buffer>(serverEnt); var serverComp1 = testWorld.ServerWorld.EntityManager.GetComponentData <GhostGen_IntStruct>(serverEnt); var serverComp2 = testWorld.ServerWorld.EntityManager.GetComponentData <GhostTypeIndex>(serverEnt); var predictedOnly = testWorld.ServerWorld.EntityManager.GetComponentData <GhostPredictedOnly>(serverEnt); var interpOnly = testWorld.ServerWorld.EntityManager.GetComponentData <GhostInterpolatedOnly>(serverEnt); for (int i = 0; i < 2; ++i) { var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[i]); var clientComp1_ToOwner = testWorld.ClientWorlds[i].EntityManager.GetComponentData <GhostGen_IntStruct>(clientEnt); var clientComp2_NonOwner = testWorld.ClientWorlds[i].EntityManager.GetComponentData <GhostTypeIndex>(clientEnt); var clientPredOnly = testWorld.ClientWorlds[i].EntityManager.GetComponentData <GhostPredictedOnly>(clientEnt); var clientInterpOnly = testWorld.ClientWorlds[i].EntityManager.GetComponentData <GhostInterpolatedOnly>(clientEnt); var clientBuffer1 = testWorld.ClientWorlds[i].EntityManager.GetBuffer <GhostGenBuffer_ByteBuffer>(clientEnt); var clientBuffer2 = testWorld.ClientWorlds[i].EntityManager.GetBuffer <GhostGenTest_Buffer>(clientEnt); Assert.AreEqual(i == 0, serverComp1.IntValue == clientComp1_ToOwner.IntValue, $"Client {i}"); Assert.AreEqual(i == 1, serverComp2.Value == clientComp2_NonOwner.Value, $"Client {i}"); //The component are sent to all the clients and only the SendToOwner matter if (mode == GhostAuthoringComponent.GhostMode.Predicted) { Assert.AreEqual(i == 0, predictedOnly.Value == clientPredOnly.Value, $"Client {i}"); Assert.AreEqual(false, interpOnly.Value == clientInterpOnly.Value, $"Client {i}"); } else if (mode == GhostAuthoringComponent.GhostMode.Interpolated) { Assert.AreEqual(false, predictedOnly.Value == clientPredOnly.Value, $"Client {i}"); Assert.AreEqual(i == 1, interpOnly.Value == clientInterpOnly.Value, $"Client {i}"); } else if (mode == GhostAuthoringComponent.GhostMode.OwnerPredicted) { Assert.AreEqual(i == 0, predictedOnly.Value == clientPredOnly.Value, $"Client {i}"); Assert.AreEqual(i == 1, interpOnly.Value == clientInterpOnly.Value, $"Client {i}"); } Assert.AreEqual(true, 10 == clientBuffer1.Length); Assert.AreEqual(i == 1, 10 == clientBuffer2.Length); Assert.AreEqual(i == 0, 0 == clientBuffer2.Length); for (int k = 0; k < clientBuffer1.Length; ++k) { Assert.AreEqual(serverBuffer1[k].Value, clientBuffer1[k].Value, $"Client {i}"); } for (int k = 0; k < clientBuffer2.Length; ++k) { Assert.AreEqual(serverBuffer2[k].IntValue, clientBuffer2[k].IntValue, $"Client {i}"); } } } }
void VerifyGhostValues(NetCodeTestWorld testWorld) { var serverEntity = testWorld.TryGetSingletonEntity <GhostGenTestTypeFlat>(testWorld.ServerWorld); var clientEntity = testWorld.TryGetSingletonEntity <GhostGenTestTypeFlat>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, serverEntity); Assert.AreNotEqual(Entity.Null, clientEntity); var serverValues = testWorld.ServerWorld.EntityManager.GetComponentData <GhostGenTestTypeFlat>(serverEntity); var clientValues = testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostGenTestTypeFlat>(clientEntity); Assert.AreEqual(serverValues.Int3, clientValues.Int3); Assert.AreEqual(serverValues.Composed_Int3, clientValues.Composed_Int3); Assert.AreEqual(serverValues.UInt3, clientValues.UInt3); Assert.AreEqual(serverValues.Composed_UInt3, clientValues.Composed_UInt3); Assert.AreEqual(serverValues.Partial_UInt3.x, clientValues.Partial_UInt3.x); Assert.AreEqual(serverValues.Partial_UInt3.y, clientValues.Partial_UInt3.y); Assert.AreEqual(0, clientValues.Partial_UInt3.z); Assert.AreEqual(serverValues.ComposedPartial_UInt3.x, clientValues.ComposedPartial_UInt3.x); Assert.AreEqual(serverValues.ComposedPartial_UInt3.y, clientValues.ComposedPartial_UInt3.y); Assert.AreEqual(0, clientValues.ComposedPartial_UInt3.z); Assert.AreEqual(serverValues.FloatX, clientValues.FloatX); Assert.AreEqual(serverValues.Composed_FloatX, clientValues.Composed_FloatX); Assert.AreEqual(serverValues.IntValue, clientValues.IntValue); Assert.AreEqual(serverValues.UIntValue, clientValues.UIntValue); Assert.AreEqual(serverValues.BoolValue, clientValues.BoolValue); Assert.AreEqual(serverValues.FloatValue, clientValues.FloatValue); Assert.AreEqual(serverValues.Interpolated_FloatValue, clientValues.Interpolated_FloatValue); Assert.AreEqual(serverValues.Unquantized_FloatValue, clientValues.Unquantized_FloatValue); Assert.AreEqual(serverValues.Unquantized_Interpolated_FloatValue, clientValues.Unquantized_Interpolated_FloatValue); Assert.AreEqual(serverValues.Float2Value, clientValues.Float2Value); Assert.AreEqual(serverValues.Interpolated_Float2Value, clientValues.Interpolated_Float2Value); Assert.AreEqual(serverValues.Unquantized_Float2Value, clientValues.Unquantized_Float2Value); Assert.AreEqual(serverValues.Interpolated_Unquantized_Float2Value, clientValues.Interpolated_Unquantized_Float2Value); Assert.AreEqual(serverValues.Float3Value, clientValues.Float3Value); Assert.AreEqual(serverValues.Interpolated_Float3Value, clientValues.Interpolated_Float3Value); Assert.AreEqual(serverValues.Unquantized_Float3Value, clientValues.Unquantized_Float3Value); Assert.AreEqual(serverValues.Interpolated_Unquantized_Float3Value, clientValues.Interpolated_Unquantized_Float3Value); Assert.AreEqual(serverValues.Float4Value, clientValues.Float4Value); Assert.AreEqual(serverValues.Interpolated_Float4Value, clientValues.Interpolated_Float4Value); Assert.AreEqual(serverValues.Unquantized_Float4Value, clientValues.Unquantized_Float4Value); Assert.AreEqual(serverValues.Interpolated_Unquantized_Float4Value, clientValues.Interpolated_Unquantized_Float4Value); Assert.Less(math.distance(serverValues.QuaternionValue.value, clientValues.QuaternionValue.value), 0.001f); Assert.Less(math.distance(serverValues.Interpolated_QuaternionValue.value, clientValues.Interpolated_QuaternionValue.value), 0.001f); Assert.AreEqual(serverValues.Unquantized_QuaternionValue, clientValues.Unquantized_QuaternionValue); Assert.AreEqual(serverValues.Interpolated_Unquantized_QuaternionValue, clientValues.Interpolated_Unquantized_QuaternionValue); Assert.AreEqual(serverValues.String32Value, clientValues.String32Value); Assert.AreEqual(serverValues.String64Value, clientValues.String64Value); Assert.AreEqual(serverValues.String128Value, clientValues.String128Value); Assert.AreEqual(serverValues.String512Value, clientValues.String512Value); Assert.AreEqual(serverValues.String4096Value, clientValues.String4096Value); Assert.AreEqual(serverEntity, serverValues.EntityValue); Assert.AreEqual(clientEntity, clientValues.EntityValue); }
void SetGhostValues(NetCodeTestWorld testWorld, int baseValue) { var serverEntity = testWorld.TryGetSingletonEntity <GhostGenTestTypeFlat>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, serverEntity); int i = 0; testWorld.ServerWorld.EntityManager.SetComponentData(serverEntity, new GhostGenTestTypeFlat { Int3 = new int3() { x = baseValue, y = baseValue + ++i, z = baseValue + ++i }, Composed_Int3 = new int3() { x = baseValue + ++i, y = baseValue + ++i, z = baseValue + ++i, }, UInt3 = new uint3() { x = (uint)baseValue + (uint)++i, y = (uint)baseValue + (uint)++i, z = (uint)baseValue + (uint)++i }, Composed_UInt3 = new uint3() { x = (uint)baseValue + (uint)++i, y = (uint)baseValue + (uint)++i, z = (uint)baseValue + (uint)++i }, Partial_UInt3 = new partialUint3() { x = (uint)baseValue + (uint)++i, y = (uint)baseValue + (uint)++i, z = (uint)baseValue + (uint)++i }, ComposedPartial_UInt3 = new partialUint3() { x = (uint)baseValue + (uint)++i, y = (uint)baseValue + (uint)++i, z = (uint)baseValue + (uint)++i }, FloatX = new floatX() { x = new float2(baseValue + (uint)++i, baseValue + (uint)++i), y = new float3(baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i), z = new float4(baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i), }, Composed_FloatX = new floatX() { x = new float2(baseValue + (uint)++i, baseValue + (uint)++i), y = new float3(baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i), z = new float4(baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i, baseValue + (uint)++i), }, IntValue = baseValue + ++i, UIntValue = (uint)baseValue + (uint)++i, BoolValue = (baseValue & ++ i) != 0, FloatValue = baseValue + ++i, Interpolated_FloatValue = baseValue + ++i, Unquantized_FloatValue = baseValue + ++i, Unquantized_Interpolated_FloatValue = baseValue + ++i, Float2Value = new float2(baseValue + ++i, baseValue + ++i), Interpolated_Float2Value = new float2(baseValue + ++i, baseValue + ++i), Unquantized_Float2Value = new float2(baseValue + ++i, baseValue + ++i), Interpolated_Unquantized_Float2Value = new float2(baseValue + ++i, baseValue + ++i), Float3Value = new float3(baseValue + ++i, baseValue + ++i, baseValue + ++i), Interpolated_Float3Value = new float3(baseValue + ++i, baseValue + ++i, baseValue + ++i), Unquantized_Float3Value = new float3(baseValue + ++i, baseValue + ++i, baseValue + ++i), Interpolated_Unquantized_Float3Value = new float3(baseValue + ++i, baseValue + ++i, baseValue + ++i), Float4Value = new float4(baseValue + ++i, baseValue + ++i, baseValue + ++i, baseValue + ++i), Interpolated_Float4Value = new float4(baseValue + ++i, baseValue + ++i, baseValue + ++i, baseValue + ++i), Unquantized_Float4Value = new float4(baseValue + ++i, baseValue + ++i, baseValue + ++i, baseValue + ++i), Interpolated_Unquantized_Float4Value = new float4(baseValue + ++i, baseValue + ++i, baseValue + ++i, baseValue + ++i), QuaternionValue = math.normalize(new quaternion(0.4f, 0.4f, 0.4f, 0.6f)), Interpolated_QuaternionValue = math.normalize(new quaternion(0.5f, 0.5f, 0.5f, 0.5f)), Unquantized_QuaternionValue = math.normalize(new quaternion(0.6f, 0.6f, 0.6f, 0.6f)), Interpolated_Unquantized_QuaternionValue = math.normalize(new quaternion(0.5f, 0.5f, 0.5f, 0.5f)), String32Value = new FixedString32($"baseValue = {baseValue + ++i}"), String64Value = new FixedString64($"baseValue = {baseValue + ++i}"), String128Value = new FixedString128($"baseValue = {baseValue + ++i}"), String512Value = new FixedString512($"baseValue = {baseValue + ++i}"), String4096Value = new FixedString4096($"baseValue = {baseValue + ++i}"), EntityValue = serverEntity, }); Debug.Log($"i is {i}"); }
public void RelevancyChangesSendsStaticGhosts() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); // Spawn 16 ghosts SetupBasicTest(testWorld, 16); // Set the ghost id for one of them to 1 so it is modified var serverQuery = testWorld.ServerWorld.EntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostOwnerComponent>()); int ghostId; using (var serverEntities = serverQuery.ToEntityArray(Allocator.TempJob)) { Assert.AreEqual(16, serverEntities.Length); ghostId = testWorld.ServerWorld.EntityManager.GetComponentData <GhostComponent>(serverEntities[0]).ghostId; } var con = testWorld.TryGetSingletonEntity <NetworkIdComponent>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, con); var connectionId = testWorld.ServerWorld.EntityManager.GetComponentData <NetworkIdComponent>(con).Value; // Get the changes across to the client for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } var clientEntityManager = testWorld.ClientWorlds[0].EntityManager; var clientQuery = clientEntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostOwnerComponent>()); var clientEntities = clientQuery.ToComponentDataArray <GhostOwnerComponent>(Allocator.Temp); Assert.AreEqual(16, clientEntities.Length); // Make one of the ghosts irrelevant var ghostSendSystem = testWorld.ServerWorld.GetExistingSystem <GhostSendSystem>(); ghostSendSystem.GhostRelevancyMode = GhostRelevancyMode.SetIsIrrelevant; var key = new RelevantGhostForConnection { Connection = connectionId, Ghost = ghostId }; ghostSendSystem.GhostRelevancySet.TryAdd(key, 1); // Get the changes across to the client for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } clientEntities = clientQuery.ToComponentDataArray <GhostOwnerComponent>(Allocator.Temp); Assert.AreEqual(15, clientEntities.Length); // Allow it to spawn again ghostSendSystem.GhostRelevancySet.Remove(key); // Get the changes across to the client for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } clientEntities = clientQuery.ToComponentDataArray <GhostOwnerComponent>(Allocator.Temp); Assert.AreEqual(16, clientEntities.Length); } }
public void SerializationVariant_AreAppliedToBothRootAndChildEntities() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject("Root"); var childGhost = new GameObject("Child"); childGhost.transform.parent = ghostGameObject.transform; var authoring = ghostGameObject.AddComponent <GhostAuthoringComponent>(); authoring.DefaultGhostMode = GhostAuthoringComponent.GhostMode.Interpolated; authoring.SupportedGhostModes = GhostAuthoringComponent.GhostModeMask.All; //Setup a variant for both root and child entity and check that the runtime serializer use this one to serialize data var attrType = typeof(TranslationVariantTest).GetCustomAttribute <GhostComponentVariationAttribute>(); var hash = GhostComponentVariationAttribute.ComputeVariantHash(typeof(TranslationVariantTest), attrType); authoring.ComponentOverrides = new List <GhostAuthoringComponent.ComponentOverride> { new GhostAuthoringComponent.ComponentOverride { fullTypeName = typeof(Transforms.Translation).FullName, gameObject = ghostGameObject, PrefabType = (int)GhostPrefabType.All, OwnerPredictedSendType = (int)GhostSendType.All, ComponentVariant = hash }, new GhostAuthoringComponent.ComponentOverride { fullTypeName = typeof(Transforms.Translation).FullName, gameObject = childGhost, PrefabType = (int)GhostPrefabType.All, OwnerPredictedSendType = (int)GhostSendType.All, ComponentVariant = hash } }; Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject), "Cannot create ghost collection"); testWorld.CreateWorlds(true, 1); //Register serializers and setup all the system for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } //In order to get the collection setup I need to enter in game testWorld.Connect(1.0f / 60f, 16); testWorld.GoInGame(); testWorld.SpawnOnServer(ghostGameObject); for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } var typeIndex = TypeManager.GetTypeIndex <Transforms.Translation>(); //Then check the expected results var collection = testWorld.TryGetSingletonEntity <GhostCollection>(testWorld.ServerWorld); var ghostSerializerCollection = testWorld.ServerWorld.EntityManager.GetBuffer <GhostComponentSerializer.State>(collection); //Check that the variant has been registered bool variantIsPresent = false; foreach (var t in ghostSerializerCollection) { variantIsPresent |= t.VariantHash == hash; } Assert.IsTrue(variantIsPresent); var componentIndex = testWorld.ServerWorld.EntityManager.GetBuffer <GhostCollectionComponentIndex>(collection); var ghostPrefabCollection = testWorld.ServerWorld.EntityManager.GetBuffer <GhostCollectionPrefabSerializer>(collection); //And verify that the component associated with the ghost for the transform point to this index for (int i = 0; i < ghostPrefabCollection[0].NumComponents; ++i) { var idx = componentIndex[ghostPrefabCollection[0].FirstComponent + i]; if (ghostSerializerCollection[idx.SerializerIndex].ComponentType.TypeIndex == typeIndex) { Assert.IsTrue(ghostSerializerCollection[idx.SerializerIndex].VariantHash == hash); } } } }
public void OverrideComponentPrefabType_RootEntity() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var names = new[] { "ServerOnly", "ClientOnly", "PredictedOnly", "InterpolatedOnly" }; var prefabTypes = new[] { GhostPrefabType.Server, GhostPrefabType.Client, GhostPrefabType.InterpolatedClient, GhostPrefabType.PredictedClient }; var collection = CreatePrefabs(names); //overrides the component prefab types in different prefabs for (int i = 0; i < prefabTypes.Length; ++i) { var authoring = collection[i].GetComponent <GhostAuthoringComponent>(); authoring.ComponentOverrides = new List <GhostAuthoringComponent.ComponentOverride> { new GhostAuthoringComponent.ComponentOverride { fullTypeName = typeof(GhostGen_IntStruct).FullName, gameObject = collection[i], PrefabType = (int)prefabTypes[i], OwnerPredictedSendType = (int)GhostSendType.All, ComponentVariant = 0 } }; } Assert.IsTrue(testWorld.CreateGhostCollection(collection)); testWorld.CreateWorlds(true, 1); //Register serializers and setup all the system for (int i = 0; i < 16; ++i) { testWorld.Tick(1.0f / 60.0f); } //Then check the expected results var ghostCollection = testWorld.TryGetSingletonEntity <GhostPrefabCollectionComponent>(testWorld.ServerWorld); var prefabList = testWorld.ServerWorld.EntityManager.GetBuffer <GhostPrefabBuffer>(ghostCollection).ToNativeArray(Allocator.Temp); Assert.AreEqual(4, prefabList.Length); for (int i = 0; i < prefabList.Length; ++i) { if ((prefabTypes[i] & GhostPrefabType.Server) != 0) { Assert.IsTrue(testWorld.ServerWorld.EntityManager.HasComponent <GhostGen_IntStruct>(prefabList[i].Value)); } else { Assert.IsFalse(testWorld.ServerWorld.EntityManager.HasComponent <GhostGen_IntStruct>(prefabList[i].Value)); } var linkedGroupBuffer = testWorld.ServerWorld.EntityManager.GetBuffer <LinkedEntityGroup>(prefabList[i].Value); Assert.IsTrue(testWorld.ServerWorld.EntityManager.HasComponent <GhostGen_IntStruct>(linkedGroupBuffer[1].Value)); } ghostCollection = testWorld.TryGetSingletonEntity <GhostPrefabCollectionComponent>(testWorld.ClientWorlds[0]); prefabList = testWorld.ClientWorlds[0].EntityManager.GetBuffer <GhostPrefabBuffer>(ghostCollection).ToNativeArray(Allocator.Temp); Assert.AreEqual(4, prefabList.Length); for (int i = 0; i < prefabList.Length; ++i) { if ((prefabTypes[i] & GhostPrefabType.Client) != 0) { Assert.IsTrue(testWorld.ClientWorlds[0].EntityManager.HasComponent <GhostGen_IntStruct>(prefabList[i].Value)); } else { Assert.IsFalse(testWorld.ClientWorlds[0].EntityManager.HasComponent <GhostGen_IntStruct>(prefabList[i].Value)); } var linkedGroupBuffer = testWorld.ClientWorlds[0].EntityManager.GetBuffer <LinkedEntityGroup>(prefabList[i].Value); Assert.IsTrue(testWorld.ClientWorlds[0].EntityManager.HasComponent <GhostGen_IntStruct>(linkedGroupBuffer[1].Value)); } } }
private void TestHelper(bool predicted, bool ownerPrediction) { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostPredictedOnlyConverter(); var ghostConfig = ghostGameObject.AddComponent <GhostAuthoringComponent>(); if (ownerPrediction) { ghostConfig.DefaultGhostMode = GhostAuthoringComponent.GhostMode.OwnerPredicted; } else { ghostConfig.SupportedGhostModes = predicted ? GhostAuthoringComponent.GhostModeMask.Predicted : GhostAuthoringComponent.GhostModeMask.Interpolated; } Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject); var serverEnt = testWorld.TryGetSingletonEntity <GhostPredictedOnly>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, serverEnt); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostPredictedOnly { Value = 1 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostInterpolatedOnly { Value = 1 }); testWorld.ServerWorld.EntityManager.SetComponentData(serverEnt, new GhostOwnerComponent { NetworkId = predicted ? 1 : 2 }); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Check the clients network id var serverCon = testWorld.TryGetSingletonEntity <NetworkIdComponent>(testWorld.ServerWorld); Assert.AreNotEqual(Entity.Null, serverCon); Assert.AreEqual(1, testWorld.ServerWorld.EntityManager.GetComponentData <NetworkIdComponent>(serverCon).Value); // Go in-game testWorld.GoInGame(); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime); } // Check that the client world has the right thing and value var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); Assert.AreEqual(predicted, testWorld.ClientWorlds[0].EntityManager.HasComponent <PredictedGhostComponent>(clientEnt)); Assert.AreEqual(predicted ? 0 : 1, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostInterpolatedOnly>(clientEnt).Value); Assert.AreEqual(predicted ? 1 : 0, testWorld.ClientWorlds[0].EntityManager.GetComponentData <GhostPredictedOnly>(clientEnt).Value); } }
public void ServerGhostCountOnlyIncludesRelevantSet() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new LateJoinCompletionConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); for (int i = 0; i < 8; ++i) { testWorld.SpawnOnServer(ghostGameObject); } // Go in-game testWorld.GoInGame(); testWorld.Tick(frameTime); // Setup relevancy var ghostSendSystem = testWorld.ServerWorld.GetExistingSystem <GhostSendSystem>(); ghostSendSystem.GhostRelevancyMode = GhostRelevancyMode.SetIsRelevant; var serverConnectionEnt = testWorld.TryGetSingletonEntity <NetworkIdComponent>(testWorld.ServerWorld); var serverConnectionId = testWorld.ServerWorld.EntityManager.GetComponentData <NetworkIdComponent>(serverConnectionEnt).Value; var query = testWorld.ServerWorld.EntityManager.CreateEntityQuery(ComponentType.ReadWrite <GhostComponent>()); var ghosts = query.ToComponentDataArray <GhostComponent>(Allocator.Temp); Assert.AreEqual(ghosts.Length, 8); for (int i = 0; i < 6; ++i) { ghostSendSystem.GhostRelevancySet.TryAdd(new RelevantGhostForConnection { Ghost = ghosts[i].ghostId, Connection = serverConnectionId }, 1); } // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 4; ++i) { testWorld.Tick(frameTime); } var ghostReceiveSystem = testWorld.ClientWorlds[0].GetExistingSystem <GhostReceiveSystem>(); // Validate that the ghost was deleted on the cliet Assert.AreEqual(6, ghostReceiveSystem.GhostCountOnServer); Assert.AreEqual(6, ghostReceiveSystem.GhostCountOnClient); // Spawn a few more and verify taht the count is updated for (int i = 0; i < 8; ++i) { testWorld.SpawnOnServer(ghostGameObject); } for (int i = 0; i < 4; ++i) { testWorld.Tick(frameTime); } Assert.AreEqual(6, ghostReceiveSystem.GhostCountOnServer); Assert.AreEqual(6, ghostReceiveSystem.GhostCountOnClient); } }
public void PartialPredictionTicksAreRolledBack() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(PredictionTestPredictionSystem)); PredictionTestPredictionSystem.s_IsEnabled = true; var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new PredictionTestConverter(); var ghostConfig = ghostGameObject.AddComponent <GhostAuthoringComponent>(); ghostConfig.DefaultGhostMode = GhostAuthoringComponent.GhostMode.Predicted; Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); var serverEnt = testWorld.SpawnOnServer(ghostGameObject); Assert.AreNotEqual(Entity.Null, serverEnt); // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); // Go in-game testWorld.GoInGame(); // Let the game run for a bit so the ghosts are spawned on the client for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } var clientEnt = testWorld.TryGetSingletonEntity <GhostOwnerComponent>(testWorld.ClientWorlds[0]); Assert.AreNotEqual(Entity.Null, clientEnt); var prevServer = testWorld.ServerWorld.EntityManager.GetComponentData <Translation>(serverEnt).Value; var prevClient = testWorld.ClientWorlds[0].EntityManager.GetComponentData <Translation>(clientEnt).Value; for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime / 4); var curServer = testWorld.ServerWorld.EntityManager.GetComponentData <Translation>(serverEnt).Value; var curClient = testWorld.ClientWorlds[0].EntityManager.GetComponentData <Translation>(clientEnt).Value; // Server does not do fractional ticks so it will not advance the position every frame Assert.GreaterOrEqual(curServer.x, prevServer.x); // Client does fractional ticks and position should be always increasing Assert.Greater(curClient.x, prevClient.x); prevServer = curServer; prevClient = curClient; } // Stop updating, let it run for a while and check that they ended on the same value PredictionTestPredictionSystem.s_IsEnabled = false; for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } prevServer = testWorld.ServerWorld.EntityManager.GetComponentData <Translation>(serverEnt).Value; prevClient = testWorld.ClientWorlds[0].EntityManager.GetComponentData <Translation>(clientEnt).Value; Assert.IsTrue(math.distance(prevServer, prevClient) < 0.01); } }