quakeball/Assets/Scripts/Net/Peers/Peer.cs

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,
}
}