예제 #1
0
        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);
            }
        }
예제 #2
0
        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);
            }
        }
예제 #3
0
        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);
                }
            }
        }
예제 #4
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);
            }
        }
        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);
            }
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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 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);
                }
            }
        }
예제 #9
0
        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);
            }
        }
예제 #12
0
        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 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>());
            }
        }
예제 #14
0
        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);
            }
        }
예제 #15
0
        public void Rpc_LateCreationOfSystem_Throws()
        {
            using (var testWorld = new NetCodeTestWorld())
            {
                testWorld.Bootstrap(true);
                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));

                Assert.Throws <InvalidOperationException>(() => { testWorld.ServerWorld.GetOrCreateSystem(typeof(NonSerializedRpcCommandRequestSystem)); });
                Assert.Throws <InvalidOperationException>(() => { testWorld.ClientWorlds[0].GetOrCreateSystem(typeof(NonSerializedRpcCommandRequestSystem)); });
            }
        }
        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 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);
            }
        }
예제 #19
0
        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 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);
            }
        }
예제 #21
0
        public void Rpc_CanSendEntityFromClientAndServer()
        {
            void SendRpc(World world, Entity entity)
            {
                var req = world.EntityManager.CreateEntity();

                world.EntityManager.AddComponentData(req, new RpcWithEntity {
                    entity = entity
                });
                world.EntityManager.AddComponentData(req, new SendRpcCommandRequestComponent {
                    TargetConnection = Entity.Null
                });
            }

            RpcWithEntity RecvRpc(World world)
            {
                var query = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly <RpcWithEntity>());

                Assert.AreEqual(1, query.CalculateEntityCount());
                var rpcReceived = query.GetSingleton <RpcWithEntity>();

                world.EntityManager.DestroyEntity(query);
                return(rpcReceived);
            }

            using (var testWorld = new NetCodeTestWorld())
            {
                testWorld.Bootstrap(true, typeof(RpcWithEntityRpcCommandRequestSystem));
                var ghostGameObject = new GameObject("SimpleGhost");
                ghostGameObject.AddComponent <TestNetCodeAuthoring>().Converter = new GhostConverter();
                testWorld.CreateGhostCollection(ghostGameObject);
                testWorld.CreateWorlds(true, 1);

                float frameTime = 1.0f / 60.0f;
                Assert.IsTrue(testWorld.Connect(frameTime, 4));
                // Go in-game
                testWorld.GoInGame();

                var serverEntity = testWorld.SpawnOnServer(ghostGameObject);
                //Wait some frame so it is spawned also on the client
                for (int i = 0; i < 8; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }

                // Retrieve the client entity
                testWorld.ClientWorlds[0].GetExistingSystem <GhostSimulationSystemGroup>().LastGhostMapWriter.Complete();
                var ghost = testWorld.ServerWorld.EntityManager.GetComponentData <GhostComponent>(serverEntity);
                Assert.IsTrue(testWorld.ClientWorlds[0].GetExistingSystem <GhostSimulationSystemGroup>().SpawnedGhostEntityMap
                              .TryGetValue(new SpawnedGhost {
                    ghostId = ghost.ghostId, spawnTick = ghost.spawnTick
                }, out var clientEntity));

                //Send the rpc to the server
                SendRpc(testWorld.ClientWorlds[0], clientEntity);
                for (int i = 0; i < 8; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }
                var rpcReceived = RecvRpc(testWorld.ServerWorld);
                Assert.IsTrue(rpcReceived.entity != Entity.Null);
                Assert.IsTrue(rpcReceived.entity == serverEntity);

                // Server send the rpc to the client
                SendRpc(testWorld.ServerWorld, serverEntity);
                for (int i = 0; i < 8; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }
                rpcReceived = RecvRpc(testWorld.ClientWorlds[0]);
                Assert.IsTrue(rpcReceived.entity != Entity.Null);
                Assert.IsTrue(rpcReceived.entity == clientEntity);

                // Client try to send a client-only entity -> result in a Entity.Null reference
                //Send the rpc to the server
                var clientOnlyEntity = testWorld.ClientWorlds[0].EntityManager.CreateEntity();
                SendRpc(testWorld.ClientWorlds[0], clientOnlyEntity);
                for (int i = 0; i < 8; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }
                rpcReceived = RecvRpc(testWorld.ServerWorld);
                Assert.IsTrue(rpcReceived.entity == Entity.Null);

                // Some Edge cases:
                // 1 - Entity has been or going to be despawned on the client. Expected: server will receive an Entity.Null in the rpc
                // 2 - Entity has been despawn on the server but the client. Server will not be able to resolve the entity correctly
                //     in that window, since the ghost mapping is reset

                //Destroy the entity on the server
                testWorld.ServerWorld.EntityManager.DestroyEntity(serverEntity);
                //Let the client try to send an rpc for it (this mimic sort of latency)
                SendRpc(testWorld.ClientWorlds[0], clientEntity);
                //Entity is destroyed on the server (so no GhostComponent). If server try to send an rpc, the entity will be translated to null
                SendRpc(testWorld.ServerWorld, serverEntity);
                for (int i = 0; i < 4; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }
                //Server should not be able to resolve the reference
                rpcReceived = RecvRpc(testWorld.ServerWorld);
                Assert.IsTrue(rpcReceived.entity == Entity.Null);
                //On the client must but null
                rpcReceived = RecvRpc(testWorld.ClientWorlds[0]);
                Assert.IsTrue(rpcReceived.entity == Entity.Null);
                //If client send the rpc now (the entity should not exists anymore and the mapping should be reset on both client and server now)
                Assert.IsFalse(testWorld.ClientWorlds[0].GetExistingSystem <GhostSimulationSystemGroup>().SpawnedGhostEntityMap
                               .TryGetValue(new SpawnedGhost {
                    ghostId = ghost.ghostId, spawnTick = ghost.spawnTick
                }, out var _));
                Assert.IsFalse(testWorld.ServerWorld.GetExistingSystem <GhostSimulationSystemGroup>().SpawnedGhostEntityMap
                               .TryGetValue(new SpawnedGhost {
                    ghostId = ghost.ghostId, spawnTick = ghost.spawnTick
                }, out var _));
                SendRpc(testWorld.ClientWorlds[0], clientEntity);
                for (int i = 0; i < 4; ++i)
                {
                    testWorld.Tick(16f / 1000f);
                }
                //The received entity must be null
                rpcReceived = RecvRpc(testWorld.ServerWorld);
                Assert.IsTrue(rpcReceived.entity == Entity.Null);
            }
        }
        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 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}");
                    }
                }
            }
        }
예제 #24
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);
            }
        }
예제 #25
0
        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);
                    }
                }
            }
        }
        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);
            }
        }
예제 #27
0
        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);
            }
        }
예제 #28
0
        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);
            }
        }