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

197 lines
8.4 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System;
using NeonTea.Quakeball.Net.Packets;
namespace NeonTea.Quakeball.Net.Peers {
public class ConnectionManager {
private Dictionary<IPEndPoint, Connection> Connections = new Dictionary<IPEndPoint, Connection>();
private Dictionary<Connection, List<Packet>> PacketQueue = new Dictionary<Connection, List<Packet>>();
private Peer Peer;
private Thread UpdateThread;
public long Timeout = 8000;
public long Interval = 100;
public ConnectionManager(Peer peer) {
Peer = peer;
UpdateThread = new Thread(new ThreadStart(UpdateThreadMethod));
UpdateThread.Start();
}
public void StopThread() {
UpdateThread.Abort();
}
public Connection Find(IPEndPoint endpoint) {
if (Connections.ContainsKey(endpoint)) {
return Connections[endpoint];
}
Connection conn = new Connection(endpoint, ConnectionStatus.Awaiting);
Connections.Add(endpoint, conn);
PacketQueue.Add(conn, new List<Packet>());
return conn;
}
public bool StartConnection(IPEndPoint endpoint, byte protocolIdent) {
if (Connections.ContainsKey(endpoint)) {
return false;
}
Connection conn = new Connection(endpoint);
conn.AssignedProtocol = protocolIdent;
Connections.Add(endpoint, conn);
PacketQueue.Add(conn, new List<Packet>());
return true;
}
public void AddPacketToQueue(Connection conn, Packet p) {
p.id = conn.PacketIDCounter++;
PacketQueue[conn].Add(p);
}
public void SendPacketQueue(Connection conn) {
Protocol protocol = Peer.GetProtocol(conn.AssignedProtocol);
if (protocol != null) {
ByteBuffer buffer = protocol.BuildMessage(conn);
List<Packet> list = PacketQueue[conn];
buffer.WriteInt(list.Count);
foreach (Packet p in list) {
buffer.WriteInt(protocol.GetPacketID(p));
p.Write(buffer);
}
Send(conn, buffer);
}
}
public void SendSinglePacket(Connection conn, Packet p) {
Protocol protocol = Peer.GetProtocol(conn.AssignedProtocol);
if (protocol != null) {
ByteBuffer buffer = protocol.BuildMessage(conn);
buffer.WriteInt(1);
buffer.WriteInt(protocol.GetPacketID(p));
p.Write(buffer);
Send(conn, buffer);
}
}
private void SendPlain(Connection conn) {
Protocol protocol = Peer.GetProtocol(conn.AssignedProtocol);
if (protocol != null) {
ByteBuffer buffer = protocol.BuildMessage(conn);
Send(conn, buffer);
}
}
private void Send(Connection conn, ByteBuffer buffer) {
if (conn.Status == ConnectionStatus.Lost) {
return;
}
byte[] bytes = buffer.Pack();
Peer.ListenerThread.LastSentConnection = conn;
Peer.UdpClient.Send(bytes, bytes.Length, conn.Endpoint);
}
public void Handle(IPEndPoint endpoint, ByteBuffer buffer) {
Connection conn = Find(endpoint);
conn.LastMessage = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ConnectionStatus oldStatus = conn.Status;
byte protocolId = buffer.Read();
Protocol protocol = Peer.GetProtocol(protocolId);
PacketStage stage = buffer.ReadStage();
switch (stage) {
case PacketStage.Establishing:
if (conn.Status == ConnectionStatus.Awaiting) {
conn.AssignedProtocol = protocolId;
string version = buffer.ReadString();
if (protocol == null || !version.Equals(protocol.Version)) {
conn.Status = ConnectionStatus.Rejected;
conn.ClosingReason = ClosingReason.Unknown;
} else {
conn.Status = ConnectionStatus.Ready;
}
if (protocol != null) {
protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn);
}
}
break;
case PacketStage.Rejected:
conn.Status = ConnectionStatus.Closed;
conn.ClosingReason = buffer.ReadClosingReason();
if (protocol != null) {
protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn);
}
break;
case PacketStage.Closed:
conn.Status = ConnectionStatus.Stopped;
if (protocol != null) {
protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn);
}
break;
case PacketStage.Ready:
if (conn.AssignedProtocol != protocolId || protocol == null) {
break;
}
if (oldStatus == ConnectionStatus.Establishing) {
conn.Status = ConnectionStatus.Ready;
protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn);
}
conn.LatestPacketSent = buffer.ReadInt();
List<Packet> list = PacketQueue[conn];
list.RemoveAll(p => p.id <= conn.LatestPacketSent);
PacketQueue[conn] = list;
int PacketAmount = buffer.ReadInt();
for (int i = 0; i < PacketAmount; i++) {
int id = buffer.ReadInt();
conn.LatestPacketReceived = Math.Max(conn.LatestPacketReceived, id);
Type t = protocol.GetPacketType(id);
Packet p = (Packet)Activator.CreateInstance(t);
p.Read(buffer);
protocol.Receive(conn, p);
}
break;
}
}
private void UpdateThreadMethod() {
try {
while (Thread.CurrentThread.IsAlive) {
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
List<KeyValuePair<IPEndPoint, Connection>> timedOut = new List<KeyValuePair<IPEndPoint, Connection>>();
foreach (KeyValuePair<IPEndPoint, Connection> pair in Connections) {
Connection conn = pair.Value;
if ((now - conn.LastMessage) > Timeout || conn.Status == ConnectionStatus.Lost) {
timedOut.Add(pair);
}
if (conn.Status != ConnectionStatus.Awaiting || conn.Status != ConnectionStatus.Stopped) {
if (conn.Status == ConnectionStatus.Ready) {
SendPacketQueue(conn);
} else {
SendPlain(conn);
}
}
}
foreach (KeyValuePair<IPEndPoint, Connection> pair in timedOut) {
Connections.Remove(pair.Key);
PacketQueue.Remove(pair.Value);
if (pair.Value.Status == ConnectionStatus.Ready
|| pair.Value.Status == ConnectionStatus.Establishing
|| pair.Value.Status == ConnectionStatus.Awaiting
|| pair.Value.Status == ConnectionStatus.Lost) {
Protocol protocol = Peer.GetProtocol(pair.Value.AssignedProtocol);
if (protocol != null) {
protocol.Timeout(pair.Value);
}
}
}
Thread.Sleep((int)Interval);
}
} catch (ThreadAbortException) {
Debug.Log("Connection Thread Stopped");
}
}
}
}