diff --git a/Assets/Scripts/TeaNet/Packets/ByteBuffer.cs b/Assets/Scripts/TeaNet/Packets/ByteBuffer.cs index b909890..0f0628a 100644 --- a/Assets/Scripts/TeaNet/Packets/ByteBuffer.cs +++ b/Assets/Scripts/TeaNet/Packets/ByteBuffer.cs @@ -3,140 +3,173 @@ using System; using System.Text; namespace NeonTea.Quakeball.TeaNet.Packets { + /// Contains a stream of bytes for sending or receiving over the internet. public class ByteBuffer { private List Bytes; private int pos = 0; + /// Creates a new empty ByteBuffer public ByteBuffer() { Bytes = new List(); } + /// Creates a new ByteBuffer from the given byte[] public ByteBuffer(byte[] bytes) { Bytes = new List(bytes); } + /// Packs the byte buffer in to a byte[] array. public byte[] Pack() { return Bytes.ToArray(); } + /// is there a byte to read next? public bool CanRead() { return pos < Bytes.Count; } + /// Reads a given object with Serializable implemented. Assumes there is one next. public void ReadSerializable(Serializable s) { s.Read(this); } + /// Read a char on the buffer. Assumes there is one next. public char ReadChar() { return BitConverter.ToChar(Read(2), 0); } + /// Read a boolean on the buffer. Assumes there is one next. public bool ReadBool() { return Read() == 1; } + /// Read a double on the buffer. Assumes there is one next. public double ReadDouble() { return BitConverter.ToDouble(Read(8), 0); } + /// Read a float on the buffer. Assumes there is one next. public float ReadFloat() { return BitConverter.ToSingle(Read(4), 0); } + /// Read an unsigned long on the buffer. Assumes there is one next. public ulong ReadULong() { return BitConverter.ToUInt64(Read(8), 0); } + /// Read an unsigned integer on the buffer. Assumes there is one next. public uint ReadUInt() { return BitConverter.ToUInt32(Read(4), 0); } + /// Read an unsigned short on the buffer. Assumes there is one next. public ushort ReadUShort() { return BitConverter.ToUInt16(Read(2), 0); } + /// Read a long on the buffer. Assumes there is one next. public long ReadLong() { return BitConverter.ToInt64(Read(8), 0); } + /// Read an integer on the buffer. Assumes there is one next. public int ReadInt() { return BitConverter.ToInt32(Read(4), 0); } + /// Read a short on the buffer. Assumes there is one next. public short ReadShort() { return BitConverter.ToInt16(Read(2), 0); } + /// Read a string on the buffer. Assumes there is one next. public string ReadString() { int length = ReadInt(); string s = Encoding.UTF8.GetString(Read(length)); return s; } + /// Read an integer on the buffer. Assumes there is one next. public byte[] Read(int amount) { byte[] bytes = Bytes.GetRange(pos, amount).ToArray(); pos += amount; return bytes; } + /// Read the next byte on the buffer. Assumes there is one next. public byte Read() { return Bytes[pos++]; } + /// Write something that implements Serializable. public void Write(Serializable s) { s.Write(this); } + /// Write a char to the buffer. public void Write(char c) { Bytes.AddRange(BitConverter.GetBytes(c)); } + /// Write a boolean to the buffer. public void Write(bool b) { Write(b ? (byte)0b1 : (byte)0b0); } + /// Write a double to the buffer. public void Write(double d) { Bytes.AddRange(BitConverter.GetBytes(d)); } + /// Write a float to the buffer. public void Write(float f) { Bytes.AddRange(BitConverter.GetBytes(f)); } + /// Write an unsigned long to the buffer. public void Write(ulong l) { Bytes.AddRange(BitConverter.GetBytes(l)); } + /// Write an unsigned integer to the buffer. public void Write(uint i) { Bytes.AddRange(BitConverter.GetBytes(i)); } + /// Write an unsigned short to the buffer. public void Write(ushort s) { Bytes.AddRange(BitConverter.GetBytes(s)); } + /// Write a long to the buffer. public void Write(long l) { Bytes.AddRange(BitConverter.GetBytes(l)); } + /// Write an integer to the buffer. public void Write(int i) { Bytes.AddRange(BitConverter.GetBytes(i)); } + /// Write a short to the buffer. public void Write(short s) { Bytes.AddRange(BitConverter.GetBytes(s)); } + /// Write a string to the buffer. public void Write(string s) { byte[] bytes = Encoding.UTF8.GetBytes(s); Write(bytes.Length); Bytes.AddRange(bytes); } + /// Write a byte to the buffer. public void Write(byte b) { Bytes.Add(b); } + /// Read weather the given fingerprint is next on the buffer. public bool ReadFingerprint(byte[] fingerprint) { foreach (byte b in fingerprint) { if (!(CanRead() && Read() == b)) { @@ -178,12 +211,14 @@ namespace NeonTea.Quakeball.TeaNet.Packets { return reason; } + /// Write an entire packet using the protocol to the buffer. public void WritePacket(Protocol protocol, Packet p) { Write(protocol.GetPacketTypeID(p)); p.WriteMeta(this); p.Write(this); } + /// Read an entire packet using the given protocol from the buffer. public Packet ReadPacket(Protocol protocol) { int packetType = ReadInt(); Type t = protocol.GetPacketType(packetType); diff --git a/Assets/Scripts/TeaNet/Packets/Packet.cs b/Assets/Scripts/TeaNet/Packets/Packet.cs index 1a28208..9b32edc 100644 --- a/Assets/Scripts/TeaNet/Packets/Packet.cs +++ b/Assets/Scripts/TeaNet/Packets/Packet.cs @@ -1,26 +1,35 @@ namespace NeonTea.Quakeball.TeaNet.Packets { + /// A packet for sending stuff over to connections. public abstract class Packet { + /// Packet meta-information: Is this packet reliable. Set just before sending. public bool Reliable = true; + /// Packet meta-information: Id of this packet. Set just before sending. public int Id; + /// Write any relevant information about this packet into the buffer. public abstract void Write(ByteBuffer buffer); + /// Read and assign any relevant information about this packet from the buffer. public abstract void Read(ByteBuffer buffer); + /// Reads packet meta-information from the buffer. public void ReadMeta(ByteBuffer buffer) { Id = buffer.ReadInt(); Reliable = buffer.ReadBool(); } + /// Writes packet meta-information to the buffer. public void WriteMeta(ByteBuffer buffer) { buffer.Write(Id); buffer.Write(Reliable); } + /// Make a shallow copy for this packet, copying any primitives but retaining any references to instances. public Packet ShallowCopy() { return (Packet)this.MemberwiseClone(); } } + /// Defines something as writeable/readable by the buffer. Useful for creating abstractions within packets. public interface Serializable { void Write(ByteBuffer buffer); void Read(ByteBuffer buffer); diff --git a/Assets/Scripts/TeaNet/Packets/Protocol.cs b/Assets/Scripts/TeaNet/Packets/Protocol.cs index 5ac9edf..399ed00 100644 --- a/Assets/Scripts/TeaNet/Packets/Protocol.cs +++ b/Assets/Scripts/TeaNet/Packets/Protocol.cs @@ -5,25 +5,28 @@ using NeonTea.Quakeball.TeaNet.Peers; namespace NeonTea.Quakeball.TeaNet.Packets { + /// Manages a single form of conversation between clients for the Peer. Don't forget to register your packets with public abstract class Protocol { private Dictionary PacketToId = new Dictionary(); private Dictionary IdToPacket = new Dictionary(); private int PacketIdCounter; + /// Refers to the peer it is registered to public Peer Peer; + /// Unique identifier for the protocol. This should be different for every protocol. public abstract byte Identifier { get; } + /// Version of the protocol, should be changed if existing packets are changed, new packets are registered or old packets are removed. public abstract string Version { get; } + /// Called when the Peer receives a packet from a connection that uses this protocol public abstract void Receive(Connection conn, Packet packet); + /// Called when a ConnectionStatus is changed for a connection that uses this protocol public abstract void ConnectionStatusChanged(ConnectionStatus oldStatus, ConnectionStatus newStatus, Connection conn); + /// Called when a connection that uses this protocol is timed out suddenly. public abstract void Timeout(Connection conn); - public void SendPacket(Packet p, Connection conn) { - Peer.ConnectionManager.AddPacketToQueue(conn.uid, p); - Peer.ConnectionManager.SendPacketQueue(conn.uid); - } - + /// Register a packet for sending and receiving. public int RegisterPacket(Type t) { if (t.BaseType != typeof(Packet) || PacketToId.ContainsKey(t)) { return -1; diff --git a/Assets/Scripts/TeaNet/Peers/Connection.cs b/Assets/Scripts/TeaNet/Peers/Connection.cs index 27a0a00..da3a60a 100644 --- a/Assets/Scripts/TeaNet/Peers/Connection.cs +++ b/Assets/Scripts/TeaNet/Peers/Connection.cs @@ -6,13 +6,19 @@ using System; using NeonTea.Quakeball.TeaNet.Packets; namespace NeonTea.Quakeball.TeaNet.Peers { + /// Represents a connection to a remot host over the internet. public class Connection { + /// The IP end point of the connection public IPEndPoint Endpoint; + /// The unique identifier of the connection. public ulong uid; + /// Connection status of the current connection. public ConnectionStatus Status; + /// Reason why the connection closed. Null if no reason. public ClosingReason ClosingReason; + /// Internal data for the connection. Do not touch, unless you know what you're doing. public ConnectionInternalData Internal = new ConnectionInternalData(); public Connection(IPEndPoint endpoint, ConnectionStatus status = ConnectionStatus.Establishing) { @@ -26,10 +32,12 @@ namespace NeonTea.Quakeball.TeaNet.Peers { Internal.LatestOutwardUnreliable = -1; } + /// Is the connection ready and established for sending packets. public bool IsReady() { return Status == ConnectionStatus.Ready; } + /// Is the connection disconnected. Shorthand for weather Status is Rejected, Closed, Stopped or Lost. public bool IsDisconnected() { return !(Status == ConnectionStatus.Ready || Status == ConnectionStatus.Awaiting @@ -38,24 +46,40 @@ namespace NeonTea.Quakeball.TeaNet.Peers { } public struct ConnectionInternalData { + /// The protocol identifier, which this connection uses. public byte AssignedProtocol; + /// Last unix timestamp in milliseconds, when this connection was last heard of. public long LastMessage; - public int LatestOutwardReliable; // Last reliable Packet ID the connection has told us they have - public int LatestOutwardUnreliable; // Last unreliablePacket ID the connection has told us they have - public int LatestInwardReliable; // Last reliable Packet ID we've received from the connection - public int LatestInwardUnreliable; // Last unreliable Packet ID we've received from the connection + /// Last reliable Packet ID the connection has told us they have + public int LatestOutwardReliable; + /// Last unreliablePacket ID the connection has told us they have + public int LatestOutwardUnreliable; + /// Last reliable Packet ID we've received from the connection + public int LatestInwardReliable; + /// Last unreliable Packet ID we've received from the connection + public int LatestInwardUnreliable; - public int ReliablePacketIDCounter; // Reliable Packet ID counter for packets we're sending them - public int UnreliablePacketIDCounter; // Unreliable Packet ID counter for packets we're sending them + /// Reliable Packet ID counter for packets we're sending them + public int ReliablePacketIDCounter; + /// Unreliable Packet ID counter for packets we're sending them + public int UnreliablePacketIDCounter; } + /// Initiali public enum ConnectionStatus { - Awaiting, // Awaiting an establishing - Establishing, // Attempting to establish - Ready, // Ready for packet sending - Rejected, // Rejected connection at endpoint, sending Rejected - Closed, // Closed connection at endpoint, sending Closed - Stopped, // Not sending packages - Lost, // Connection Lost + /// Awaiting the other endpoint to establish the connection. + Awaiting, + /// Attempting to establish the connection. + Establishing, + /// Ready for packet sending + Ready, + /// Rejected connection at endpoint, sending information that it was rejected. + Rejected, + /// Closed the endpoint, and informing the connection that it should stop. + Closed, + /// Connection is stopped and waiting for timeout. + Stopped, + /// Connection Lost + Lost, } } diff --git a/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs b/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs index 1fdd37b..47b005f 100644 --- a/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs +++ b/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs @@ -6,6 +6,7 @@ using System; using NeonTea.Quakeball.TeaNet.Packets; namespace NeonTea.Quakeball.TeaNet.Peers { + /// Manages connections for Peer, sends them keepalives and sends and handles incoming messages. public class ConnectionManager { private ulong ConnectionCounter; private Dictionary Connections = new Dictionary(); @@ -28,6 +29,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { UpdateThread.Abort(); } + /// Find a given Connection. Should not be used, unless expecting to establish a connection with the endpoint. public Connection Find(IPEndPoint endpoint) { if (IPtoID.ContainsKey(endpoint)) { return Connections[IPtoID[endpoint]]; @@ -37,6 +39,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { return conn; } + /// Start establishing a connection to a given endpoint with the given protocol public bool StartConnection(IPEndPoint endpoint, byte protocolIdent) { if (IPtoID.ContainsKey(endpoint)) { return false; @@ -47,12 +50,14 @@ namespace NeonTea.Quakeball.TeaNet.Peers { return true; } + /// Get the connection instance from the given uid, if such exists. Null otherwise. public Connection GetConnection(ulong uid) { Connection conn; Connections.TryGetValue(uid, out conn); return conn; } + /// Add a reliable packet to the packet queue, to be sent on the next update, or when SendPacketQueue is called. public void AddPacketToQueue(ulong uid, Packet p) { if (!Connections.ContainsKey(uid)) { return; @@ -62,6 +67,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { PacketQueue[uid].Add(p); } + /// Send the current packet queue instantly. public void SendPacketQueue(ulong uid) { if (!Connections.ContainsKey(uid)) { return; @@ -79,6 +85,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { } } + /// Send a single unreliable packet. public void SendSingleUnreliable(ulong uid, Packet p) { if (!Connections.ContainsKey(uid)) { return; diff --git a/Assets/Scripts/TeaNet/Peers/ListenerThread.cs b/Assets/Scripts/TeaNet/Peers/ListenerThread.cs index acd8831..d41f3df 100644 --- a/Assets/Scripts/TeaNet/Peers/ListenerThread.cs +++ b/Assets/Scripts/TeaNet/Peers/ListenerThread.cs @@ -7,6 +7,7 @@ using System.Threading; using NeonTea.Quakeball.TeaNet.Packets; namespace NeonTea.Quakeball.TeaNet.Peers { + /// Manager for the thread that listens from the given endpoint. Initiated with public class ListenerThread { private IPEndPoint EndPoint; private Thread Thread; @@ -21,7 +22,6 @@ namespace NeonTea.Quakeball.TeaNet.Peers { Peer = peer; } - public bool Start() { if (Thread != null) { return false; diff --git a/Assets/Scripts/TeaNet/Peers/Peer.cs b/Assets/Scripts/TeaNet/Peers/Peer.cs index c48dc64..943a7b6 100644 --- a/Assets/Scripts/TeaNet/Peers/Peer.cs +++ b/Assets/Scripts/TeaNet/Peers/Peer.cs @@ -6,27 +6,53 @@ using System.Net.Sockets; using NeonTea.Quakeball.TeaNet.Packets; namespace NeonTea.Quakeball.TeaNet.Peers { + /// Main class for networking. Remember to register a protocol before using. public class Peer : PeerMessageListener { + /// Underlying UdpClient. Do not touch unless you know what you are doing. public UdpClient UdpClient { get; private set; } + + /// The fingerprint for networking. Used to make sure incoming bytes are from a correct source. public byte[] Fingerprint { get; private set; } + /// Shorthand for ConnectionManager.Timeout: The amount of milliseconds before a connection is timed out. + public long Timeout { + get { + return ConnectionManager.Timeout; + } + set { + ConnectionManager.Timeout = value; + } + } + /// Shorthand for ConnectionManager.Interval: The interval of updates and rate of re-sending reliable messages. + public long UpdateInterval { + get { + return ConnectionManager.Interval; + } + set { + ConnectionManager.Interval = value; + } + } public ListenerThread ListenerThread; public ConnectionManager ConnectionManager; public Dictionary RegisteredProtocols = new Dictionary(); + /// Listener for messages and errors from within the Peer. public PeerMessageListener MessageListener; + /// Creates a new Peer with the given fingerprint. The fingerprint can be anything, it just must be same on both peers. public Peer(byte[] fingerprint) { Fingerprint = fingerprint; ConnectionManager = new ConnectionManager(this); MessageListener = this; } + /// Starts the UdpClient, but does no networking as is. public void Start(int sending_port) { UdpClient = new UdpClient(sending_port); MessageListener.Message("UdpClient Started"); } + /// Abruptly stops the UdpClient and all relevant threads. public void Stop() { ConnectionManager.StopThread(); if (ListenerThread != null) { @@ -36,6 +62,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { UdpClient.Close(); } + /// Start listening to a given address and port. Usually 0.0.0.0, 0 for clients and 0.0.0.0, port for servers public void StartListen(string address, int port) { IPEndPoint endpoint = new IPEndPoint(FindAddress(address), port); StartListen(endpoint); @@ -50,6 +77,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { MessageListener.Message($"Started listening to {endpoint}"); } + /// Connect to a remote host. Will initialize a listener to 0.0.0.0:0 if no listener is started. public void Connect(string address, int port, byte protocolIdent, bool startListening = true) { if (startListening) { IPEndPoint listenEndpoint = (IPEndPoint)UdpClient.Client.LocalEndPoint; @@ -60,23 +88,28 @@ namespace NeonTea.Quakeball.TeaNet.Peers { MessageListener.Message($"Connecting to {endpoint}"); } + /// Send a reliable packet, meaning it will reliably be delivered. public void SendReliable(ulong uid, Packet packet) { ConnectionManager.AddPacketToQueue(uid, packet); ConnectionManager.SendPacketQueue(uid); } + /// Add reliable packet to queue, so that it will be sent on the next update. public void SendReliableLater(ulong uid, Packet packet) { ConnectionManager.AddPacketToQueue(uid, packet); } + /// Send an unreliable packet, meaning its delivery is not reliable. public void SendUnreliable(ulong uid, Packet packet) { ConnectionManager.SendSingleUnreliable(uid, packet); } + /// Get a Connection instance from the given uid, if such exists. Null otherwise. public Connection GetConnection(ulong uid) { return ConnectionManager.GetConnection(uid); } + /// Register a given protocol. Returns protocol.Identifier if successful, 0 otherwise. public byte RegisterProtocol(Protocol protocol) { byte ident = protocol.Identifier; if (RegisteredProtocols.ContainsKey(ident)) { @@ -87,6 +120,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { return ident; } + /// Get protocol instance from the given identifier, if such exists. public Protocol GetProtocol(byte ident) { if (RegisteredProtocols.ContainsKey(ident)) { return RegisteredProtocols[ident]; @@ -97,7 +131,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { public void Message(string msg) { } public void Err(string msg) { } - public IPAddress FindAddress(string host) { + private IPAddress FindAddress(string host) { IPAddress addr; try { addr = Dns.GetHostAddresses(host)[0]; @@ -108,6 +142,7 @@ namespace NeonTea.Quakeball.TeaNet.Peers { } } + /// Listener for messages and errors from the Peer. public interface PeerMessageListener { void Message(string msg); void Err(string msg);