extern alias Client; extern alias Server; using System; using System.IO; using System.Linq; using System.Net; using System.Threading; using FsCheck; using Server::Barotrauma; using Server::Barotrauma.Networking; using Xunit; using Xunit.Abstractions; using DeliveryMethod = Barotrauma.Networking.DeliveryMethod; using Option = Barotrauma.Option; using PeerDisconnectPacket = Client::Barotrauma.Networking.PeerDisconnectPacket; using Random = System.Random; using WriteOnlyMessage = Client::Barotrauma.Networking.WriteOnlyMessage; namespace TestProject; public class ClientServerTests : IDisposable { private readonly ITestOutputHelper testOutputHelper; private readonly GameMain? serverGame; private GameServer? currentServer; private readonly string generatedPassword; private const string ServerName = "UnitTestServer"; public ClientServerTests(ITestOutputHelper testOutputHelper) { this.testOutputHelper = testOutputHelper; if (!File.Exists(ContentPackageManager.VanillaFileList)) { return; } serverGame = new GameMain(Array.Empty()); serverGame.Init(); var random = new Random(); generatedPassword = new string(Enumerable.Range(0, 24).Select(_ => (char)random.Next(minValue: 'A', maxValue: 'z')).ToArray()); } [Fact] public void TestLidgren() { if (serverGame == null) { testOutputHelper.WriteLine("VanillaFileList not found, skipping test"); return; } var server = CreateServer(); var client = CreateClient(server); Prop.ForAll(data => { var msg = new WriteOnlyMessage(); foreach (byte b in data) { msg.WriteByte(b); } client.ClientPeer.Send(msg, DeliveryMethod.Reliable); string logBytes = ToolBox.BytesToHexString(data); if (logBytes.Length > 10) { logBytes = logBytes[..10] + "..."; } testOutputHelper.WriteLine($"Sent {data.Length} byte(s) to server: {logBytes}"); client.Update(); UpdateServer(server); // some data will make the client disconnect, // for example, if the generated data lands on a disconnect packet // so don't treat this as a failure, just reconnect if (!client.IsConnected) { client = CreateClient(server); } }).VerboseCheckThrowOnFailure(); Thread.Sleep(1000); client.ClientPeer.Close(PeerDisconnectPacket.Custom("Test complete")); } private HeadlessNetworkClient CreateClient(GameServer server) { var client = new HeadlessNetworkClient(IPAddress.Loopback, server.Port, generatedPassword, testOutputHelper); while (!client.IsConnected) { client.Update(); UpdateServer(server); Thread.Sleep(17); } return client; } private GameServer CreateServer() { if (currentServer != null) { serverGame.CloseServer(); } currentServer = new GameServer( name: ServerName, listenIp: null, port: NetConfig.DefaultPort, queryPort: NetConfig.DefaultQueryPort, maxPlayers: 1, password: generatedPassword, isPublic: false, attemptUPnP: false, ownerKey: Option.None, ownerEndpoint: Option.None); GameMain.Server = currentServer; currentServer.StartServer(registerToServerList: false); return currentServer; } private void UpdateServer(GameServer server) { server.Update((float)Timing.Step); Timing.TotalTime += Timing.Step; } ~ClientServerTests() { OnTestsFinished(); } private void OnTestsFinished() => serverGame?.CloseServer(); public void Dispose() { OnTestsFinished(); GC.SuppressFinalize(this); } }