using System.Collections.Generic; using UnityEngine; using System; using System.Net; using System.Net.Sockets; using System.Threading; namespace NeonTea.Quakeball.Net.Peers { public abstract class Peer { private Thread ListenThread; public byte[] Fingerprint = new byte[] { 0xCA, 0x11, 0x7F, 0xF8 }; protected UdpClient UdpClient; protected Connection MainConnection; public abstract void OnStart(string orighost, int origport); public abstract void OnStop(DisconnectReason reason); public abstract void HandlePacket(IPEndPoint endpoint, ByteReader reader); public void Start(string host, int port) { int own_port = port; if (this.GetType() == typeof(Client)) { own_port = 0; } UdpClient = new UdpClient(own_port); try { MainConnection = new Connection(new IPEndPoint(FindAddress(host), port)); } catch (Exception e) { Debug.Log($"Failed to create initial connection: {e.ToString()}"); Net.Singleton.Stop(new DisconnectReason(Reason.INITIAL_ERROR, e.ToString())); return; } StartListen(MainConnection); OnStart(host, port); } public void Stop(DisconnectReason reason) { UdpClient.Close(); if (ListenThread != null) { ListenThread.Abort(); } MainConnection.Status = ConnectionStatus.Closed; OnStop(reason); } protected IPAddress FindAddress(string host) { IPAddress addr; try { addr = Dns.GetHostAddresses(host)[0]; } catch (ArgumentException) { addr = IPAddress.Parse(host); } return addr; } protected void SendBytes(byte[] bytes, IPEndPoint endpoint) { if (bytes != null) { List ByteList = new List(); ByteList.AddRange(Fingerprint); ByteList.AddRange(bytes); byte[] sent = ByteList.ToArray(); UdpClient.Send(sent, sent.Length, endpoint); } } private bool StartListen(Connection connection) { if (ListenThread != null) { return false; } Thread t = new Thread(ListenThreadMethod); t.Start(connection); ListenThread = t; return true; } private void ListenThreadMethod(object obj) { Connection listened; try { listened = (Connection)obj; } catch (InvalidCastException) { Debug.Log($"Can not cast {obj} to a Connection"); return; } while (listened.Status != ConnectionStatus.Closed) { try { IPEndPoint Listened = new IPEndPoint(listened.Endpoint.Address, listened.Endpoint.Port); ByteReader Received = new ByteReader(UdpClient.Receive(ref Listened)); foreach (byte b in Fingerprint) { if (!(Received.hasNext() && Received.next() == b)) { goto end_of_handle; } } HandlePacket(Listened, Received); } catch (Exception e) { Net.Singleton.Stop(new DisconnectReason(Reason.LISTENING_ERROR, e.ToString())); } end_of_handle: { }; } } } public class ByteReader { private byte[] ByteList; private uint pos = 0; public ByteReader(byte[] list) { ByteList = list; } public bool hasNext() { return pos < ByteList.Length; } public byte next() { return ByteList[pos++]; } } public struct DisconnectReason { public static DisconnectReason LOCAL_MANUAL = new DisconnectReason(Reason.LOCAL_MANUAL, ""); public Reason Reason; public string Description; public DisconnectReason(Reason reason, string description) { Reason = reason; Description = description; } } public enum Reason { LOCAL_MANUAL, LISTENING_ERROR, INITIAL_ERROR, } }