diff --git a/Assets/Scripts.meta b/Assets/Scripts.meta new file mode 100644 index 0000000..16c905c --- /dev/null +++ b/Assets/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 06f737a2dd3e1214a9e33c689e945955 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net.meta b/Assets/Scripts/Net.meta new file mode 100644 index 0000000..34d5379 --- /dev/null +++ b/Assets/Scripts/Net.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b2b07bcc084a8f449d46fad84fb9149 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/CanvasInput.cs b/Assets/Scripts/Net/CanvasInput.cs new file mode 100644 index 0000000..66acd52 --- /dev/null +++ b/Assets/Scripts/Net/CanvasInput.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using NeonTea.Quakeball.Net.Endpoint; + +namespace NeonTea.Quakeball.Net { + public class CanvasInput : MonoBehaviour { + + public Button Host; + public Button Join; + public InputField HostAddr; + public InputField Port; + + void Start() { + Host.onClick.AddListener(() => { + //Destroy(Join.gameObject); + //Host.interactable = false; + Network.Singleton.Start(new Server(), "0.0.0.0", 8080); + }); + Join.onClick.AddListener(() => { + //Destroy(Host.gameObject); + //Join.interactable = false; + Network.Singleton.Start(new Client(), "127.0.0.1", 8080); + }); + } + + + } +} \ No newline at end of file diff --git a/Assets/Scripts/Net/CanvasInput.cs.meta b/Assets/Scripts/Net/CanvasInput.cs.meta new file mode 100644 index 0000000..3e06335 --- /dev/null +++ b/Assets/Scripts/Net/CanvasInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c88f7b0d79ee6d40adc7ee64150d47e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Connection.cs b/Assets/Scripts/Net/Connection.cs new file mode 100644 index 0000000..4abba3f --- /dev/null +++ b/Assets/Scripts/Net/Connection.cs @@ -0,0 +1,17 @@ +using System.Collections; +using System.Collections.Generic; +using System.Net; + +namespace NeonTea.Quakeball.Net { + public class Connection { + + public IPEndPoint Endpoint; + public uint uuid; + public bool ShouldExist = true; + + public Connection(IPEndPoint endpoint, uint uuid) { + this.Endpoint = endpoint; + this.uuid = uuid; + } + } +} diff --git a/Assets/Scripts/Net/Connection.cs.meta b/Assets/Scripts/Net/Connection.cs.meta new file mode 100644 index 0000000..7e17d48 --- /dev/null +++ b/Assets/Scripts/Net/Connection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bee661c56fae81b4885405c7da9fbb08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Endpoint.meta b/Assets/Scripts/Net/Endpoint.meta new file mode 100644 index 0000000..18331f8 --- /dev/null +++ b/Assets/Scripts/Net/Endpoint.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d5b2ed44f40cc940bae2d8e4c868bf4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Endpoint/Client.cs b/Assets/Scripts/Net/Endpoint/Client.cs new file mode 100644 index 0000000..283cafb --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Client.cs @@ -0,0 +1,46 @@ +using UnityEngine; +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace NeonTea.Quakeball.Net.Endpoint { + public class Client : AbstractEndpoint { + public override UdpClient UdpClient { + get { return InnerClient; } + } + + private Connection Connection; + private UdpClient InnerClient; + + public override void Start(string host, int port) { + InnerClient = new UdpClient(0); + + try { + Connection = new Connection(new IPEndPoint(FindAddress(host), port), 0); + } catch (Exception e) { + Debug.Log($"Failed to create initial connection: {e.ToString()}"); + Network.Singleton.Stop(); + } + StartListen(Connection); + SendBytes(Encoding.UTF8.GetBytes("Hello! This is testing.")); + + Debug.Log($"Client started at {host}:{port}!"); + } + + public void OnStop() { + Connection.ShouldExist = false; + } + + public override void ConnectionClosed(Connection conn, ClosingReason reason) { + if (Connection == conn) { // Make sure the closed connection is this one + Debug.Log($"Closing client. Connection was closed: {reason.Description}"); + Network.Singleton.Stop(); + } + } + + private void SendBytes(Byte[] bytes) { + UdpClient.Send(bytes, bytes.Length, Connection.Endpoint); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Net/Endpoint/Client.cs.meta b/Assets/Scripts/Net/Endpoint/Client.cs.meta new file mode 100644 index 0000000..b58c363 --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Client.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffeef21f11f5f864f9213c2822a05d38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Endpoint/Endpoint.cs b/Assets/Scripts/Net/Endpoint/Endpoint.cs new file mode 100644 index 0000000..b1088c4 --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Endpoint.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using UnityEngine; +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Text; + +namespace NeonTea.Quakeball.Net.Endpoint { + public abstract class AbstractEndpoint { + private Dictionary ListenThreads = new Dictionary(); + + public abstract void Start(string host, int port); + public abstract UdpClient UdpClient { get; } + + public virtual void ConnectionClosed(Connection conn, ClosingReason reason) { + } + + public void Stop() { + UdpClient.Close(); + foreach (Thread thread in ListenThreads.Values) { + thread.Abort(); + } + } + + protected IPAddress FindAddress(string host) { + IPAddress addr; + try { + addr = Dns.GetHostAddresses(host)[0]; + } catch (ArgumentException) { + addr = IPAddress.Parse(host); + } + return addr; + } + + protected bool StartListen(Connection connection) { + if (ListenThreads.ContainsKey(connection.uuid)) { + return false; + } + Thread t = new Thread(ListenThread); + t.Start(connection); + ListenThreads.Add(connection.uuid, t); + return true; + } + + private void ListenThread(object obj) { + Connection listened; + try { + listened = (Connection)obj; + } catch (InvalidCastException) { + Debug.Log($"Can not cast {obj} to a Connection"); + return; + } + while (listened.ShouldExist) { + try { + Byte[] Received = UdpClient.Receive(ref listened.Endpoint); + Debug.Log(Encoding.UTF8.GetString(Received)); + } catch (Exception e) { + Debug.Log($"Error listening to connection, closing connection: {e.ToString()}"); + listened.ShouldExist = false; + ConnectionClosed(listened, new ClosingReason(Reason.LISTENING_ERROR, e.ToString())); + } + } + } + } +} + +public struct ClosingReason { + public Reason Reason; + public string Description; + + public ClosingReason(Reason reason, string description) { + Reason = reason; + Description = description; + } +} + +public enum Reason { + LISTENING_ERROR +} \ No newline at end of file diff --git a/Assets/Scripts/Net/Endpoint/Endpoint.cs.meta b/Assets/Scripts/Net/Endpoint/Endpoint.cs.meta new file mode 100644 index 0000000..cadb89e --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Endpoint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de661f9bd7cfa694db4111a798643dd3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/Endpoint/Server.cs b/Assets/Scripts/Net/Endpoint/Server.cs new file mode 100644 index 0000000..2ff8708 --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Server.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using UnityEngine; +using System; +using System.Net; +using System.Net.Sockets; + +namespace NeonTea.Quakeball.Net.Endpoint { + public class Server : AbstractEndpoint { + public override UdpClient UdpClient { + get { return InnerClient; } + } + + private static uint ConnectionUUIDCounter; + private Dictionary Connections = new Dictionary(); + private UdpClient InnerClient; + + public override void Start(string host, int port) { + InnerClient = new UdpClient(port); + + Connection conn; + try { + conn = AddConnection(new IPEndPoint(FindAddress(host), port), uint.MaxValue); + } catch (Exception e) { + Debug.Log($"Failed to create initial connection: {e.ToString()}"); + Network.Singleton.Stop(); + return; + } + + StartListen(conn); + + Debug.Log($"Server started at {host}:{port}!"); + } + + private Connection AddConnection(IPEndPoint endpoint, uint uuid = 0) { + if (uuid == 0) { + uuid = ConnectionUUIDCounter++; + } + Connection conn = new Connection(endpoint, uuid); + Connections.Add(uuid, conn); + return conn; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Net/Endpoint/Server.cs.meta b/Assets/Scripts/Net/Endpoint/Server.cs.meta new file mode 100644 index 0000000..7eceb8b --- /dev/null +++ b/Assets/Scripts/Net/Endpoint/Server.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08642b59bb65292409a46c688efbf134 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Net/NetworkHandler.cs b/Assets/Scripts/Net/NetworkHandler.cs new file mode 100644 index 0000000..785941d --- /dev/null +++ b/Assets/Scripts/Net/NetworkHandler.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using NeonTea.Quakeball.Net.Endpoint; +using System.Threading; + +namespace NeonTea.Quakeball.Net { + + public class Network { + public static Network Singleton = new Network(); + + private AbstractEndpoint Endpoint; + + public void Start(AbstractEndpoint endpoint, string host, int port) { + if (Endpoint != null) { + Debug.Log("Can not start multiple endpoints at once! Use Server if multiple connections are required."); + return; + } + Endpoint = endpoint; + Endpoint.Start(host, port); + } + + public void Stop() { + Endpoint.Stop(); + Endpoint = null; + } + } +} diff --git a/Assets/Scripts/Net/NetworkHandler.cs.meta b/Assets/Scripts/Net/NetworkHandler.cs.meta new file mode 100644 index 0000000..259e5ed --- /dev/null +++ b/Assets/Scripts/Net/NetworkHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8773e20fdc6d1842848081eaf595ad5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: