using System.Collections.Generic; using System.Collections.Concurrent; using System; using System.Net; using System.Net.Sockets; using NeonTea.Quakeball.TeaNet.Packets; namespace NeonTea.Quakeball.TeaNet.Peers { /// Main class for networking. Remember to register a protocol before using. Remember to call Update from a gameobject! public class Peer : PeerMessageListener { /// Underlying UdpClient. Do not touch unless you know what you are doing. public UdpClient UdpClient { get; private set; } /// The fingerprint for networking. Used to make sure incoming bytes are from a correct source. public byte[] Fingerprint { get; private set; } /// Shorthand for ConnectionManager.Timeout: The amount of milliseconds before a connection is timed out. public long Timeout { get { return ConnectionManager.Timeout; } set { ConnectionManager.Timeout = value; } } /// Shorthand for ConnectionManager.Interval: The interval of updates and rate of re-sending reliable messages. public long UpdateInterval { get { return ConnectionManager.Interval; } set { ConnectionManager.Interval = value; } } /// The interval of traffic analyzed before updating. By default 5000 (5 seconds) public long TrafficDataInterval = 5000; /// The amount of bytes received in the last TrafficDataIntervel public int TrafficReceived { get; private set; } /// The amount of bytes sent in the last TrafficDataIntervel public int TrafficSent { get; private set; } /// The amount of bytes sent in the last TrafficDataIntervel by Packet public ConcurrentDictionary TrafficSentByPacket => ConnectionManager.SentByPacket; /// The amount of bytes received in the last TrafficDataIntervel by Packet public ConcurrentDictionary TrafficReceivedByPacket => ConnectionManager.ReceivedByPacket; /// Whether the Peer is currently doing anything or not.null public bool Running { get; private set; } public ListenerThread ListenerThread; public ConnectionManager ConnectionManager; public Dictionary RegisteredProtocols = new Dictionary(); /// Listener for messages and errors from within the Peer. public PeerMessageListener MessageListener; private long LastTrafficData; /// Creates a new Peer with the given fingerprint. The fingerprint can be anything, it just must be same on both peers. public Peer(byte[] fingerprint) { Fingerprint = fingerprint; ConnectionManager = new ConnectionManager(this); MessageListener = this; LastTrafficData = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } /// Starts the UdpClient, but does no networking as is. public void Start(int sending_port) { if (Running) { return; } UdpClient = new UdpClient(sending_port); MessageListener.Message("UdpClient Started"); Running = true; } /// Abruptly stops the UdpClient and all relevant threads. public void Stop() { ConnectionManager.StopThread(); if (ListenerThread != null) { ListenerThread.Stop(); } UdpClient.Dispose(); UdpClient.Close(); Running = false; } /// Start listening to a given address and port. Usually 0.0.0.0, 0 for clients and 0.0.0.0, port for servers public void StartListen(string address, int port) { IPEndPoint endpoint = new IPEndPoint(FindAddress(address), port); StartListen(endpoint); } private void StartListen(IPEndPoint endpoint) { if (ListenerThread != null) { return; // Cant listen twice } ListenerThread = new ListenerThread(this, endpoint); ListenerThread.Start(); MessageListener.Message($"Started listening to {endpoint}"); } /// Connect to a remote host. Will initialize a listener to 0.0.0.0:0 if no listener is started. public void Connect(string address, int port, byte protocolIdent, bool startListening = true) { if (startListening) { IPEndPoint listenEndpoint = (IPEndPoint)UdpClient.Client.LocalEndPoint; StartListen(listenEndpoint); } IPEndPoint endpoint = new IPEndPoint(FindAddress(address), port); ConnectionManager.StartConnection(endpoint, protocolIdent); MessageListener.Message($"Connecting to {endpoint}"); } /// Soft-disconnects the connection with the given uid, meaning it will wait until they acknowledge, before timing out.abstract public void Disconnect(ulong uid) { ConnectionManager.CloseConnection(uid, ClosingReason.Manual); } /// Send a reliable packet, meaning it will reliably be delivered. public void SendReliable(ulong uid, Packet packet) { if (Running) { ConnectionManager.AddPacketToQueue(uid, packet); ConnectionManager.SendPacketQueue(uid); } } /// Add reliable packet to queue, so that it will be sent on the next update. public void SendReliableLater(ulong uid, Packet packet) { if (Running) { ConnectionManager.AddPacketToQueue(uid, packet); } } /// Send an unreliable packet, meaning its delivery is not reliable. public void SendUnreliable(ulong uid, Packet packet) { if (Running) { ConnectionManager.SendSingleUnreliable(uid, packet); } } /// Get a Connection instance from the given uid, if such exists. Null otherwise. public Connection GetConnection(ulong uid) { return ConnectionManager.GetConnection(uid); } /// Register a given protocol. Returns protocol.Identifier if successful, 0 otherwise. public byte RegisterProtocol(Protocol protocol) { byte ident = protocol.Identifier; if (RegisteredProtocols.ContainsKey(ident)) { return 0; } RegisteredProtocols.Add(ident, protocol); ConnectionManager.ProtocolActionQueues.Add(ident, new Queue()); protocol.Peer = this; return ident; } /// Get protocol instance from the given identifier, if such exists. public Protocol GetProtocol(byte ident) { if (RegisteredProtocols.ContainsKey(ident)) { return RegisteredProtocols[ident]; } return null; } /// Shorthand for Peer.ConnectionManager.Update(): Handles network stuff that was received since last update. public void Update() { ConnectionManager.Update(); long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); if (now - LastTrafficData > TrafficDataInterval) { LastTrafficData = now; if (ListenerThread != null) { TrafficReceived = ListenerThread.ReceivedBytes; ListenerThread.ReceivedBytes = 0; } if (ConnectionManager != null) { TrafficSent = ConnectionManager.BytesSent; ConnectionManager.BytesSent = 0; foreach (Type t in ConnectionManager.ReceivedByPacket.Keys) { ConnectionManager.ReceivedByPacket[t] = 0; } foreach (Type t in ConnectionManager.SentByPacket.Keys) { ConnectionManager.ReceivedByPacket[t] = 0; } } } } public void Message(string msg) { } public void Err(string msg) { } private IPAddress FindAddress(string host) { IPAddress addr; try { addr = Dns.GetHostAddresses(host)[0]; } catch (ArgumentException) { addr = IPAddress.Parse(host); } return addr; } } /// Listener for messages and errors from the Peer. public interface PeerMessageListener { void Message(string msg); void Err(string msg); } }