Original Behaviour was using old methods for detecting entering/exiting universe, they are now all under the same hood and the original is kept for UniverseEntranceManager because it needs to enter the universe without itself. The internal behaviour kept under a subnamespace of "Core.Internal" for the purpose that it might come in handy for other use cases.
86 lines
3.3 KiB
C#
86 lines
3.3 KiB
C#
using System.Net.Sockets;
|
|
|
|
using LiteNetLib;
|
|
using LiteNetLib.Utils;
|
|
|
|
using Engine.Core;
|
|
using Engine.Core.Debug;
|
|
|
|
namespace Engine.Systems.Network;
|
|
|
|
public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient
|
|
{
|
|
private readonly NetDataWriter netDataWriter = new();
|
|
|
|
private CancellationTokenSource? cancellationTokenSource = null;
|
|
|
|
protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibServerConnection(peer);
|
|
|
|
public INetworkCommunicatorClient Connect(string address, int port, string? password = null)
|
|
{
|
|
if (!UniverseObject.IsInUniverse)
|
|
throw new($"{nameof(LiteNetLibClient)} must be in an universe to connect");
|
|
|
|
password ??= string.Empty;
|
|
|
|
// Okay, for some reason sometimes LiteNetLib goes dumb when the server hostname has IPv6 address as well as IPv4
|
|
// but the client doesn't support IPv6, it still tries to use the v6 unless we explicitly tell the ip to connect to,
|
|
// which fails to connect... So for the time being I am preferring IPv4 below over IPv6 for clients.
|
|
// TODO: I think this is something that happens on Linux only? I need to check on Windows as well just to be sure.
|
|
System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(address);
|
|
string connectionAddress = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork, addresses[0]).ToString();
|
|
|
|
logger?.Log(this, $"Connecting to server at '{address}:{port}' with password '{password}'");
|
|
logger?.Log(this, $"Resolved address for {address}: {connectionAddress}");
|
|
|
|
Manager.Start();
|
|
Manager.Connect(connectionAddress, port, password);
|
|
|
|
return this;
|
|
}
|
|
|
|
public INetworkCommunicatorClient SendToServer<T>(T packet, PacketDelivery packetDelivery) where T : class, new()
|
|
{
|
|
netDataWriter.Reset();
|
|
netPacketProcessor.Write(netDataWriter, packet);
|
|
|
|
switch (packetDelivery)
|
|
{
|
|
case PacketDelivery.ReliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
|
case PacketDelivery.UnreliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break;
|
|
case PacketDelivery.ReliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break;
|
|
case PacketDelivery.UnreliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break;
|
|
default: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public override void EnterUniverse(IUniverse universe)
|
|
{
|
|
base.EnterUniverse(universe);
|
|
|
|
cancellationTokenSource = new CancellationTokenSource();
|
|
PollEvents(cancellationTokenSource.Token);
|
|
}
|
|
|
|
public override void ExitUniverse(IUniverse universe)
|
|
{
|
|
base.ExitUniverse(universe);
|
|
cancellationTokenSource?.Cancel();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Client needs to send everything as soon as possible so
|
|
/// the events are polled a separate thread running constantly
|
|
/// </summary>
|
|
private async void PollEvents(CancellationToken cancellationToken) => await Task.Run(() =>
|
|
{
|
|
while (true)
|
|
{
|
|
Manager.PollEvents();
|
|
Thread.Sleep(1);
|
|
}
|
|
}, cancellationToken);
|
|
}
|