140 lines
4.3 KiB
C#
140 lines
4.3 KiB
C#
|
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<byte> ByteList = new List<byte>();
|
|||
|
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,
|
|||
|
}
|
|||
|
}
|