commit
624face1ba
9 changed files with 1162 additions and 0 deletions
@ -0,0 +1 @@ |
|||
*.meta |
@ -0,0 +1,235 @@ |
|||
using System.Collections.Generic; |
|||
using System; |
|||
using System.Text; |
|||
|
|||
namespace NeonTea.Quakeball.TeaNet.Packets { |
|||
/// <summary>Contains a stream of bytes for sending or receiving over the internet.</summary>
|
|||
public class ByteBuffer { |
|||
public int Size => Bytes.Count; |
|||
|
|||
private List<byte> Bytes; |
|||
private int pos = 0; |
|||
|
|||
/// <summary>Creates a new empty ByteBuffer</summary>
|
|||
public ByteBuffer() { |
|||
Bytes = new List<byte>(); |
|||
} |
|||
|
|||
/// <summary>Creates a new ByteBuffer from the given byte[]</summary>
|
|||
public ByteBuffer(byte[] bytes) { |
|||
Bytes = new List<byte>(bytes); |
|||
} |
|||
|
|||
/// <summary>Packs the byte buffer in to a byte[] array.</summary>
|
|||
public byte[] Pack() { |
|||
return Bytes.ToArray(); |
|||
} |
|||
|
|||
/// <summary>is there a byte to read next?</summary>
|
|||
public bool CanRead() { |
|||
return pos < Bytes.Count; |
|||
} |
|||
|
|||
/// <summary>Reads a given object with Serializable implemented. Assumes there is one next.</summary>
|
|||
public void ReadSerializable(Serializable s) { |
|||
s.Read(this); |
|||
} |
|||
|
|||
/// <summary>Read a char on the buffer. Assumes there is one next.</summary>
|
|||
public char ReadChar() { |
|||
return BitConverter.ToChar(Read(2), 0); |
|||
} |
|||
|
|||
/// <summary>Read a boolean on the buffer. Assumes there is one next.</summary>
|
|||
public bool ReadBool() { |
|||
return Read() == 1; |
|||
} |
|||
|
|||
/// <summary>Read a double on the buffer. Assumes there is one next.</summary>
|
|||
public double ReadDouble() { |
|||
return BitConverter.ToDouble(Read(8), 0); |
|||
} |
|||
|
|||
/// <summary>Read a float on the buffer. Assumes there is one next.</summary>
|
|||
public float ReadFloat() { |
|||
return BitConverter.ToSingle(Read(4), 0); |
|||
} |
|||
|
|||
/// <summary>Read an unsigned long on the buffer. Assumes there is one next.</summary>
|
|||
public ulong ReadULong() { |
|||
return BitConverter.ToUInt64(Read(8), 0); |
|||
} |
|||
|
|||
/// <summary>Read an unsigned integer on the buffer. Assumes there is one next.</summary>
|
|||
public uint ReadUInt() { |
|||
return BitConverter.ToUInt32(Read(4), 0); |
|||
} |
|||
|
|||
/// <summary>Read an unsigned short on the buffer. Assumes there is one next.</summary>
|
|||
public ushort ReadUShort() { |
|||
return BitConverter.ToUInt16(Read(2), 0); |
|||
} |
|||
|
|||
/// <summary>Read a long on the buffer. Assumes there is one next.</summary>
|
|||
public long ReadLong() { |
|||
return BitConverter.ToInt64(Read(8), 0); |
|||
} |
|||
|
|||
/// <summary>Read an integer on the buffer. Assumes there is one next.</summary>
|
|||
public int ReadInt() { |
|||
return BitConverter.ToInt32(Read(4), 0); |
|||
} |
|||
|
|||
/// <summary>Read a short on the buffer. Assumes there is one next.</summary>
|
|||
public short ReadShort() { |
|||
return BitConverter.ToInt16(Read(2), 0); |
|||
} |
|||
|
|||
/// <summary>Read a string on the buffer. Assumes there is one next.</summary>
|
|||
public string ReadString() { |
|||
int length = ReadInt(); |
|||
string s = Encoding.UTF8.GetString(Read(length)); |
|||
return s; |
|||
} |
|||
|
|||
/// <summary>Read an integer on the buffer. Assumes there is one next.</summary>
|
|||
public byte[] Read(int amount) { |
|||
byte[] bytes = Bytes.GetRange(pos, amount).ToArray(); |
|||
pos += amount; |
|||
return bytes; |
|||
} |
|||
|
|||
/// <summary>Read the next byte on the buffer. Assumes there is one next.</summary>
|
|||
public byte Read() { |
|||
return Bytes[pos++]; |
|||
} |
|||
|
|||
/// <summary>Write something that implements Serializable.</summary>
|
|||
public void Write(Serializable s) { |
|||
s.Write(this); |
|||
} |
|||
|
|||
/// <summary>Write a char to the buffer.</summary>
|
|||
public void Write(char c) { |
|||
Bytes.AddRange(BitConverter.GetBytes(c)); |
|||
} |
|||
|
|||
/// <summary>Write a boolean to the buffer.</summary>
|
|||
public void Write(bool b) { |
|||
Write(b ? (byte)0b1 : (byte)0b0); |
|||
} |
|||
|
|||
/// <summary>Write a double to the buffer.</summary>
|
|||
public void Write(double d) { |
|||
Bytes.AddRange(BitConverter.GetBytes(d)); |
|||
} |
|||
|
|||
/// <summary>Write a float to the buffer.</summary>
|
|||
public void Write(float f) { |
|||
Bytes.AddRange(BitConverter.GetBytes(f)); |
|||
} |
|||
|
|||
/// <summary>Write an unsigned long to the buffer.</summary>
|
|||
public void Write(ulong l) { |
|||
Bytes.AddRange(BitConverter.GetBytes(l)); |
|||
} |
|||
|
|||
/// <summary>Write an unsigned integer to the buffer.</summary>
|
|||
public void Write(uint i) { |
|||
Bytes.AddRange(BitConverter.GetBytes(i)); |
|||
} |
|||
|
|||
/// <summary>Write an unsigned short to the buffer.</summary>
|
|||
public void Write(ushort s) { |
|||
Bytes.AddRange(BitConverter.GetBytes(s)); |
|||
} |
|||
|
|||
/// <summary>Write a long to the buffer.</summary>
|
|||
public void Write(long l) { |
|||
Bytes.AddRange(BitConverter.GetBytes(l)); |
|||
} |
|||
|
|||
/// <summary>Write an integer to the buffer.</summary>
|
|||
public void Write(int i) { |
|||
Bytes.AddRange(BitConverter.GetBytes(i)); |
|||
} |
|||
|
|||
/// <summary>Write a short to the buffer.</summary>
|
|||
public void Write(short s) { |
|||
Bytes.AddRange(BitConverter.GetBytes(s)); |
|||
} |
|||
|
|||
/// <summary>Write a string to the buffer.</summary>
|
|||
public void Write(string s) { |
|||
byte[] bytes = Encoding.UTF8.GetBytes(s); |
|||
Write(bytes.Length); |
|||
Bytes.AddRange(bytes); |
|||
} |
|||
|
|||
/// <summary>Write a byte to the buffer.</summary>
|
|||
public void Write(byte b) { |
|||
Bytes.Add(b); |
|||
} |
|||
|
|||
/// <summary>Read weather the given fingerprint is next on the buffer.</summary>
|
|||
public bool ReadFingerprint(byte[] fingerprint) { |
|||
foreach (byte b in fingerprint) { |
|||
if (!(CanRead() && Read() == b)) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
public PacketStage ReadStage() { |
|||
PacketStage stage = PacketStage.Closed; |
|||
switch (Read()) { |
|||
case 0: |
|||
stage = PacketStage.Establishing; |
|||
break; |
|||
case 1: |
|||
stage = PacketStage.Rejected; |
|||
break; |
|||
case 2: |
|||
stage = PacketStage.Closed; |
|||
break; |
|||
case 3: |
|||
stage = PacketStage.Ready; |
|||
break; |
|||
} |
|||
return stage; |
|||
} |
|||
|
|||
public ClosingReason ReadClosingReason() { |
|||
ClosingReason reason = ClosingReason.Unknown; |
|||
switch (Read()) { |
|||
case 0: |
|||
reason = ClosingReason.Unknown; |
|||
break; |
|||
case 1: |
|||
reason = ClosingReason.IncorrectVersion; |
|||
break; |
|||
} |
|||
return reason; |
|||
} |
|||
|
|||
/// <summary>Write an entire packet using the protocol to the buffer.</summary>
|
|||
public void WritePacket(Protocol protocol, Packet p) { |
|||
int old = Bytes.Count; |
|||
Write(protocol.GetPacketTypeID(p)); |
|||
p.Write(this); |
|||
p.Size = Bytes.Count - old; |
|||
} |
|||
|
|||
/// <summary>Read an entire packet using the given protocol from the buffer.</summary>
|
|||
public Packet ReadPacket(Protocol protocol) { |
|||
int old = pos; |
|||
int packetType = ReadInt(); |
|||
Type t = protocol.GetPacketType(packetType); |
|||
Packet p = (Packet)Activator.CreateInstance(t); |
|||
p.Read(this); |
|||
p.Size = pos - old; |
|||
return p; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
namespace NeonTea.Quakeball.TeaNet.Packets { |
|||
/// <summary>A packet for sending stuff over to connections.</summary>
|
|||
public abstract class Packet { |
|||
/// <summary>Packet meta-information: Is this packet reliable. Set just before sending.</summary>
|
|||
public bool PacketIsReliable = true; |
|||
/// <summary>Packet meta-information: Id of this packet. Set just before sending.</summary>
|
|||
public int PacketId; |
|||
/// <summary>Size of this packet in bytes. Only available after the packet has been Read (when received) or Written (when sent).</summary>
|
|||
public int Size; |
|||
|
|||
/// <summary>Write any relevant information about this packet into the buffer.</summary>
|
|||
public abstract void Write(ByteBuffer buffer); |
|||
/// <summary>Read and assign any relevant information about this packet from the buffer.</summary>
|
|||
public abstract void Read(ByteBuffer buffer); |
|||
|
|||
/// <summary>Make a shallow copy for this packet, copying any primitives but retaining any references to instances.</summary>
|
|||
public Packet ShallowCopy() { |
|||
return (Packet)this.MemberwiseClone(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Defines something as writeable/readable by the buffer. Useful for creating abstractions within packets.</summary>
|
|||
public interface Serializable { |
|||
void Write(ByteBuffer buffer); |
|||
void Read(ByteBuffer buffer); |
|||
} |
|||
|
|||
public enum PacketStage { |
|||
Establishing = 0, |
|||
Rejected = 1, |
|||
Closed = 2, |
|||
Ready = 3, |
|||
} |
|||
|
|||
public enum ClosingReason { |
|||
Unknown = 0, |
|||
IncorrectVersion = 1, |
|||
Timeout = 2, |
|||
Manual = 3, |
|||
} |
|||
|
|||
} |
@ -0,0 +1,71 @@ |
|||
using System.Collections.Generic; |
|||
using System; |
|||
|
|||
using NeonTea.Quakeball.TeaNet.Peers; |
|||
|
|||
|
|||
namespace NeonTea.Quakeball.TeaNet.Packets { |
|||
/// <summary>Manages a single form of conversation between clients for the Peer. Don't forget to register your packets with <see cref="Protocol.RegisterPacket(Type)"/></summary>
|
|||
public abstract class Protocol { |
|||
private Dictionary<Type, int> PacketToId = new Dictionary<Type, int>(); |
|||
private Dictionary<int, Type> IdToPacket = new Dictionary<int, Type>(); |
|||
private int PacketIdCounter; |
|||
|
|||
/// <summary>Refers to the peer it is registered to</summary>
|
|||
public Peer Peer; |
|||
|
|||
/// <summary>Unique identifier for the protocol. This should be different for every protocol.</summary>
|
|||
public abstract byte Identifier { get; } |
|||
/// <summary>Version of the protocol, should be changed if existing packets are changed, new packets are registered or old packets are removed.</summary>
|
|||
public abstract string Version { get; } |
|||
|
|||
/// <summary>Called when the Peer receives a packet from a connection that uses this protocol</summary>
|
|||
public abstract void Receive(Connection conn, Packet packet); |
|||
/// <summary>Called when a ConnectionStatus is changed for a connection that uses this protocol</summary>
|
|||
public abstract void ConnectionStatusChanged(ConnectionStatus oldStatus, ConnectionStatus newStatus, Connection conn); |
|||
/// <summary>Called when a connection that uses this protocol is timed out suddenly.</summary>
|
|||
public abstract void Timeout(Connection conn); |
|||
|
|||
/// <summary>Register a packet for sending and receiving.</summary>
|
|||
public int RegisterPacket(Type t) { |
|||
if (t.BaseType != typeof(Packet) || PacketToId.ContainsKey(t)) { |
|||
return -1; |
|||
} |
|||
int id = PacketIdCounter++; |
|||
PacketToId.Add(t, id); |
|||
IdToPacket.Add(id, t); |
|||
return id; |
|||
} |
|||
|
|||
public ByteBuffer BuildMessage(Connection connection, int firstId, bool reliable) { |
|||
ByteBuffer buffer = new ByteBuffer(); |
|||
foreach (byte b in Peer.Fingerprint) { |
|||
buffer.Write(b); |
|||
} |
|||
buffer.Write(Identifier); |
|||
if (connection.Status == ConnectionStatus.Establishing) { |
|||
buffer.Write((byte)PacketStage.Establishing); |
|||
buffer.Write(Version); |
|||
} else if (connection.Status == ConnectionStatus.Closed) { |
|||
buffer.Write((byte)PacketStage.Closed); |
|||
} else if (connection.Status == ConnectionStatus.Rejected) { |
|||
buffer.Write((byte)PacketStage.Rejected); |
|||
buffer.Write((byte)connection.ClosingReason); |
|||
} else if (connection.Status == ConnectionStatus.Ready) { |
|||
buffer.Write((byte)PacketStage.Ready); |
|||
buffer.Write(connection.Internal.LatestInwardReliable); |
|||
buffer.Write(firstId); |
|||
buffer.Write(reliable); |
|||
} |
|||
return buffer; |
|||
} |
|||
|
|||
public int GetPacketTypeID(Packet packet) { |
|||
return PacketToId[packet.GetType()]; |
|||
} |
|||
|
|||
public Type GetPacketType(int id) { |
|||
return IdToPacket[id]; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
using System.Net; |
|||
using System; |
|||
|
|||
using NeonTea.Quakeball.TeaNet.Packets; |
|||
|
|||
namespace NeonTea.Quakeball.TeaNet.Peers { |
|||
/// <summary>Represents a connection to a remot host over the internet.</summary>
|
|||
public class Connection { |
|||
|
|||
/// <summary>The IP end point of the connection</summary>
|
|||
public IPEndPoint Endpoint; |
|||
/// <summary>The unique identifier of the connection.</summary>
|
|||
public ulong uid; |
|||
/// <summary>Connection status of the current connection.</summary>
|
|||
public ConnectionStatus Status; |
|||
/// <summary>Reason why the connection closed. Null if no reason.</summary>
|
|||
public ClosingReason ClosingReason; |
|||
|
|||
/// <summary>Internal data for the connection. Do not touch, unless you know what you're doing.</summary>
|
|||
public ConnectionInternalData Internal = new ConnectionInternalData(); |
|||
|
|||
public Connection(IPEndPoint endpoint, ConnectionStatus status = ConnectionStatus.Establishing) { |
|||
Endpoint = endpoint; |
|||
Status = status; |
|||
|
|||
Internal.LastMessage = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); |
|||
Internal.LatestInwardReliable = -1; |
|||
Internal.LatestInwardUnreliable = -1; |
|||
Internal.LatestOutwardReliable = -1; |
|||
Internal.LatestOutwardUnreliable = -1; |
|||
} |
|||
|
|||
/// <summary>Is the connection ready and established for sending packets.</summary>
|
|||
public bool IsReady() { |
|||
return Status == ConnectionStatus.Ready; |
|||
} |
|||
|
|||
/// <summary>Is the connection disconnected. Shorthand for weather Status is Rejected, Closed, Stopped or Lost.</summary>
|
|||
public bool IsDisconnected() { |
|||
return !(Status == ConnectionStatus.Ready |
|||
|| Status == ConnectionStatus.Awaiting |
|||
|| Status == ConnectionStatus.Establishing); |
|||
} |
|||
} |
|||
|
|||
public struct ConnectionInternalData { |
|||
/// <summary>The protocol identifier, which this connection uses.</summary>
|
|||
public byte AssignedProtocol; |
|||
/// <summary>Last unix timestamp in milliseconds, when this connection was last heard of.</summary>
|
|||
public long LastMessage; |
|||
/// <summary>Last reliable Packet ID the connection has told us they have</summary>
|
|||
public int LatestOutwardReliable; |
|||
/// <summary>Last unreliablePacket ID the connection has told us they have</summary>
|
|||
public int LatestOutwardUnreliable; |
|||
/// <summary>Last reliable Packet ID we've received from the connection</summary>
|
|||
public int LatestInwardReliable; |
|||
/// <summary>Last unreliable Packet ID we've received from the connection</summary>
|
|||
public int LatestInwardUnreliable; |
|||
|
|||
/// <summary>Reliable Packet ID counter for packets we're sending them</summary>
|
|||
public int ReliablePacketIDCounter; |
|||
/// <summary>Unreliable Packet ID counter for packets we're sending them</summary>
|
|||
public int UnreliablePacketIDCounter; |
|||
} |
|||
|
|||
/// <summary>Initiali</summary>
|
|||
public enum ConnectionStatus { |
|||
/// <summary>Awaiting the other endpoint to establish the connection.</summary>
|
|||
Awaiting, |
|||
/// <summary>Attempting to establish the connection.</summary>
|
|||
Establishing, |
|||
/// <summary>Ready for packet sending</summary>
|
|||
Ready, |
|||
/// <summary>Rejected connection at endpoint, sending information that it was rejected.</summary>
|
|||
Rejected, |
|||
/// <summary>Closed the endpoint, and informing the connection that it should stop.</summary>
|
|||
Closed, |
|||
/// <summary>Connection is stopped and waiting for timeout.</summary>
|
|||
Stopped, |
|||
/// <summary>Connection Lost</summary>
|
|||
Lost, |
|||
} |
|||
} |
@ -0,0 +1,403 @@ |
|||
using System.Collections.Generic; |
|||
using System.Collections.Concurrent; |
|||
using System.Net; |
|||
using System.Threading; |
|||
using System; |
|||
using NeonTea.Quakeball.TeaNet.Packets; |
|||
using UnityEngine; |
|||
|
|||
namespace NeonTea.Quakeball.TeaNet.Peers { |
|||
/// <summary>Manages connections for Peer, sends them keepalives and sends and handles incoming messages.</summary>
|
|||
public class ConnectionManager { |
|||
private ulong ConnectionCounter; |
|||
private Dictionary<ulong, Connection> Connections = new Dictionary<ulong, Connection>(); |
|||
private Dictionary<IPEndPoint, ulong> IPtoID = new Dictionary<IPEndPoint, ulong>(); |
|||
private Dictionary<ulong, ConcurrentQueue<Packet>> PacketQueue = new Dictionary<ulong, ConcurrentQueue<Packet>>(); |
|||
private Peer Peer; |
|||
|
|||
public Dictionary<ulong, Queue<ProtocolAction>> ProtocolActionQueues = new Dictionary<ulong, Queue<ProtocolAction>>(); |
|||
private Queue<Connection> ConnectionsToRemove = new Queue<Connection>(); |
|||
|
|||
private Thread UpdateThread; |
|||
|
|||
public long Timeout = 8000; |
|||
public long Interval = 100; |
|||
|
|||
public ConnectionManagerTrafficData TrafficData = new ConnectionManagerTrafficData(); |
|||
|
|||
public ConnectionManager(Peer peer) { |
|||
Peer = peer; |
|||
UpdateThread = new Thread(new ThreadStart(UpdateThreadMethod)); |
|||
UpdateThread.Start(); |
|||
} |
|||
|
|||
public void StopThread() { |
|||
UpdateThread.Abort(); |
|||
} |
|||
|
|||
/// <summary>Find a given Connection. Should not be used, unless expecting to establish a connection with the endpoint.</summary>
|
|||
public Connection Find(IPEndPoint endpoint) { |
|||
if (IPtoID.ContainsKey(endpoint)) { |
|||
return Connections[IPtoID[endpoint]]; |
|||
} |
|||
Connection conn = new Connection(endpoint, ConnectionStatus.Awaiting); |
|||
AddConnection(conn); |
|||
return conn; |
|||
} |
|||
|
|||
/// <summary>Start establishing a connection to a given endpoint with the given protocol</summary>
|
|||
public bool StartConnection(IPEndPoint endpoint, byte protocolIdent) { |
|||
if (IPtoID.ContainsKey(endpoint)) { |
|||
return false; |
|||
} |
|||
Connection conn = new Connection(endpoint); |
|||
conn.Internal.AssignedProtocol = protocolIdent; |
|||
AddConnection(conn); |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>Get the connection instance from the given uid, if such exists. Null otherwise.</summary>
|
|||
public Connection GetConnection(ulong uid) { |
|||
Connection conn; |
|||
Connections.TryGetValue(uid, out conn); |
|||
return conn; |
|||
} |
|||
|
|||
/// <summary>Soft-closes the connection with the given uid, meaning it will wait for them to acknowledge the closing</summary>
|
|||
public void CloseConnection(ulong uid, ClosingReason reason) { |
|||
if (Connections.ContainsKey(uid)) { |
|||
Connections[uid].ClosingReason = reason; |
|||
Connections[uid].Status = ConnectionStatus.Rejected; |
|||
} |
|||
} |
|||
|
|||
/// <summary>Add a reliable packet to the packet queue, to be sent on the next update, or when SendPacketQueue is called.</summary>
|
|||
public void AddPacketToQueue(ulong uid, Packet p) { |
|||
if (!Connections.ContainsKey(uid)) { |
|||
return; |
|||
} |
|||
p = p.ShallowCopy(); |
|||
p.PacketId = Connections[uid].Internal.ReliablePacketIDCounter++; |
|||
PacketQueue[uid].Enqueue(p); |
|||
} |
|||
|
|||
/// <summary>Send the current packet queue instantly.</summary>
|
|||
public void SendPacketQueue(ulong uid) { |
|||
if (!Connections.ContainsKey(uid)) { |
|||
return; |
|||
} |
|||
Connection conn = Connections[uid]; |
|||
Protocol protocol = Peer.GetProtocol(conn.Internal.AssignedProtocol); |
|||
if (protocol != null && conn.IsReady()) { |
|||
Packet[] list = PacketQueue[uid].ToArray(); |
|||
int firstId = Int32.MaxValue; |
|||
if (list.Length > 0) { |
|||
firstId = list[0].PacketId; |
|||
} |
|||
ByteBuffer buffer = protocol.BuildMessage(conn, firstId, true); |
|||
buffer.Write(list.Length); |
|||
foreach (Packet p in list) { |
|||
buffer.WritePacket(protocol, p); |
|||
|
|||
// Do the analytics dance!
|
|||
PerPacketData OldSentByPacket; |
|||
TrafficData.SentByPacket.TryGetValue(p.GetType(), out OldSentByPacket); |
|||
OldSentByPacket.Bytes += p.Size; |
|||
OldSentByPacket.Packets += 1; |
|||
TrafficData.SentByPacket[p.GetType()] = OldSentByPacket; |
|||
TrafficData.ReliablePacketsSent++; |
|||
} |
|||
Send(conn, buffer); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Send a single unreliable packet.</summary>
|
|||
public void SendSingleUnreliable(ulong uid, Packet p) { |
|||
if (!Connections.ContainsKey(uid)) { |
|||
return; |
|||
} |
|||
Connection conn = Connections[uid]; |
|||
p = p.ShallowCopy(); |
|||
p.PacketId = conn.Internal.UnreliablePacketIDCounter++; |
|||
p.PacketIsReliable = false; |
|||
Protocol protocol = Peer.GetProtocol(conn.Internal.AssignedProtocol); |
|||
if (protocol != null && conn.IsReady()) { |
|||
ByteBuffer buffer = protocol.BuildMessage(conn, p.PacketId, false); |
|||
buffer.Write(1); |
|||
buffer.WritePacket(protocol, p); |
|||
Send(conn, buffer); |
|||
TrafficData.UnreliablePacketsSent++; |
|||
|
|||
// Do the analytics dance!
|
|||
PerPacketData OldSentByPacket; |
|||
TrafficData.SentByPacket.TryGetValue(p.GetType(), out OldSentByPacket); |
|||
OldSentByPacket.Bytes += p.Size; |
|||
OldSentByPacket.Packets += 1; |
|||
TrafficData.SentByPacket[p.GetType()] = OldSentByPacket; |
|||
} |
|||
} |
|||
|
|||
/// <summary>Go through queue of networking actions that have happened since last update.</summary>
|
|||
public void Update() { |
|||
foreach (byte id in ProtocolActionQueues.Keys) { |
|||
Protocol protocol = Peer.GetProtocol(id); |
|||
while (ProtocolActionQueues[id].Count > 0) { |
|||
ProtocolAction action = ProtocolActionQueues[id].Dequeue(); |
|||
if (action is ReceiveAction) { |
|||
ReceiveAction receive = (ReceiveAction)action; |
|||
protocol.Receive(receive.Connection, receive.Packet); |
|||
} else if (action is ConnectionChangedAction) { |
|||
ConnectionChangedAction changed = (ConnectionChangedAction)action; |
|||
protocol.ConnectionStatusChanged(changed.OldStatus, changed.NewStatus, changed.Connection); |
|||
} else if (action is TimeoutAction) { |
|||
TimeoutAction changed = (TimeoutAction)action; |
|||
protocol.Timeout(changed.Connection); |
|||
} |
|||
} |
|||
} |
|||
while (ConnectionsToRemove.Count > 0) { |
|||
Connection conn = ConnectionsToRemove.Dequeue(); |
|||
RemoveConnection(conn); |
|||
} |
|||
} |
|||
|
|||
private void AddConnection(Connection conn) { |
|||
conn.uid = ConnectionCounter++; |
|||
Connections.Add(conn.uid, conn); |
|||
IPtoID.Add(conn.Endpoint, conn.uid); |
|||
PacketQueue.Add(conn.uid, new ConcurrentQueue<Packet>()); |
|||
} |
|||
|
|||
private void RemoveConnection(Connection conn) { |
|||
Connections.Remove(conn.uid); |
|||
IPtoID.Remove(conn.Endpoint); |
|||
PacketQueue.Remove(conn.uid); |
|||
} |
|||
|
|||
private void SendPlain(Connection conn) { |
|||
Protocol protocol = Peer.GetProtocol(conn.Internal.AssignedProtocol); |
|||
if (protocol != null) { |
|||
ByteBuffer buffer = protocol.BuildMessage(conn, Int32.MaxValue, false); |
|||
Send(conn, buffer); |
|||
} |
|||
} |
|||
|
|||
private void Send(Connection conn, ByteBuffer buffer) { |
|||
if (conn.Status == ConnectionStatus.Lost) { |
|||
return; |
|||
} |
|||
TrafficData.BytesSent += buffer.Size; |
|||
TrafficData.TotalMessagesSent++; |
|||
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.Internal.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.Internal.AssignedProtocol = protocolId; |
|||
string version = buffer.ReadString(); |
|||
if (protocol == null || !version.Equals(protocol.Version)) { |
|||
conn.Status = ConnectionStatus.Rejected; |
|||
conn.ClosingReason = ClosingReason.IncorrectVersion; |
|||
} else { |
|||
conn.Status = ConnectionStatus.Ready; |
|||
} |
|||
if (protocol != null) { |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ConnectionChangedAction(oldStatus, conn.Status, conn)); |
|||
} |
|||
} |
|||
break; |
|||
case PacketStage.Rejected: |
|||
conn.Status = ConnectionStatus.Closed; |
|||
conn.ClosingReason = buffer.ReadClosingReason(); |
|||
if (protocol != null) { |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ConnectionChangedAction(oldStatus, conn.Status, conn)); |
|||
} |
|||
break; |
|||
case PacketStage.Closed: |
|||
if (conn.Status == ConnectionStatus.Stopped) { |
|||
break; |
|||
} |
|||
conn.Status = ConnectionStatus.Stopped; |
|||
if (protocol != null) { |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ConnectionChangedAction(oldStatus, conn.Status, conn)); |
|||
} |
|||
break; |
|||
case PacketStage.Ready: |
|||
if (conn.Internal.AssignedProtocol != protocolId || protocol == null) { |
|||
break; |
|||
} |
|||
if (oldStatus == ConnectionStatus.Establishing) { // Update connection status
|
|||
conn.Status = ConnectionStatus.Ready; |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ConnectionChangedAction(oldStatus, conn.Status, conn)); |
|||
} |
|||
if (!(oldStatus == ConnectionStatus.Establishing || oldStatus == ConnectionStatus.Ready)) { |
|||
break; // No cheating at this table! For realsies this time!
|
|||
} |
|||
|
|||
conn.Internal.LatestOutwardReliable = buffer.ReadInt(); |
|||
int FirstPacketId = buffer.ReadInt(); |
|||
bool Reliable = buffer.ReadBool(); |
|||
|
|||
ConcurrentQueue<Packet> queue = PacketQueue[conn.uid]; |
|||
Packet peeked; |
|||
while (queue.TryPeek(out peeked)) { |
|||
if (peeked.PacketId > conn.Internal.LatestOutwardReliable) { |
|||
break; |
|||
} else { |
|||
Packet LastRemoved; |
|||
queue.TryDequeue(out LastRemoved); |
|||
} |
|||
} |
|||
PacketQueue[conn.uid] = queue; |
|||
|
|||
|
|||
int PacketAmount = buffer.ReadInt(); |
|||
|
|||
if (Reliable) { |
|||
TrafficData.ReliablePacketsReceived += PacketAmount; |
|||
} else { |
|||
TrafficData.UnreliablePacketsreceived += PacketAmount; |
|||
} |
|||
|
|||
for (int i = 0; i < PacketAmount; i++) { |
|||
Packet p = buffer.ReadPacket(protocol); |
|||
p.PacketId = FirstPacketId + i; |
|||
p.PacketIsReliable = Reliable; |
|||
if (p.PacketIsReliable) { |
|||
if (p.PacketId > conn.Internal.LatestInwardReliable) { |
|||
conn.Internal.LatestInwardReliable = p.PacketId; |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ReceiveAction(conn, p)); |
|||
} |
|||
} else if (p.PacketId > conn.Internal.LatestInwardUnreliable) { |
|||
conn.Internal.LatestInwardUnreliable = p.PacketId; |
|||
ProtocolActionQueues[protocol.Identifier].Enqueue(new ReceiveAction(conn, p)); |
|||
} |
|||
|
|||
// Do some analytics!
|
|||
PerPacketData OldReceivedByPacket; |
|||
TrafficData.ReceivedByPacket.TryGetValue(p.GetType(), out OldReceivedByPacket); |
|||
OldReceivedByPacket.Bytes += p.Size; |
|||
OldReceivedByPacket.Packets += 1; |
|||
TrafficData.ReceivedByPacket[p.GetType()] = OldReceivedByPacket; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
private void UpdateThreadMethod() { |
|||
try { |
|||
while (Thread.CurrentThread.IsAlive) { |
|||
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); |
|||
List<ulong> timedOut = new List<ulong>(); |
|||
foreach (ulong uid in Connections.Keys) { |
|||
Connection conn = Connections[uid]; |
|||
if ((now - conn.Internal.LastMessage) > Timeout || conn.Status == ConnectionStatus.Lost) { |
|||
timedOut.Add(uid); |
|||
} |
|||
if (conn.Status != ConnectionStatus.Awaiting || conn.Status != ConnectionStatus.Stopped) { |
|||
if (conn.Status == ConnectionStatus.Ready) { |
|||
SendPacketQueue(uid); |
|||
} else { |
|||
SendPlain(conn); |
|||
} |
|||
} |
|||
} |
|||
foreach (ulong uid in timedOut) { |
|||
Connection conn = Connections[uid]; |
|||
ConnectionsToRemove.Enqueue(conn); |
|||
if (conn.Status == ConnectionStatus.Ready |
|||
|| conn.Status == ConnectionStatus.Establishing |
|||
|| conn.Status == ConnectionStatus.Awaiting |
|||
|| conn.Status == ConnectionStatus.Lost) { |
|||
Protocol protocol = Peer.GetProtocol(conn.Internal.AssignedProtocol); |
|||
if (protocol != null) { |
|||
conn.ClosingReason = ClosingReason.Timeout; |
|||
ProtocolActionQueues[conn.Internal.AssignedProtocol].Enqueue(new TimeoutAction(conn)); |
|||
} |
|||
} |
|||
} |
|||
Thread.Sleep((int)Interval); |
|||
} |
|||
} catch (ThreadAbortException) { |
|||
Peer.MessageListener.Message("Connection Thread Stopped"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public abstract class ProtocolAction { }; |
|||
|
|||
class ReceiveAction : ProtocolAction { |
|||
public Connection Connection; |
|||
public Packet Packet; |
|||
|
|||
public ReceiveAction(Connection connection, Packet packet) { |
|||
Connection = connection; |
|||
Packet = packet; |
|||
} |
|||
} |
|||
|
|||
class ConnectionChangedAction : ProtocolAction { |
|||
public ConnectionStatus OldStatus; |
|||
public ConnectionStatus NewStatus; |
|||
public Connection Connection; |
|||
|
|||
public ConnectionChangedAction(ConnectionStatus old, ConnectionStatus newstatus, Connection connection) { |
|||
Connection = connection; |
|||
OldStatus = old; |
|||
NewStatus = newstatus; |
|||
} |
|||
} |
|||
|
|||
class TimeoutAction : ProtocolAction { |
|||
public Connection Connection; |
|||
|
|||
public TimeoutAction(Connection connection) { |
|||
Connection = connection; |
|||
} |
|||
} |
|||
|
|||
public class ConnectionManagerTrafficData { |
|||
/// <summary>The amount of bytes sent since the last clear interval from Peer</summary>
|
|||
public int BytesSent; |
|||
/// <summary>The amount of total IP messages sent since the last clear interval from Peer</summary>
|
|||
public int TotalMessagesSent; |
|||
/// <summary>The amount of reliable packets sent since the last clear interval from Peer</summary>
|
|||
public int ReliablePacketsSent; |
|||
/// <summary>The amount of unreliable packets sent since the last clear interval from Peer</summary>
|
|||
public int UnreliablePacketsSent; |
|||
/// <summary>The amount of reliable packets received since the last clear interval from Peer</summary>
|
|||
public int ReliablePacketsReceived; |
|||
/// <summary>The amount of unreliable packets received since the last clear interval from Peer</summary>
|
|||
public int UnreliablePacketsreceived; |
|||
/// <summary>Data relating to outward packet specific traffic</summary>
|
|||
public ConcurrentDictionary<Type, PerPacketData> SentByPacket = new ConcurrentDictionary<Type, PerPacketData>(); |
|||
/// <summary>Data relating to inward packet specific traffic</summary>
|
|||
public ConcurrentDictionary<Type, PerPacketData> ReceivedByPacket = new ConcurrentDictionary<Type, PerPacketData>(); |
|||
|
|||
public void Clear() { |
|||
BytesSent = 0; |
|||
TotalMessagesSent = 0; |
|||
ReliablePacketsSent = 0; |
|||
UnreliablePacketsSent = 0; |
|||
ReliablePacketsReceived = 0; |
|||
UnreliablePacketsreceived = 0; |
|||
} |
|||
} |
|||
|
|||
public struct PerPacketData { |
|||
public int Bytes; |
|||
public int Packets; |
|||
} |
|||
} |
@ -0,0 +1,79 @@ |
|||
using UnityEngine; |
|||
using System.Net; |
|||
using System; |
|||
using System.Net.Sockets; |
|||
using System.Threading; |
|||
|
|||
using NeonTea.Quakeball.TeaNet.Packets; |
|||
|
|||
namespace NeonTea.Quakeball.TeaNet.Peers { |
|||
/// <summary>Manager for the thread that listens from the given endpoint. Initiated with <see cref="Peer.StartListen(string, int)"/></summary>
|
|||
public class ListenerThread { |
|||
private IPEndPoint EndPoint; |
|||
private Thread Thread; |
|||
private Peer Peer; |
|||
|
|||
public Connection LastSentConnection; |
|||
|
|||
private static int[] CONN_LOST_CODES = new int[] { 10054, 10051 }; |
|||
|
|||
/// <summary>The amount of bytes received since the last clear interval from Peer</summary>
|
|||
public int BytesReceived; |
|||
/// <summary>The amount of bytes received since the last clear interval from Peer</summary>
|
|||
public int MessagesReceived; |
|||
|
|||
public ListenerThread(Peer peer, IPEndPoint endpoint) { |
|||
EndPoint = endpoint; |
|||
Peer = peer; |
|||
} |
|||
|
|||
public bool Start() { |
|||
if (Thread != null) { |
|||
return false; |
|||
} |
|||
Thread t = new Thread(new ThreadStart(ListenThreadMethod)); |
|||
|
|||
t.Start(); |
|||
Thread = t; |
|||
return true; |
|||
} |
|||
|
|||
public bool Stop() { |
|||
if (Thread == null) { |
|||
return false; |
|||
} |
|||
Thread.Abort(); |
|||
return true; |
|||
} |
|||
|
|||
private void ListenThreadMethod() { |
|||
try { |
|||
while (Thread.CurrentThread.IsAlive) { |
|||
if (Peer.UdpClient.Available > 0) { |
|||
IPEndPoint Listened = new IPEndPoint(EndPoint.Address, EndPoint.Port); |
|||
ByteBuffer Buffer = new ByteBuffer(); |
|||
try { |
|||
Buffer = new ByteBuffer(Peer.UdpClient.Receive(ref Listened)); |
|||
} catch (SocketException e) { |
|||
if (Array.Exists(CONN_LOST_CODES, x => x == e.ErrorCode)) { |
|||
if (LastSentConnection != null) { |
|||
LastSentConnection.Status = ConnectionStatus.Lost; |
|||
Peer.MessageListener.Err($"Connection lost to {LastSentConnection.Endpoint}: {e.ToString()}"); |
|||
} |
|||
} else { |
|||
Peer.MessageListener.Err($"Listener error: {e.ToString()}"); |
|||
} |
|||
} |
|||
if (Buffer.ReadFingerprint(Peer.Fingerprint)) { |
|||
Peer.ConnectionManager.Handle(Listened, Buffer); |
|||
BytesReceived += Buffer.Size; |
|||
MessagesReceived++; |
|||
} |
|||
} |
|||
} |
|||
} catch (ThreadAbortException) { |
|||
Peer.MessageListener.Message("Listener Thread stopped"); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,235 @@ |
|||
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>(); |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
# TeaNet |
|||
|
|||
TeaNet is a networking library written in **C#** using .NET Core 3.1 System.Net.Sockets |
|||
|
|||
TeaNet works with a very low-level networking API to provide a highly performant networking library |
|||
that is primarily meant for use in games, but can be used for anything else as well. |
|||
|
|||
TeaNet was originally written in [Unity][unity] for a game project called [quakeball][quakeball] by [neontea][neontea], |
|||
and while the entire TeaNet codebase is written by me, it's commit history can be found in quakeball's repository. |
|||
|
|||
[unity]: https://unity.com/ |
|||
[quakeball]: https://git.teascade.net/neontea/quakeball/ |
|||
[neontea]: https://neontea.itch.io/ |
Loading…
Reference in new issue