public void DynamicGhostsInSameChunkAsStaticAreSent() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(StaticOptimizationTestSystem)); StaticOptimizationTestSystem.s_ModifyNetworkId = 1; // 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>()); using (var serverEntities = serverQuery.ToEntityArray(Allocator.TempJob)) { Assert.AreEqual(16, serverEntities.Length); testWorld.ServerWorld.EntityManager.SetComponentData(serverEntities[0], new GhostOwnerComponent { NetworkId = 1 }); } // 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>()); Entity clientEnt = Entity.Null; using (var clientEntities = clientQuery.ToEntityArray(Allocator.TempJob)) { Assert.AreEqual(16, clientEntities.Length); for (int i = 0; i < clientEntities.Length; ++i) { if (clientEntityManager.GetComponentData <GhostOwnerComponent>(clientEntities[i]).NetworkId == 1) { Assert.AreEqual(Entity.Null, clientEnt); clientEnt = clientEntities[i]; } } Assert.AreNotEqual(Entity.Null, clientEnt); } // 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 that we are getting updates for the ghost clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); Assert.AreNotEqual(lastSnapshot, clientSnapshot.GetLatestTick(clientSnapshotBuffer)); } }
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 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 DisposingClientServerWorldDoesNotCauseErrors() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(false); testWorld.CreateWorlds(true, 1); testWorld.Tick(1.0f / 60.0f); testWorld.DisposeAllClientWorlds(); testWorld.Tick(1.0f / 60.0f); testWorld.DisposeServerWorld(); testWorld.Tick(1.0f / 60.0f); } }
public void ServerGhostCountIsVisibleOnClient() { 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); for (int i = 0; i < 8; ++i) { 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 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(8, ghostReceiveSystem.GhostCountOnServer); Assert.AreEqual(8, 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(16, ghostReceiveSystem.GhostCountOnServer); Assert.AreEqual(16, ghostReceiveSystem.GhostCountOnClient); } }
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 Rpc_SerializedRpcFlow_Works() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(SerializedClientRcpSendSystem), typeof(SerializedServerRpcReceiveSystem), typeof(SerializedRpcCommandRequestSystem)); testWorld.CreateWorlds(true, 1); int SendCount = 1; var SendCmd = new SerializedRpcCommand { intValue = 123456, shortValue = 32154, floatValue = 12345.67f }; SerializedClientRcpSendSystem.SendCount = SendCount; SerializedClientRcpSendSystem.Cmd = SendCmd; SerializedServerRpcReceiveSystem.ReceivedCount = 0; 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 < 33; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(SendCount, SerializedServerRpcReceiveSystem.ReceivedCount); Assert.AreEqual(SendCmd, SerializedServerRpcReceiveSystem.ReceivedCmd); } }
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); } }
public void Rpc_CanSendMoreThanOnePacketPerFrame() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(SerializedClientLargeRcpSendSystem), typeof(SerializedServerLargeRpcReceiveSystem), typeof(SerializedLargeRpcCommandRequestSystem)); testWorld.CreateWorlds(true, 1); int SendCount = 50; var SendCmd = new SerializedLargeRpcCommand { stringValue = new FixedString512("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; SerializedClientLargeRcpSendSystem.SendCount = SendCount; SerializedClientLargeRcpSendSystem.Cmd = SendCmd; SerializedServerLargeRpcReceiveSystem.ReceivedCount = 0; 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 < 33; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(SendCount, SerializedServerLargeRpcReceiveSystem.ReceivedCount); Assert.AreEqual(SendCmd, SerializedServerLargeRpcReceiveSystem.ReceivedCmd); } }
void SetupBasicTest(NetCodeTestWorld testWorld, int entitiesToSpawn = 1) { var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new StaticOptimizationTestConverter(); var ghostConfig = ghostGameObject.AddComponent <GhostAuthoringComponent>(); ghostConfig.OptimizationMode = GhostAuthoringComponent.GhostOptimizationMode.Static; Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); for (int i = 0; i < entitiesToSpawn; ++i) { 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); } }
public void Rpc_SendingBeforeGettingNetworkId_Throws() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(FlawedClientRcpSendSystem), typeof(ServerRpcReceiveSystem), typeof(NonSerializedRpcCommandRequestSystem)); testWorld.CreateWorlds(true, 1); int SendCount = 1; ServerRpcReceiveSystem.ReceivedCount = 0; FlawedClientRcpSendSystem.SendCount = SendCount; float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); LogAssert.Expect(LogType.Exception, new Regex("InvalidOperationException: Cannot send RPC with no remote connection.")); for (int i = 0; i < 33; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(0, ServerRpcReceiveSystem.ReceivedCount); } }
public void Rpc_UsingBroadcastOnClient_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)); for (int i = 0; i < 33; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(SendCount, ServerRpcReceiveSystem.ReceivedCount); } }
public void UnintializedGhostOwnerThrowsException() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new InvalidUsageConverter(); ghostGameObject.AddComponent <GhostAuthoringComponent>().DefaultGhostMode = GhostAuthoringComponent.GhostMode.OwnerPredicted; Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); 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(); LogAssert.Expect(LogType.Error, new Regex("Trying to spawn an owner predicted ghost which does not have a valid owner set. When using owner prediction you must set GhostOwnerComponent.NetworkId when spawning the ghost. If the ghost is not owned by a player you can set NetworkId to -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); } } }
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 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 GhostValuesAreSerialized() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); var ghostGameObject = new GameObject(); ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostValueSerializerConverter(); Assert.IsTrue(testWorld.CreateGhostCollection(ghostGameObject)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject); SetGhostValues(testWorld, 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); } VerifyGhostValues(testWorld); SetGhostValues(testWorld, 43); for (int i = 0; i < 64; ++i) { testWorld.Tick(frameTime); } // Assert that replicated version is correct VerifyGhostValues(testWorld); } }
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 GhostsWithSameArchetypeAreDifferent() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(GhostTypeIndex0TestGhostSpawnSystem), typeof(GhostTypeIndex0TestGhostUpdateSystem), typeof(GhostTypeIndex1TestGhostSpawnSystem), typeof(GhostTypeIndex1TestGhostUpdateSystem), typeof(GhostTypeTestGhostSendSystem), typeof(GhostTypeTestGhostReceiveSystem)); var ghostGameObject0 = new GameObject(); ghostGameObject0.AddComponent <TestNetCodeAuthoring>().Converter = new GhostTypeIndexConverter(); ghostGameObject0.name = "GhostTypeIndex0Test"; var ghostGameObject1 = new GameObject(); ghostGameObject1.AddComponent <TestNetCodeAuthoring>().Converter = new GhostTypeIndexConverter(); ghostGameObject1.name = "GhostTypeIndex1Test"; Assert.IsTrue(testWorld.CreateGhostCollection( "/../Packages/com.unity.netcode/Tests/Editor/Generated/", "GhostTypeTest", ghostGameObject0, ghostGameObject1)); testWorld.CreateWorlds(true, 1); testWorld.SpawnOnServer(ghostGameObject0); testWorld.SpawnOnServer(ghostGameObject0); testWorld.SpawnOnServer(ghostGameObject1); testWorld.SpawnOnServer(ghostGameObject1); VerifyGhostTypes(testWorld.ServerWorld); 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); } // Assert that replicated version is correct VerifyGhostTypes(testWorld.ClientWorlds[0]); } }
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 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 GhostsCanBeStaticWhenChunksAreDirty() { using (var testWorld = new NetCodeTestWorld()) { // The system will get write access to translation which will dirty the chunk, but not actually write anything testWorld.Bootstrap(true, typeof(StaticOptimizationTestSystem)); StaticOptimizationTestSystem.s_ModifyNetworkId = 1; SetupBasicTest(testWorld, 16); var clientEntityManager = testWorld.ClientWorlds[0].EntityManager; var clientQuery = clientEntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostOwnerComponent>()); using (var clientEntities = clientQuery.ToEntityArray(Allocator.TempJob)) { Assert.AreEqual(16, clientEntities.Length); var lastSnapshot = new NativeArray <uint>(clientEntities.Length, Allocator.Temp); for (int i = 0; i < clientEntities.Length; ++i) { var clientEnt = clientEntities[i]; // Store the last tick we got for this var clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); var clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); lastSnapshot[i] = clientSnapshot.GetLatestTick(clientSnapshotBuffer); } // Run a bit longer for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } // Verify that we did not get any new snapshot for (int i = 0; i < clientEntities.Length; ++i) { var clientEnt = clientEntities[i]; // Store the last tick we got for this var clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); var clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); Assert.AreEqual(lastSnapshot[i], clientSnapshot.GetLatestTick(clientSnapshotBuffer)); } } } }
public void Rpc_MalformedPackets_ThrowsAndLogError() { using (var testWorld = new NetCodeTestWorld()) { testWorld.DriverRandomSeed = 0xbadc0de; testWorld.DriverFuzzOffset = 1; testWorld.DriverFuzzFactor = new int[2]; testWorld.DriverFuzzFactor[0] = 10; testWorld.Bootstrap(true, typeof(MalformedClientRcpSendSystem), typeof(ServerMultipleRpcReceiveSystem), typeof(MultipleClientSerializedRpcCommandRequestSystem)); testWorld.CreateWorlds(true, 2); int SendCount = 15; MalformedClientRcpSendSystem.SendCount[0] = SendCount; MalformedClientRcpSendSystem.SendCount[1] = SendCount; MalformedClientRcpSendSystem.Cmds[0] = new ClientIdRpcCommand { Id = 0 }; MalformedClientRcpSendSystem.Cmds[1] = new ClientIdRpcCommand { Id = 1 }; ServerMultipleRpcReceiveSystem.ReceivedCount[0] = 0; ServerMultipleRpcReceiveSystem.ReceivedCount[1] = 0; float frameTime = 1.0f / 60.0f; // Connect and make sure the connection could be established Assert.IsTrue(testWorld.Connect(frameTime, 4)); LogAssert.Expect(LogType.Error, new Regex("RpcSystem received invalid rpc from connection 1")); for (int i = 0; i < 32; ++i) { testWorld.Tick(16f / 1000f); } Assert.Less(ServerMultipleRpcReceiveSystem.ReceivedCount[0], SendCount); Assert.True(ServerMultipleRpcReceiveSystem.ReceivedCount[1] == SendCount); } }
public void StaticGhostsAreNotSent() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); SetupBasicTest(testWorld, 16); var clientEntityManager = testWorld.ClientWorlds[0].EntityManager; var clientQuery = clientEntityManager.CreateEntityQuery(ComponentType.ReadOnly <GhostOwnerComponent>()); using (var clientEntities = clientQuery.ToEntityArray(Allocator.TempJob)) { Assert.AreEqual(16, clientEntities.Length); var lastSnapshot = new NativeArray <uint>(clientEntities.Length, Allocator.Temp); for (int i = 0; i < clientEntities.Length; ++i) { var clientEnt = clientEntities[i]; // Store the last tick we got for this var clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); var clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); lastSnapshot[i] = clientSnapshot.GetLatestTick(clientSnapshotBuffer); } // Run a bit longer for (int i = 0; i < 16; ++i) { testWorld.Tick(frameTime); } // Verify that we did not get any new snapshot for (int i = 0; i < clientEntities.Length; ++i) { var clientEnt = clientEntities[i]; // Store the last tick we got for this var clientSnapshotBuffer = clientEntityManager.GetBuffer <SnapshotDataBuffer>(clientEnt); var clientSnapshot = clientEntityManager.GetComponentData <SnapshotData>(clientEnt); Assert.AreEqual(lastSnapshot[i], clientSnapshot.GetLatestTick(clientSnapshotBuffer)); } } } }
public void ConnectSingleClient() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true, typeof(CheckConnectionSystem)); testWorld.CreateWorlds(true, 1); CheckConnectionSystem.IsConnected = 0; var ep = NetworkEndPoint.LoopbackIpv4; ep.Port = 7979; testWorld.ServerWorld.GetExistingSystem <NetworkStreamReceiveSystem>().Listen(ep); testWorld.ClientWorlds[0].GetExistingSystem <NetworkStreamReceiveSystem>().Connect(ep); for (int i = 0; i < 16 && CheckConnectionSystem.IsConnected != 3; ++i) { testWorld.Tick(16f / 1000f); } Assert.AreEqual(3, CheckConnectionSystem.IsConnected); } }
public void SameVersion_ConnectSuccessfully() { using (var testWorld = new NetCodeTestWorld()) { testWorld.Bootstrap(true); testWorld.CreateWorlds(true, 1); var serverVersion = testWorld.ServerWorld.EntityManager.CreateEntity(typeof(NetworkProtocolVersion)); testWorld.ServerWorld.EntityManager.SetComponentData(serverVersion, new NetworkProtocolVersion { NetCodeVersion = 1, GameVersion = 0, RpcCollectionVersion = 1, ComponentCollectionVersion = 1 }); var clientVersion = testWorld.ClientWorlds[0].EntityManager.CreateEntity(typeof(NetworkProtocolVersion)); testWorld.ClientWorlds[0].EntityManager.SetComponentData(clientVersion, new NetworkProtocolVersion { NetCodeVersion = 1, GameVersion = 0, RpcCollectionVersion = 1, ComponentCollectionVersion = 1 }); var ep = NetworkEndPoint.LoopbackIpv4; ep.Port = 7979; testWorld.ServerWorld.GetExistingSystem <NetworkStreamReceiveSystem>().Listen(ep); testWorld.ClientWorlds[0].GetExistingSystem <NetworkStreamReceiveSystem>().Connect(ep); for (int i = 0; i < 16; ++i) { testWorld.Tick(16f / 1000f); } var query = testWorld.ClientWorlds[0].EntityManager.CreateEntityQuery(ComponentType.ReadOnly <NetworkStreamConnection>()); Assert.AreEqual(1, query.CalculateEntityCount()); } }
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}"); } } } }
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 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])); } }