diff --git a/Assets/Scripts/Net/CanvasInput.cs b/Assets/Scripts/Net/CanvasInput.cs index 49551ee..b2a05b3 100644 --- a/Assets/Scripts/Net/CanvasInput.cs +++ b/Assets/Scripts/Net/CanvasInput.cs @@ -1,30 +1,45 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; -using NeonTea.Quakeball.Net.Peers; +using NeonTea.Quakeball.TeaNet.Peers; +using NeonTea.Quakeball.Net.Packets; +using System; namespace NeonTea.Quakeball.Net { public class CanvasInput : MonoBehaviour, PeerMessageListener { public Button Host; public Button Join; public Button Stop; + public Button Send; public InputField HostAddr; public InputField Port; + public InputField MessageField; public Text TextField; private static List Stuff = new List(); void Start() { Host.onClick.AddListener(() => { - Net.Singleton.StartServer("0.0.0.0", 8080, this); + string addr = String.IsNullOrWhiteSpace(HostAddr.text) ? "0.0.0.0" : HostAddr.text; + string port = String.IsNullOrWhiteSpace(Port.text) ? "8080" : Port.text; + Debug.Log(addr); + Net.Singleton.StartServer(addr, Int32.Parse(port), this); }); Join.onClick.AddListener(() => { - Net.Singleton.StartClient("127.0.0.1", 8080, this); + string addr = String.IsNullOrWhiteSpace(HostAddr.text) ? "127.0.0.1" : HostAddr.text; + string port = String.IsNullOrWhiteSpace(Port.text) ? "8080" : Port.text; + Net.Singleton.StartClient(addr, Int32.Parse(port), this); }); Stop.onClick.AddListener(() => { Net.Singleton.Stop(); }); + Send.onClick.AddListener(() => { + if (Net.Singleton.Peer != null && Net.Singleton.Connections.Count > 0) { + HelloPckt pckt = new HelloPckt(); + pckt.Text = MessageField.text; + Net.Singleton.Peer.SendReliable(Net.Singleton.Connections[0], pckt); + } + }); } void Update() { @@ -33,7 +48,7 @@ namespace NeonTea.Quakeball.Net { public void Message(string text) { Stuff.Add(text); - Debug.Log(string.Join(", ", Stuff.ToArray())); + Debug.Log(text); } diff --git a/Assets/Scripts/Net/Net.cs b/Assets/Scripts/Net/Net.cs index 27a09d9..619863b 100644 --- a/Assets/Scripts/Net/Net.cs +++ b/Assets/Scripts/Net/Net.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; -using System; -using UnityEngine; -using NeonTea.Quakeball.Net.Peers; -using System.Threading; +using UnityEngine; +using NeonTea.Quakeball.TeaNet.Peers; +using System.Collections.Generic; namespace NeonTea.Quakeball.Net { @@ -11,6 +9,9 @@ namespace NeonTea.Quakeball.Net { private static byte[] FP = new byte[] { 0xFF, 0xF7 }; public Peer Peer; + public List Connections = new List(); + + public bool IsServer = false; public void StartClient(string address, int port, PeerMessageListener listener) { if (Peer != null) { @@ -29,6 +30,7 @@ namespace NeonTea.Quakeball.Net { Debug.Log("Can not start multiple endpoints at once! Use Server if multiple connections are required."); return; } + IsServer = true; Peer = new Peer(FP); Peer.MessageListener = listener; Peer.Start(port); diff --git a/Assets/Scripts/Net/Packets.meta b/Assets/Scripts/Net/Packets.meta index f25d4d3..377738d 100644 --- a/Assets/Scripts/Net/Packets.meta +++ b/Assets/Scripts/Net/Packets.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 195df611d888d7248a7d4d22f10dfd71 +guid: adeff8cbe20fc50429024e6798ae38ef folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Scripts/Net/Packets/HelloPckt.cs b/Assets/Scripts/Net/Packets/HelloPckt.cs new file mode 100644 index 0000000..f7f3b91 --- /dev/null +++ b/Assets/Scripts/Net/Packets/HelloPckt.cs @@ -0,0 +1,17 @@ + +using NeonTea.Quakeball.TeaNet.Packets; + +namespace NeonTea.Quakeball.Net.Packets { + public class HelloPckt : Packet { + + public string Text; + + public override void Read(ByteBuffer buffer) { + Text = buffer.ReadString(); + } + + public override void Write(ByteBuffer buffer) { + buffer.WriteString(Text); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Net/Packets/HelloPckt.cs.meta b/Assets/Scripts/Net/Packets/HelloPckt.cs.meta new file mode 100644 index 0000000..37be3c3 --- /dev/null +++ b/Assets/Scripts/Net/Packets/HelloPckt.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3990dcb6938054946ad2a33e57d2aaa7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/TestProtocol.cs b/Assets/Scripts/Net/TestProtocol.cs index ba69641..fe64d9e 100644 --- a/Assets/Scripts/Net/TestProtocol.cs +++ b/Assets/Scripts/Net/TestProtocol.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System; using UnityEngine; -using NeonTea.Quakeball.Net.Peers; -using NeonTea.Quakeball.Net; +using NeonTea.Quakeball.TeaNet.Peers; +using NeonTea.Quakeball.TeaNet.Packets; using NeonTea.Quakeball.Net.Packets; namespace NeonTea.Quakeball.Net { @@ -10,14 +10,26 @@ namespace NeonTea.Quakeball.Net { public class TestProtocol : Protocol { public override byte Identifier => 0x7A; - public override string Version => "0.0.1"; + public override string Version => "0.0.2"; + + public TestProtocol() { + RegisterPacket(typeof(HelloPckt)); + } public override void ConnectionStatusChanged(ConnectionStatus oldStatus, ConnectionStatus newStatus, Connection conn) { Peer.MessageListener.Message($"Connection Status Changed into {newStatus.ToString()} for {conn.Endpoint}"); + if (newStatus == ConnectionStatus.Ready && !Net.Singleton.Connections.Contains(conn)) { + Net.Singleton.Connections.Add(conn); + } else if (newStatus == ConnectionStatus.Closed) { + Net.Singleton.Peer.MessageListener.Message($"Conncection closed: {conn.ClosingReason}"); + } } public override void Receive(Connection conn, Packet packet) { - Peer.MessageListener.Message($"Received packet from {conn.Endpoint}"); + if (packet is HelloPckt) { + HelloPckt Hello = (HelloPckt)packet; + Peer.MessageListener.Message($"Received HelloPckt: {Hello.Text}"); + } } public override void Timeout(Connection conn) { diff --git a/Assets/Scripts/TeaNet.meta b/Assets/Scripts/TeaNet.meta new file mode 100644 index 0000000..097cef2 --- /dev/null +++ b/Assets/Scripts/TeaNet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fc8812ec471e62b4f9c75b88e23ce48b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/TeaNet/Packets.meta b/Assets/Scripts/TeaNet/Packets.meta new file mode 100644 index 0000000..f25d4d3 --- /dev/null +++ b/Assets/Scripts/TeaNet/Packets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 195df611d888d7248a7d4d22f10dfd71 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Packets/Packet.cs b/Assets/Scripts/TeaNet/Packets/Packet.cs similarity index 70% rename from Assets/Scripts/Net/Packets/Packet.cs rename to Assets/Scripts/TeaNet/Packets/Packet.cs index 3842e82..6095ce1 100644 --- a/Assets/Scripts/Net/Packets/Packet.cs +++ b/Assets/Scripts/TeaNet/Packets/Packet.cs @@ -2,12 +2,23 @@ using System.Collections.Generic; using System; using System.Text; -namespace NeonTea.Quakeball.Net.Packets { +namespace NeonTea.Quakeball.TeaNet.Packets { public abstract class Packet { - public int id; + public bool Reliable = true; + public int Id; public abstract void Write(ByteBuffer buffer); public abstract void Read(ByteBuffer buffer); + + public void ReadMeta(ByteBuffer buffer) { + Id = buffer.ReadInt(); + Reliable = buffer.ReadBool(); + } + + public void WriteMeta(ByteBuffer buffer) { + buffer.WriteInt(Id); + buffer.WriteBool(Reliable); + } } public enum PacketStage { @@ -19,9 +30,9 @@ namespace NeonTea.Quakeball.Net.Packets { public enum ClosingReason { Unknown = 0, + IncorrectVersion = 1, } - public class ByteBuffer { private List Bytes; private int pos = 0; @@ -42,6 +53,10 @@ namespace NeonTea.Quakeball.Net.Packets { return pos < Bytes.Count; } + public bool ReadBool() { + return Read() == 1; + } + public int ReadInt() { return BitConverter.ToInt32(Read(4), 0); } @@ -62,6 +77,10 @@ namespace NeonTea.Quakeball.Net.Packets { return Bytes[pos++]; } + public void WriteBool(bool b) { + Write(b ? (byte)0b1 : (byte)0b0); + } + public void WriteInt(int i) { Bytes.AddRange(BitConverter.GetBytes(i)); } @@ -110,9 +129,27 @@ namespace NeonTea.Quakeball.Net.Packets { case 0: reason = ClosingReason.Unknown; break; + case 1: + reason = ClosingReason.IncorrectVersion; + break; } return reason; } + + public void WritePacket(Protocol protocol, Packet p) { + WriteInt(protocol.GetPacketTypeID(p)); + p.WriteMeta(this); + p.Write(this); + } + + public Packet ReadPacket(Protocol protocol) { + int packetType = ReadInt(); + Type t = protocol.GetPacketType(packetType); + Packet p = (Packet)Activator.CreateInstance(t); + p.ReadMeta(this); + p.Read(this); + return p; + } } } \ No newline at end of file diff --git a/Assets/Scripts/Net/Packets/Packet.cs.meta b/Assets/Scripts/TeaNet/Packets/Packet.cs.meta similarity index 100% rename from Assets/Scripts/Net/Packets/Packet.cs.meta rename to Assets/Scripts/TeaNet/Packets/Packet.cs.meta diff --git a/Assets/Scripts/Net/Packets/Protocol.cs b/Assets/Scripts/TeaNet/Packets/Protocol.cs similarity index 89% rename from Assets/Scripts/Net/Packets/Protocol.cs rename to Assets/Scripts/TeaNet/Packets/Protocol.cs index 0d84cf9..1709601 100644 --- a/Assets/Scripts/Net/Packets/Protocol.cs +++ b/Assets/Scripts/TeaNet/Packets/Protocol.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System; -using NeonTea.Quakeball.Net.Peers; +using NeonTea.Quakeball.TeaNet.Peers; -namespace NeonTea.Quakeball.Net.Packets { +namespace NeonTea.Quakeball.TeaNet.Packets { public abstract class Protocol { private Dictionary PacketToId = new Dictionary(); private Dictionary IdToPacket = new Dictionary(); @@ -41,7 +41,6 @@ namespace NeonTea.Quakeball.Net.Packets { } buffer.Write(Identifier); if (connection.Status == ConnectionStatus.Establishing) { - Peer.MessageListener.Message("Sending Establishing"); buffer.Write((byte)PacketStage.Establishing); buffer.WriteString(Version); } else if (connection.Status == ConnectionStatus.Closed) { @@ -51,12 +50,12 @@ namespace NeonTea.Quakeball.Net.Packets { buffer.Write((byte)connection.ClosingReason); } else if (connection.Status == ConnectionStatus.Ready) { buffer.Write((byte)PacketStage.Ready); - buffer.WriteInt(connection.LatestPacketReceived); + buffer.WriteInt(connection.LatestInwardReliable); } return buffer; } - public int GetPacketID(Packet packet) { + public int GetPacketTypeID(Packet packet) { return PacketToId[packet.GetType()]; } diff --git a/Assets/Scripts/Net/Packets/Protocol.cs.meta b/Assets/Scripts/TeaNet/Packets/Protocol.cs.meta similarity index 100% rename from Assets/Scripts/Net/Packets/Protocol.cs.meta rename to Assets/Scripts/TeaNet/Packets/Protocol.cs.meta diff --git a/Assets/Scripts/Net/Peers.meta b/Assets/Scripts/TeaNet/Peers.meta similarity index 100% rename from Assets/Scripts/Net/Peers.meta rename to Assets/Scripts/TeaNet/Peers.meta diff --git a/Assets/Scripts/Net/Connection.cs b/Assets/Scripts/TeaNet/Peers/Connection.cs similarity index 57% rename from Assets/Scripts/Net/Connection.cs rename to Assets/Scripts/TeaNet/Peers/Connection.cs index 5471b8b..8b4da32 100644 --- a/Assets/Scripts/Net/Connection.cs +++ b/Assets/Scripts/TeaNet/Peers/Connection.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Net; using System; -using NeonTea.Quakeball.Net.Packets; +using NeonTea.Quakeball.TeaNet.Packets; -namespace NeonTea.Quakeball.Net { +namespace NeonTea.Quakeball.TeaNet.Peers { public class Connection { public IPEndPoint Endpoint; @@ -14,9 +14,13 @@ namespace NeonTea.Quakeball.Net { public ClosingReason ClosingReason; public long LastMessage; - public int LatestPacketSent; // Last Packet ID the connection has told us they have - public int LatestPacketReceived; // Last Packet ID we've received from the connection - public int PacketIDCounter; // Packet ID counter for packets we're sending them + public int LatestOutwardReliable = -1; // Last reliable Packet ID the connection has told us they have + public int LatestOutwardUnreliable = -1; // Last unreliablePacket ID the connection has told us they have + public int LatestInwardReliable = -1; // Last reliable Packet ID we've received from the connection + public int LatestInwardUnreliable = -1; // Last unreliable Packet ID we've received from the connection + + 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 public Connection(IPEndPoint endpoint, ConnectionStatus status = ConnectionStatus.Establishing) { Endpoint = endpoint; diff --git a/Assets/Scripts/Net/Connection.cs.meta b/Assets/Scripts/TeaNet/Peers/Connection.cs.meta similarity index 83% rename from Assets/Scripts/Net/Connection.cs.meta rename to Assets/Scripts/TeaNet/Peers/Connection.cs.meta index 7e17d48..082184b 100644 --- a/Assets/Scripts/Net/Connection.cs.meta +++ b/Assets/Scripts/TeaNet/Peers/Connection.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bee661c56fae81b4885405c7da9fbb08 +guid: 560e96a51f4b686409986c4ea873941a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Scripts/Net/Peers/ConnectionManager.cs b/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs similarity index 84% rename from Assets/Scripts/Net/Peers/ConnectionManager.cs rename to Assets/Scripts/TeaNet/Peers/ConnectionManager.cs index feb40be..c59a3f4 100644 --- a/Assets/Scripts/Net/Peers/ConnectionManager.cs +++ b/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Net; using System.Threading; using System; -using NeonTea.Quakeball.Net.Packets; +using NeonTea.Quakeball.TeaNet.Packets; -namespace NeonTea.Quakeball.Net.Peers { +namespace NeonTea.Quakeball.TeaNet.Peers { public class ConnectionManager { private Dictionary Connections = new Dictionary(); private Dictionary> PacketQueue = new Dictionary>(); @@ -48,7 +48,7 @@ namespace NeonTea.Quakeball.Net.Peers { } public void AddPacketToQueue(Connection conn, Packet p) { - p.id = conn.PacketIDCounter++; + p.Id = conn.ReliablePacketIDCounter++; PacketQueue[conn].Add(p); } @@ -59,20 +59,20 @@ namespace NeonTea.Quakeball.Net.Peers { List list = PacketQueue[conn]; buffer.WriteInt(list.Count); foreach (Packet p in list) { - buffer.WriteInt(protocol.GetPacketID(p)); - p.Write(buffer); + buffer.WritePacket(protocol, p); } Send(conn, buffer); } } - public void SendSinglePacket(Connection conn, Packet p) { + public void SendSingleUnreliable(Connection conn, Packet p) { + p.Id = conn.UnreliablePacketIDCounter++; + p.Reliable = false; 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); + buffer.WritePacket(protocol, p); Send(conn, buffer); } } @@ -108,7 +108,7 @@ namespace NeonTea.Quakeball.Net.Peers { string version = buffer.ReadString(); if (protocol == null || !version.Equals(protocol.Version)) { conn.Status = ConnectionStatus.Rejected; - conn.ClosingReason = ClosingReason.Unknown; + conn.ClosingReason = ClosingReason.IncorrectVersion; } else { conn.Status = ConnectionStatus.Ready; } @@ -125,6 +125,9 @@ namespace NeonTea.Quakeball.Net.Peers { } break; case PacketStage.Closed: + if (conn.Status == ConnectionStatus.Stopped) { + break; + } conn.Status = ConnectionStatus.Stopped; if (protocol != null) { protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn); @@ -134,23 +137,28 @@ namespace NeonTea.Quakeball.Net.Peers { if (conn.AssignedProtocol != protocolId || protocol == null) { break; } - if (oldStatus == ConnectionStatus.Establishing) { + if (oldStatus == ConnectionStatus.Establishing) { // Update connection status conn.Status = ConnectionStatus.Ready; protocol.ConnectionStatusChanged(oldStatus, conn.Status, conn); } - conn.LatestPacketSent = buffer.ReadInt(); + conn.LatestOutwardReliable = buffer.ReadInt(); + List list = PacketQueue[conn]; - list.RemoveAll(p => p.id <= conn.LatestPacketSent); + list.RemoveAll(p => p.Id <= conn.LatestOutwardReliable); 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); + Packet p = buffer.ReadPacket(protocol); + if (p.Reliable) { + if (p.Id > conn.LatestInwardReliable) { + conn.LatestInwardReliable = p.Id; + protocol.Receive(conn, p); + } + } else if (p.Id > conn.LatestInwardUnreliable) { + conn.LatestInwardUnreliable = p.Id; + protocol.Receive(conn, p); + } } break; } @@ -190,7 +198,7 @@ namespace NeonTea.Quakeball.Net.Peers { Thread.Sleep((int)Interval); } } catch (ThreadAbortException) { - Debug.Log("Connection Thread Stopped"); + Peer.MessageListener.Message("Connection Thread Stopped"); } } } diff --git a/Assets/Scripts/Net/Peers/ConnectionManager.cs.meta b/Assets/Scripts/TeaNet/Peers/ConnectionManager.cs.meta similarity index 100% rename from Assets/Scripts/Net/Peers/ConnectionManager.cs.meta rename to Assets/Scripts/TeaNet/Peers/ConnectionManager.cs.meta diff --git a/Assets/Scripts/Net/Peers/ListenerThread.cs b/Assets/Scripts/TeaNet/Peers/ListenerThread.cs similarity index 92% rename from Assets/Scripts/Net/Peers/ListenerThread.cs rename to Assets/Scripts/TeaNet/Peers/ListenerThread.cs index 7c405d7..e0ba64f 100644 --- a/Assets/Scripts/Net/Peers/ListenerThread.cs +++ b/Assets/Scripts/TeaNet/Peers/ListenerThread.cs @@ -4,9 +4,9 @@ using System; using System.Net.Sockets; using System.Threading; -using NeonTea.Quakeball.Net.Packets; +using NeonTea.Quakeball.TeaNet.Packets; -namespace NeonTea.Quakeball.Net.Peers { +namespace NeonTea.Quakeball.TeaNet.Peers { public class ListenerThread { private IPEndPoint EndPoint; private Thread Thread; @@ -37,7 +37,6 @@ namespace NeonTea.Quakeball.Net.Peers { if (Thread == null) { return false; } - Debug.Log("Stopping ListenerThread!"); Thread.Abort(); return true; } @@ -66,7 +65,7 @@ namespace NeonTea.Quakeball.Net.Peers { } } } catch (ThreadAbortException) { - Debug.Log("Listener Thread stopped"); + Peer.MessageListener.Message("Listener Thread stopped"); } } } diff --git a/Assets/Scripts/Net/Peers/ListenerThread.cs.meta b/Assets/Scripts/TeaNet/Peers/ListenerThread.cs.meta similarity index 100% rename from Assets/Scripts/Net/Peers/ListenerThread.cs.meta rename to Assets/Scripts/TeaNet/Peers/ListenerThread.cs.meta diff --git a/Assets/Scripts/Net/Peers/Peer.cs b/Assets/Scripts/TeaNet/Peers/Peer.cs similarity index 84% rename from Assets/Scripts/Net/Peers/Peer.cs rename to Assets/Scripts/TeaNet/Peers/Peer.cs index 67caf9a..de2ab39 100644 --- a/Assets/Scripts/Net/Peers/Peer.cs +++ b/Assets/Scripts/TeaNet/Peers/Peer.cs @@ -3,12 +3,9 @@ using UnityEngine; using System; using System.Net; using System.Net.Sockets; -using System.Threading; -using NeonTea.Quakeball.Net.Packets; -using System.Threading.Tasks; -using System.Runtime.Remoting; +using NeonTea.Quakeball.TeaNet.Packets; -namespace NeonTea.Quakeball.Net.Peers { +namespace NeonTea.Quakeball.TeaNet.Peers { public class Peer : PeerMessageListener { public UdpClient UdpClient { get; private set; } public byte[] Fingerprint { get; private set; } @@ -45,6 +42,9 @@ namespace NeonTea.Quakeball.Net.Peers { } 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}"); @@ -58,6 +58,15 @@ namespace NeonTea.Quakeball.Net.Peers { MessageListener.Message($"Connecting to {endpoint}"); } + public void SendReliable(Connection conn, Packet packet) { + ConnectionManager.AddPacketToQueue(conn, packet); + ConnectionManager.SendPacketQueue(conn); + } + + public void SendUnreliable(Connection conn, Packet packet) { + ConnectionManager.SendSingleUnreliable(conn, packet); + } + public byte RegisterProtocol(Protocol protocol) { byte ident = protocol.Identifier; if (RegisteredProtocols.ContainsKey(ident)) { diff --git a/Assets/Scripts/Net/Peers/Peer.cs.meta b/Assets/Scripts/TeaNet/Peers/Peer.cs.meta similarity index 100% rename from Assets/Scripts/Net/Peers/Peer.cs.meta rename to Assets/Scripts/TeaNet/Peers/Peer.cs.meta