using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using Robust.Shared; using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Serialization.Manager.Attributes; namespace Robust.UnitTesting.Shared.GameState; internal sealed partial class AutoNetworkingTests : RobustIntegrationTest { /// /// Does basic testing for AutoNetworkedFieldAttribute and AutoGenerateComponentStateAttribute /// to make sure the datafields are correctly networked to the client when dirtied. /// TODO: Add test for field deltas. /// [Test] public async Task AutoNetworkingTest() { var serverOpts = new ServerIntegrationOptions { Pool = false }; var clientOpts = new ClientIntegrationOptions { Pool = false }; using var server = StartServer(serverOpts); using var client = StartClient(clientOpts); await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); var netMan = client.ResolveDependency(); Assert.DoesNotThrow(() => client.SetConnectTarget(server)); client.Post(() => netMan.ClientConnect(null!, 0, null!)); server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, true)); // Set up map. EntityUid map = default; server.Post(() => { map = server.System().CreateMap(); }); await RunTicks(); // Spawn entities var coords = new EntityCoordinates(map, default); EntityUid player = default; EntityUid cPlayer = default; EntityUid serverEnt1 = default; EntityUid serverEnt2 = default; EntityUid serverEnt3 = default; NetEntity serverNet1 = default; NetEntity serverNet2 = default; NetEntity serverNet3 = default; await server.WaitPost(() => { // Attach player. player = server.EntMan.SpawnAttachedTo(null, coords); var session = server.PlayerMan.Sessions.First(); server.PlayerMan.SetAttachedEntity(session, player); server.PlayerMan.JoinGame(session); // Spawn test entities. serverEnt1 = server.EntMan.SpawnAttachedTo(null, coords); serverEnt2 = server.EntMan.SpawnAttachedTo(null, coords); serverEnt3 = server.EntMan.SpawnAttachedTo(null, coords); serverNet1 = server.EntMan.GetNetEntity(serverEnt1); serverNet2 = server.EntMan.GetNetEntity(serverEnt2); serverNet3 = server.EntMan.GetNetEntity(serverEnt3); // Setup components server.EntMan.EnsureComponent(serverEnt1); server.EntMan.EnsureComponent(serverEnt2); server.EntMan.EnsureComponent(serverEnt3); }); await RunTicks(); // check client await client.WaitPost(() => { // Get the client-side entities cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player)); var clientEnt1 = client.EntMan.GetEntity(serverNet1); var clientEnt2 = client.EntMan.GetEntity(serverNet2); var clientEnt3 = client.EntMan.GetEntity(serverNet3); // Check player got properly attached Assert.That(client.AttachedEntity, Is.EqualTo(cPlayer)); Assert.That(client.EntMan.EntityExists(cPlayer)); // Get the client-side components Assert.That(client.EntMan.TryGetComponent(clientEnt1, out AutoNetworkingTestComponent? cmpClient1)); Assert.That(client.EntMan.TryGetComponent(clientEnt2, out AutoNetworkingTestChildComponent? cmpClient2)); Assert.That(client.EntMan.TryGetComponent(clientEnt3, out AutoNetworkingTestEmptyChildComponent? cmpClient3)); // All datafields should be the default value Assert.That(cmpClient1?.IsNetworked, Is.EqualTo(1)); Assert.That(cmpClient1?.NotNetworked, Is.EqualTo(2)); Assert.That(cmpClient2?.ChildNetworked, Is.EqualTo(1)); Assert.That(cmpClient2?.Child, Is.EqualTo(2)); Assert.That(cmpClient2?.ParentNetworked, Is.EqualTo(3)); Assert.That(cmpClient2?.Parent, Is.EqualTo(4)); Assert.That(cmpClient3?.ParentNetworked, Is.EqualTo(3)); Assert.That(cmpClient3?.Parent, Is.EqualTo(4)); }); // make changes on the server await server.WaitPost(() => { // Get the server-side components var cmpServer1 = server.EntMan.GetComponent(serverEnt1); var cmpServer2 = server.EntMan.GetComponent(serverEnt2); var cmpServer3 = server.EntMan.GetComponent(serverEnt3); // All datafields should be the default value Assert.That(cmpServer1.IsNetworked, Is.EqualTo(1)); Assert.That(cmpServer1.NotNetworked, Is.EqualTo(2)); Assert.That(cmpServer2.ChildNetworked, Is.EqualTo(1)); Assert.That(cmpServer2.Child, Is.EqualTo(2)); Assert.That(cmpServer2.ParentNetworked, Is.EqualTo(3)); Assert.That(cmpServer2.Parent, Is.EqualTo(4)); Assert.That(cmpServer3.ParentNetworked, Is.EqualTo(3)); Assert.That(cmpServer3.Parent, Is.EqualTo(4)); // change the datafields and dirty them cmpServer1.IsNetworked = 101; cmpServer1.NotNetworked = 102; cmpServer2.ChildNetworked = 101; cmpServer2.Child = 102; cmpServer2.ParentNetworked = 103; cmpServer2.Parent = 104; cmpServer3.ParentNetworked = 103; cmpServer3.Parent = 104; server.EntMan.Dirty(serverEnt1, cmpServer1); server.EntMan.Dirty(serverEnt2, cmpServer2); server.EntMan.Dirty(serverEnt3, cmpServer3); }); await RunTicks(); // check the client again await client.WaitPost(() => { // Get the client-side entities cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player)); var clientEnt1 = client.EntMan.GetEntity(serverNet1); var clientEnt2 = client.EntMan.GetEntity(serverNet2); var clientEnt3 = client.EntMan.GetEntity(serverNet3); // Get the client-side components Assert.That(client.EntMan.TryGetComponent(clientEnt1, out AutoNetworkingTestComponent? cmpClient1)); Assert.That(client.EntMan.TryGetComponent(clientEnt2, out AutoNetworkingTestChildComponent? cmpClient2)); Assert.That(client.EntMan.TryGetComponent(clientEnt3, out AutoNetworkingTestEmptyChildComponent? cmpClient3)); // All datafields should be the default value Assert.That(cmpClient1?.IsNetworked, Is.EqualTo(101)); Assert.That(cmpClient1?.NotNetworked, Is.EqualTo(2)); // unchanged Assert.That(cmpClient2?.ChildNetworked, Is.EqualTo(101)); Assert.That(cmpClient2?.Child, Is.EqualTo(2)); // unchanged Assert.That(cmpClient2?.ParentNetworked, Is.EqualTo(103)); Assert.That(cmpClient2?.Parent, Is.EqualTo(4)); // unchanged Assert.That(cmpClient3?.ParentNetworked, Is.EqualTo(103)); Assert.That(cmpClient3?.Parent, Is.EqualTo(4)); // unchanged }); async Task RunTicks() { for (int i = 0; i < 10; i++) { await server!.WaitRunTicks(1); await client!.WaitRunTicks(1); } } await client.WaitPost(() => netMan.ClientDisconnect("")); await server.WaitRunTicks(5); await client.WaitRunTicks(5); } } [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class AutoNetworkingTestComponent : Component { [DataField, AutoNetworkedField] public int IsNetworked = 1; [DataField] public int NotNetworked = 2; } [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class AutoNetworkingTestChildComponent : AutoNetworkingTestParentComponent { [DataField, AutoNetworkedField] public int ChildNetworked = 1; [DataField] public int Child = 2; } [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class AutoNetworkingTestEmptyChildComponent : AutoNetworkingTestParentComponent { } public abstract partial class AutoNetworkingTestParentComponent : Component { [DataField, AutoNetworkedField] public int ParentNetworked = 3; [DataField] public int Parent = 4; }