236 lines
11 KiB
C#
236 lines
11 KiB
C#
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 {
|
|
/// <summary>Main class for networking. Remember to register a protocol before using. Remember to call Update from a gameobject!</summary>
|
|
public class Peer : PeerMessageListener {
|
|
/// <summary>Underlying UdpClient. Do not touch unless you know what you are doing.</summary>
|
|
public UdpClient UdpClient { get; private set; }
|
|
|
|
/// <summary>The fingerprint for networking. Used to make sure incoming bytes are from a correct source.</summary>
|
|
public byte[] Fingerprint { get; private set; }
|
|
/// <summary>Shorthand for ConnectionManager.Timeout: The amount of milliseconds before a connection is timed out.</summary>
|
|
public long Timeout {
|
|
get {
|
|
return ConnectionManager.Timeout;
|
|
}
|
|
set {
|
|
ConnectionManager.Timeout = value;
|
|
}
|
|
}
|
|
/// <summary>Shorthand for ConnectionManager.Interval: The interval of updates and rate of re-sending reliable messages.</summary>
|
|
public long UpdateInterval {
|
|
get {
|
|
return ConnectionManager.Interval;
|
|
}
|
|
set {
|
|
ConnectionManager.Interval = value;
|
|
}
|
|
}
|
|
/// <summary>The interval of traffic analyzed before updating. By default 5000 (5 seconds)</summary>
|
|
public long TrafficDataInterval = 5000;
|
|
/// <summary>Traffic Data for this Peer</summary>
|
|
public TrafficData TrafficData { get; private set; } = new TrafficData();
|
|
/// <summary>Whether the Peer is currently doing anything or not.null</sumary>
|
|
public bool Running { get; private set; }
|
|
|
|
public ListenerThread ListenerThread;
|
|
public ConnectionManager ConnectionManager;
|
|
public Dictionary<byte, Protocol> RegisteredProtocols = new Dictionary<byte, Protocol>();
|
|
|
|
/// <summary>Listener for messages and errors from within the Peer.</summary>
|
|
public PeerMessageListener MessageListener;
|
|
|
|
private long LastTrafficData;
|
|
|
|
/// <summary>Creates a new Peer with the given fingerprint. The fingerprint can be anything, it just must be same on both peers.</summary>
|
|
public Peer(byte[] fingerprint) {
|
|
Fingerprint = fingerprint;
|
|
ConnectionManager = new ConnectionManager(this);
|
|
MessageListener = this;
|
|
LastTrafficData = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
}
|
|
|
|
/// <summary>Starts the UdpClient, but does no networking as is.</summary>
|
|
public void Start(int sending_port) {
|
|
if (Running) {
|
|
return;
|
|
}
|
|
UdpClient = new UdpClient(sending_port);
|
|
MessageListener.Message("UdpClient Started");
|
|
Running = true;
|
|
}
|
|
|
|
/// <summary>Abruptly stops the UdpClient and all relevant threads.</summary>
|
|
public void Stop() {
|
|
ConnectionManager.StopThread();
|
|
if (ListenerThread != null) {
|
|
ListenerThread.Stop();
|
|
}
|
|
UdpClient.Dispose();
|
|
UdpClient.Close();
|
|
Running = false;
|
|
}
|
|
|
|
/// <summary>Start listening to a given address and port. Usually 0.0.0.0, 0 for clients and 0.0.0.0, port for servers</summary>
|
|
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}");
|
|
}
|
|
|
|
/// <summary>Connect to a remote host. Will initialize a listener to 0.0.0.0:0 if no listener is started.</summary>
|
|
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}");
|
|
}
|
|
|
|
/// <summary>Soft-disconnects the connection with the given uid, meaning it will wait until they acknowledge, before timing out.abstract</summary>
|
|
public void Disconnect(ulong uid) {
|
|
ConnectionManager.CloseConnection(uid, ClosingReason.Manual);
|
|
}
|
|
|
|
/// <summary>Send a reliable packet, meaning it will reliably be delivered.</summary>
|
|
public void SendReliable(ulong uid, Packet packet) {
|
|
if (Running) {
|
|
ConnectionManager.AddPacketToQueue(uid, packet);
|
|
ConnectionManager.SendPacketQueue(uid);
|
|
}
|
|
}
|
|
|
|
/// <summary>Add reliable packet to queue, so that it will be sent on the next update.</summary>
|
|
public void SendReliableLater(ulong uid, Packet packet) {
|
|
if (Running) {
|
|
ConnectionManager.AddPacketToQueue(uid, packet);
|
|
}
|
|
}
|
|
|
|
/// <summary>Send an unreliable packet, meaning its delivery is not reliable.</summary>
|
|
public void SendUnreliable(ulong uid, Packet packet) {
|
|
if (Running) {
|
|
ConnectionManager.SendSingleUnreliable(uid, packet);
|
|
}
|
|
}
|
|
|
|
/// <summary>Get a Connection instance from the given uid, if such exists. Null otherwise.</summary>
|
|
public Connection GetConnection(ulong uid) {
|
|
return ConnectionManager.GetConnection(uid);
|
|
}
|
|
|
|
/// <summary>Register a given protocol. Returns protocol.Identifier if successful, 0 otherwise.</summary>
|
|
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<ProtocolAction>());
|
|
protocol.Peer = this;
|
|
return ident;
|
|
}
|
|
|
|
/// <summary>Get protocol instance from the given identifier, if such exists.</summary>
|
|
public Protocol GetProtocol(byte ident) {
|
|
if (RegisteredProtocols.ContainsKey(ident)) {
|
|
return RegisteredProtocols[ident];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>Shorthand for Peer.ConnectionManager.Update(): Handles network stuff that was received since last update.</summary>
|
|
public void Update() {
|
|
ConnectionManager.Update();
|
|
|
|
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
if (now - LastTrafficData > TrafficDataInterval) {
|
|
LastTrafficData = now;
|
|
if (ListenerThread != null) {
|
|
TrafficData.Received = ListenerThread.BytesReceived;
|
|
TrafficData.TotalMessagesReceived = ListenerThread.MessagesReceived;
|
|
ListenerThread.BytesReceived = 0;
|
|
ListenerThread.MessagesReceived = 0;
|
|
}
|
|
if (ConnectionManager != null) {
|
|
TrafficData.Sent = ConnectionManager.TrafficData.BytesSent;
|
|
TrafficData.TotalMessagesSent = ConnectionManager.TrafficData.TotalMessagesSent;
|
|
TrafficData.UnreliableReceived = ConnectionManager.TrafficData.UnreliablePacketsreceived;
|
|
TrafficData.ReliableReceived = ConnectionManager.TrafficData.ReliablePacketsReceived;
|
|
TrafficData.UnreliableSent = ConnectionManager.TrafficData.UnreliablePacketsSent;
|
|
TrafficData.ReliableSent = ConnectionManager.TrafficData.ReliablePacketsSent;
|
|
|
|
PerPacketData empty = new PerPacketData();
|
|
foreach (Type t in ConnectionManager.TrafficData.ReceivedByPacket.Keys) {
|
|
TrafficData.ReceivedByPacket[t] = ConnectionManager.TrafficData.ReceivedByPacket[t];
|
|
ConnectionManager.TrafficData.ReceivedByPacket[t] = empty;
|
|
}
|
|
foreach (Type t in ConnectionManager.TrafficData.SentByPacket.Keys) {
|
|
TrafficData.SentByPacket[t] = ConnectionManager.TrafficData.SentByPacket[t];
|
|
ConnectionManager.TrafficData.SentByPacket[t] = empty;
|
|
}
|
|
|
|
ConnectionManager.TrafficData.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>Listener for messages and errors from the Peer.</summary>
|
|
public interface PeerMessageListener {
|
|
void Message(string msg);
|
|
void Err(string msg);
|
|
}
|
|
|
|
public class TrafficData {
|
|
/// <summary>The amount of bytes received in the last TrafficDataIntervel</summary>
|
|
public int Received;
|
|
/// <summary>The amount of bytes sent in the last TrafficDataIntervel</summary>
|
|
public int Sent;
|
|
/// <summary>The amount of total messages received in the last TrafficDataIntervel</summary>
|
|
public int TotalMessagesReceived;
|
|
/// <summary>The amount of total messages sent in the last TrafficDataIntervel</summary>
|
|
public int TotalMessagesSent;
|
|
/// <summary>The amount of reliable messages received in the last TrafficDataIntervel</summary>
|
|
public int ReliableReceived;
|
|
/// <summary>The amount of reliable messages sent in the last TrafficDataIntervel</summary>
|
|
public int ReliableSent;
|
|
/// <summary>The amount of unreliable messages received in the last TrafficDataIntervel</summary>
|
|
public int UnreliableReceived;
|
|
/// <summary>The amount of unreliable messages sent in the last TrafficDataIntervel</summary>
|
|
public int UnreliableSent;
|
|
/// <summary>The amount of bytes sent in the last TrafficDataIntervel by Packet</summary>
|
|
public Dictionary<Type, PerPacketData> SentByPacket = new Dictionary<Type, PerPacketData>();
|
|
/// <summary>The amount of bytes received in the last TrafficDataIntervel by Packet</summary>
|
|
public Dictionary<Type, PerPacketData> ReceivedByPacket = new Dictionary<Type, PerPacketData>();
|
|
}
|
|
}
|