diff --git a/MLAPI/Data/NetworkedClient.cs b/MLAPI/Data/NetworkedClient.cs new file mode 100644 index 0000000..43e3f1a --- /dev/null +++ b/MLAPI/Data/NetworkedClient.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace MLAPI +{ + public class NetworkedClient + { + public int ClientId; + public GameObject PlayerObject; + } +} diff --git a/MLAPI/Data/NetworkingConfiguration.cs b/MLAPI/Data/NetworkingConfiguration.cs new file mode 100644 index 0000000..9588670 --- /dev/null +++ b/MLAPI/Data/NetworkingConfiguration.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using UnityEngine.Networking; + +namespace MLAPI +{ + public class NetworkingConfiguration + { + public ushort ProtocolVersion = 0; + public Dictionary Channels = new Dictionary(); + public List MessageTypes = new List(); + public int MessageBufferSize = 65536; + public int MaxMessagesPerFrame = 150; + public int MaxConnections = 100; + public int Port = 7777; + public string Address; + public int ClientConnectionBufferTimeout = 10; + public bool ConnectionApproval = false; + public Action> ConnectionApprovalCallback; + public byte[] ConnectionData; + public bool HandleObjectSpawning = true; + //TODO + public bool CompressMessages = false; + //Should only be used for dedicated servers and will require the servers RSA keypair being hard coded into clients in order to exchange a AES key + //TODO + public bool EncryptMessages = false; + + + //Cached config hash + private byte[] ConfigHash = null; + public byte[] GetConfig(bool cache = true) + { + if (ConfigHash != null && cache) + return ConfigHash; + + using(MemoryStream writeStream = new MemoryStream()) + { + using(BinaryWriter writer = new BinaryWriter(writeStream)) + { + writer.Write(ProtocolVersion); + foreach (KeyValuePair pair in Channels) + { + writer.Write(pair.Key); + writer.Write((int)pair.Value); + } + for (int i = 0; i < MessageTypes.Count; i++) + { + writer.Write(MessageTypes[i]); + } + writer.Write(HandleObjectSpawning); + writer.Write(CompressMessages); + writer.Write(EncryptMessages); + } + using(SHA256Managed sha256 = new SHA256Managed()) + { + //Returns a 256 bit / 32 byte long checksum of the config + if (cache) + { + ConfigHash = sha256.ComputeHash(writeStream); + return ConfigHash; + } + return sha256.ComputeHash(writeStream); + } + } + } + + public bool CompareConfig(byte[] hash) + { + return hash == GetConfig(); + } + } +} diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 0e4499c..ba5e30f 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -35,20 +35,18 @@ - - ..\..\..\..\..\..\Program Files\Unity\Editor\Data\Managed\UnityEngine.dll - - - - - + + + + + diff --git a/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs b/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs new file mode 100644 index 0000000..6bf7047 --- /dev/null +++ b/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace MLAPI +{ + public abstract class NetworkedBehaviour : MonoBehaviour + { + protected bool isLocalPlayer + { + get + { + return networkedObject.isLocalPlayer; + } + } + protected bool isServer = NetworkingManager.singleton.isServer; + protected NetworkedObject networkedObject + { + get + { + if(_networkedObject == null) + { + _networkedObject = GetComponentInParent(); + } + return _networkedObject; + } + } + private NetworkedObject _networkedObject = null; + protected uint netId + { + get + { + return networkedObject.NetworkId; + } + } + + //Change data type + private Dictionary registeredMessageHandlers = new Dictionary(); + + public int RegisterMessageHandler(string name, Action action) + { + int counter = NetworkingManager.singleton.AddIncomingMessageHandler(name, action); + registeredMessageHandlers.Add(name, counter); + return counter; + } + + public void DeregisterMessageHandler(string name, int counter) + { + NetworkingManager.singleton.RemoveIncomingMessageHandler(name, counter); + } + + private void OnDestroy() + { + foreach (KeyValuePair pair in registeredMessageHandlers) + { + DeregisterMessageHandler(pair.Key, pair.Value); + } + } + + public void Send(int connectionId, string messageType, string channelName, byte[] data) + { + Send(connectionId, messageType, channelName, data); + } + + public void Send(int[] connectonIds, string messageType, string channelName, byte[] data) + { + for (int i = 0; i < connectonIds.Length; i++) + { + Send(connectonIds[i], messageType, channelName, data); + } + } + + public void Send(List connectonIds, string messageType, string channelName, byte[] data) + { + for (int i = 0; i < connectonIds.Count; i++) + { + Send(connectonIds[i], messageType, channelName, data); + } + } + } +} diff --git a/MLAPI/MonoBehaviours/Core/NetworkedObject.cs b/MLAPI/MonoBehaviours/Core/NetworkedObject.cs new file mode 100644 index 0000000..ce4c706 --- /dev/null +++ b/MLAPI/MonoBehaviours/Core/NetworkedObject.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace MLAPI +{ + //TODO + //Will be used for objects which will be spawned automatically across clients + public class NetworkedObject : MonoBehaviour + { + public uint NetworkId; + public int OwnerClientId = -1; + public int SpawnablePrefabId; + internal bool IsPlayerObject = false; + public bool isLocalPlayer + { + get + { + return OwnerClientId == NetworkingManager.singleton.MyClientId; + } + } + + private void OnDestroy() + { + NetworkingManager.OnDestroyObject(NetworkId); + } + } +} diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs new file mode 100644 index 0000000..b1f1c1d --- /dev/null +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -0,0 +1,565 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.SceneManagement; + +namespace MLAPI +{ + public class NetworkingManager : MonoBehaviour + { + public List SpawnablePrefabs; + public GameObject DefaultPlayerPrefab; + public static NetworkingManager singleton; + //Client only, what my connectionId is on the server + public int MyClientId; + private Dictionary connectedClients; + private HashSet pendingClients; + internal bool isServer; + internal bool isClient; + internal bool isHost + { + get + { + return isServer && isClient; + } + } + private bool isListening; + private byte[] messageBuffer; + private Dictionary channels; + private Dictionary messageTypes; + private Dictionary>> messageCallbacks; + private Dictionary messageHandlerCounter; + private Dictionary> releasedMessageHandlerCounters; + private int localConnectionId; + public Scene PlayScene; + public Scene MenuScene; + private Dictionary spawnedObjects; + private List spawnedObjectIds; + private Stack releasedNetworkObjectIds; + private uint networkObjectIdCounter; + private uint GetNetworkObjectId() + { + if (releasedNetworkObjectIds.Count > 0) + { + return releasedNetworkObjectIds.Pop(); + } + else + { + networkObjectIdCounter++; + return networkObjectIdCounter; + } + } + + + public NetworkingConfiguration NetworkConfig; + + private void OnValidate() + { + for (int i = 0; i < SpawnablePrefabs.Count; i++) + { + if(SpawnablePrefabs[i].GetComponentInChildren() == null) + { + Debug.LogWarning("MLAPI: All SpawnablePrefabs need a NetworkedObject component. Please add one to the prefab " + SpawnablePrefabs[i].gameObject.name); + } + } + } + + internal GameObject SpawnObject(int spawnablePrefabIndex, uint networkId, int ownerId) + { + GameObject go = Instantiate(singleton.SpawnablePrefabs[spawnablePrefabIndex]); + NetworkedObject netObject = go.GetComponent(); + if(netObject == null) + { + Debug.LogWarning("MLAPI: Please add a NetworkedObject component to the root of all spawnable objects"); + netObject = go.AddComponent(); + } + netObject.SpawnablePrefabId = spawnablePrefabIndex; + if(singleton.isServer) + { + netObject.NetworkId = singleton.GetNetworkObjectId(); + } + else + { + netObject.NetworkId = networkId; + } + netObject.OwnerClientId = ownerId; + + singleton.spawnedObjectIds.Add(netObject.NetworkId); + singleton.spawnedObjects.Add(netObject.NetworkId, netObject); + return go; + } + + internal GameObject SpawnPlayerObject(int connectionId, uint networkId) + { + GameObject go = Instantiate(DefaultPlayerPrefab); + NetworkedObject netObject = go.GetComponent(); + if (netObject == null) + { + Debug.LogWarning("MLAPI: Please add a NetworkedObject component to the root of the player prefab"); + netObject = go.AddComponent(); + } + netObject.OwnerClientId = connectionId; + if(isServer) + { + netObject.NetworkId = singleton.GetNetworkObjectId(); + } + else + { + netObject.NetworkId = networkId; + } + netObject.IsPlayerObject = true; + connectedClients[connectionId].PlayerObject = go; + singleton.spawnedObjectIds.Add(netObject.NetworkId); + singleton.spawnedObjects.Add(netObject.NetworkId, netObject); + return go; + } + + internal static void OnDestroyObject(uint networkId) + { + if (!singleton.isServer) + return; + + NetworkedObject netObject = singleton.spawnedObjects[networkId]; + singleton.releasedNetworkObjectIds.Push(networkId); + } + + internal int AddIncomingMessageHandler(string name, Action action) + { + if(messageTypes.ContainsKey(name)) + { + if(messageCallbacks.ContainsKey(messageTypes[name])) + { + int handlerId = 0; + if (messageHandlerCounter.ContainsKey(messageTypes[name])) + { + if (!releasedMessageHandlerCounters.ContainsKey(messageTypes[name])) + releasedMessageHandlerCounters.Add(messageTypes[name], new Stack()); + + if(releasedMessageHandlerCounters[messageTypes[name]].Count == 0) + { + handlerId = messageHandlerCounter[messageTypes[name]]; + messageHandlerCounter[messageTypes[name]]++; + } + else + { + handlerId = releasedMessageHandlerCounters[messageTypes[name]].Pop(); + } + } + else + { + messageHandlerCounter.Add(messageTypes[name], handlerId + 1); + } + messageCallbacks[messageTypes[name]].Add(handlerId, action); + return handlerId; + } + else + { + messageCallbacks.Add(messageTypes[name], new Dictionary>()); + messageHandlerCounter.Add(messageTypes[name], 1); + messageCallbacks[messageTypes[name]].Add(0, action); + return 0; + } + } + else + { + Debug.LogWarning("MLAPI: The message type " + name + " has not been registered. Please define it in the netConfig"); + return -1; + } + } + + internal void RemoveIncomingMessageHandler(string name, int counter) + { + if (counter == -1) + return; + + if (messageTypes.ContainsKey(name) && messageCallbacks.ContainsKey(messageTypes[name]) && messageCallbacks[messageTypes[name]].ContainsKey(counter)) + { + messageCallbacks[messageTypes[name]].Remove(counter); + releasedMessageHandlerCounters[messageTypes[name]].Push(counter); + } + } + + private ConnectionConfig Init(NetworkingConfiguration netConfig) + { + NetworkConfig = netConfig; + + pendingClients = new HashSet(); + connectedClients = new Dictionary(); + messageBuffer = new byte[NetworkConfig.MessageBufferSize]; + channels = new Dictionary(); + messageTypes = new Dictionary(); + messageCallbacks = new Dictionary>>(); + messageHandlerCounter = new Dictionary(); + releasedMessageHandlerCounters = new Dictionary>(); + spawnedObjects = new Dictionary(); + spawnedObjectIds = new List(); + releasedNetworkObjectIds = new Stack(); + if(NetworkConfig.HandleObjectSpawning) + { + NetworkedObject[] sceneObjects = FindObjectsOfType(); + for (int i = 0; i < sceneObjects.Length; i++) + { + uint networkId = GetNetworkObjectId(); + spawnedObjects.Add(networkId, sceneObjects[i]); + spawnedObjectIds.Add(networkId); + } + } + + if (NetworkConfig.ConnectionApproval) + { + if(NetworkConfig.ConnectionApprovalCallback == null) + { + Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection aproval will timeout"); + } + } + + NetworkTransport.Init(); + ConnectionConfig cConfig = new ConnectionConfig(); + + //MLAPI channels and messageTypes + NetworkConfig.Channels.Add("MLAPI_RELIABLE_FRAGMENTED", QosType.ReliableFragmented); + messageTypes.Add("MLAPI_CONNECTION_REQUEST", 0); + messageTypes.Add("MLAPI_CLIENT_LIST", 1); + //messageTypes.Add("") + + + HashSet channelNames = new HashSet(); + foreach (KeyValuePair pair in NetworkConfig.Channels) + { + if(pair.Key.StartsWith("MLAPI_")) + { + Debug.LogWarning("MLAPI: Channel names are not allowed to start with MLAPI_. This is to prevent name conflicts"); + continue; + } + else if(channelNames.Contains(pair.Key)) + { + Debug.LogWarning("MLAPI: Duplicate channel name: " + pair.Key); + continue; + } + channels.Add(pair.Key, cConfig.AddChannel(pair.Value)); + } + //0-32 are reserved for MLAPI messages + for (ushort i = 32; i < NetworkConfig.MessageTypes.Count; i++) + { + messageTypes.Add(NetworkConfig.MessageTypes[i], i); + } + return cConfig; + } + + + public void StartServer(NetworkingConfiguration netConfig) + { + ConnectionConfig cConfig = Init(netConfig); + HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); + hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port, null); + isServer = true; + isClient = false; + isListening = true; + SceneManager.LoadScene(PlayScene.buildIndex); + } + + public void StartClient(NetworkingConfiguration netConfig) + { + ConnectionConfig cConfig = Init(netConfig); + HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); + hostId = NetworkTransport.AddHost(hostTopology, 0); + + localConnectionId = NetworkTransport.Connect(hostId, NetworkConfig.Address, NetworkConfig.Port, 0, out error); + isServer = false; + isClient = true; + isListening = true; + } + + public void StartHost(NetworkingConfiguration netConfig) + { + ConnectionConfig cConfig = Init(netConfig); + HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); + hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port, null); + isServer = true; + isClient = true; + isListening = true; + connectedClients.Add(-1, new NetworkedClient() { ClientId = -1 }); + SceneManager.LoadScene(PlayScene.buildIndex); + } + + private void OnEnable() + { + if (singleton != null) + { + Debug.LogWarning("MLAPI: Multiple NetworkingManagers"); + return; + } + singleton = this; + DontDestroyOnLoad(gameObject); + } + + private void OnDisable() + { + singleton = null; + } + + //Receive stuff + int hostId; + int connectionId; + int channelId; + int receivedSize; + byte error; + private void Update() + { + if(isListening) + { + NetworkEventType eventType; + int messagesProcessed = 0; + do + { + messagesProcessed++; + eventType = NetworkTransport.Receive(out hostId, out connectionId, out channelId, messageBuffer, NetworkConfig.MessageBufferSize, out receivedSize, out error); + NetworkError networkError = (NetworkError)error; + if (networkError != NetworkError.Ok) + { + Debug.LogWarning("MLAPI: NetworkTransport receive error: " + networkError.ToString()); + return; + } + switch (eventType) + { + case NetworkEventType.ConnectEvent: + if (isServer) + { + pendingClients.Add(connectionId); + StartCoroutine(ApprovalTimeout(connectionId)); + } + else + { + using (MemoryStream writeStream = new MemoryStream()) + { + using (BinaryWriter writer = new BinaryWriter(writeStream)) + { + writer.Write(NetworkConfig.GetConfig()); + if(NetworkConfig.ConnectionApproval) + { + writer.Write(NetworkConfig.ConnectionData); + } + Send(connectionId, "MLAPI_CONNECTION_REQUEST", "MLAPI_RELIABLE_FRAGMENTED", writeStream.ToArray()); + } + } + } + break; + case NetworkEventType.DataEvent: + HandleIncomingData(connectionId, ref messageBuffer); + break; + } + } while (eventType != NetworkEventType.Nothing && + (messagesProcessed < NetworkConfig.MaxMessagesPerFrame || NetworkConfig.MaxMessagesPerFrame < 0)); + + } + } + + IEnumerator ApprovalTimeout(int connectionId) + { + float timeStarted = Time.time; + //We yield every frame incase a pending client disconnects and someone else gets its connection id + while (Time.time - timeStarted < NetworkConfig.ClientConnectionBufferTimeout && pendingClients.Contains(connectionId)) + { + yield return null; + } + if(pendingClients.Contains(connectionId)) + { + //Timeout + DisconnectClient(connectionId); + } + } + + private void HandleIncomingData(int connectonId, ref byte[] data) + { + using(MemoryStream readStream = new MemoryStream(data)) + { + using (BinaryReader reader = new BinaryReader(readStream)) + { + ushort messageType = reader.ReadUInt16(); + + //Client tried to send a network message that was not the connection request before he was accepted. + if (pendingClients.Contains(connectionId) && messageType != 0) + return; + + if (messageType >= 32) + { + //Custom message, invoke all message handlers + foreach (KeyValuePair> pair in messageCallbacks[messageType]) + { + pair.Value(connectionId, reader.ReadBytes(int.MaxValue)); + } + } + else + { + //MLAPI message + switch (messageType) + { + case 0: //Client to server > sends connection buffer + if (isServer) + { + using (MemoryStream messageReadStream = new MemoryStream(reader.ReadBytes(int.MaxValue))) + { + using (BinaryReader messageReader = new BinaryReader(messageReadStream)) + { + byte[] configHash = messageReader.ReadBytes(32); + if (NetworkConfig.CompareConfig(configHash) == false) + { + Debug.LogWarning("MLAPI: NetworkConfiguration missmatch. The configuration between the server and client does not match."); + DisconnectClient(connectionId); + return; + } + if (NetworkConfig.ConnectionApproval) + { + byte[] connectionBuffer = messageReader.ReadBytes(int.MaxValue); + NetworkConfig.ConnectionApprovalCallback(connectionBuffer, connectionId, HandleApproval); + } + else + { + HandleApproval(connectionId, true); + } + } + } + } + break; + case 1: //Server informs client it has been approved: + if (isClient) + { + SceneManager.LoadScene(PlayScene.buildIndex); + using (MemoryStream messageReadStream = new MemoryStream(reader.ReadBytes(int.MaxValue))) + { + using (BinaryReader messageReader = new BinaryReader(messageReadStream)) + { + MyClientId = messageReader.ReadInt32(); + connectedClients.Add(MyClientId, new NetworkedClient() { ClientId = MyClientId }); + int clientCount = messageReader.ReadInt32(); + for (int i = 0; i < clientCount; i++) + { + int conId = messageReader.ReadInt32(); + connectedClients.Add(conId, new NetworkedClient() { ClientId = conId }); + } + if(NetworkConfig.HandleObjectSpawning) + { + int objectCount = messageReader.ReadInt32(); + for (int i = 0; i < objectCount; i++) + { + bool isPlayerObject = messageReader.ReadBoolean(); + uint networkId = messageReader.ReadUInt32(); + int ownerId = messageReader.ReadInt32(); + int prefabId = messageReader.ReadInt32(); + if(isPlayerObject) + { + SpawnPlayerObject(ownerId, networkId); + } + else + { + SpawnObject(prefabId, networkId, ownerId); + } + } + } + } + } + } + break; + } + } + } + } + } + + internal void Send(int connectionId, string messageType, string channelName, byte[] data) + { + using (MemoryStream stream = new MemoryStream()) + { + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write(messageTypes[messageType]); + writer.Write(data); + NetworkTransport.Send(hostId, connectionId, channels[channelName], data, data.Length, out error); + } + } + } + + internal void Send(int[] connectonIds, string messageType, string channelName, byte[] data) + { + for (int i = 0; i < connectonIds.Length; i++) + { + Send(connectonIds[i], messageType, channelName, data); + } + } + + internal void Send(List connectonIds, string messageType, string channelName, byte[] data) + { + for (int i = 0; i < connectonIds.Count; i++) + { + Send(connectonIds[i], messageType, channelName, data); + } + } + + internal void DisconnectClient(int connectionId) + { + if (!isServer) + return; + if (pendingClients.Contains(connectionId)) + pendingClients.Remove(connectionId); + if (connectedClients.ContainsKey(connectionId)) + connectedClients.Remove(connectionId); + NetworkTransport.Disconnect(hostId, connectionId, out error); + + } + + private void HandleApproval(int connectionId, bool approved) + { + if(approved) + { + NetworkedClient client = new NetworkedClient() + { + ClientId = connectionId + }; + connectedClients.Add(connectionId, client); + if(NetworkConfig.HandleObjectSpawning) + { + uint networkId = GetNetworkObjectId(); + GameObject go = SpawnPlayerObject(connectionId, networkId); + connectedClients[connectionId].PlayerObject = go; + } + using (MemoryStream writeStream = new MemoryStream()) + { + using (BinaryWriter writer = new BinaryWriter(writeStream)) + { + writer.Write(connectionId); + writer.Write(connectedClients.Count - 1); + foreach (KeyValuePair item in connectedClients) + { + //Our own ID. Already added as the first one above + if (item.Key == connectionId) + continue; + writer.Write(item.Key); //Connection id + } + if (NetworkConfig.HandleObjectSpawning) + { + writer.Write(spawnedObjectIds.Count); + for (int i = 0; i < spawnedObjectIds.Count; i++) + { + writer.Write(spawnedObjects[spawnedObjectIds[i]].IsPlayerObject); + writer.Write(spawnedObjects[spawnedObjectIds[i]].NetworkId); + writer.Write(spawnedObjects[spawnedObjectIds[i]].OwnerClientId); + writer.Write(spawnedObjects[spawnedObjectIds[i]].SpawnablePrefabId); + } + } + + Send(connectionId, "MLAPI_CLIENT_LIST", "MLAPI_RELIABLE_FRAGMENTED", writeStream.ToArray()); + } + } + } + else + { + if (pendingClients.Contains(connectionId)) + pendingClients.Remove(connectionId); + NetworkTransport.Disconnect(hostId, connectionId, out error); + } + } + } +} \ No newline at end of file diff --git a/MLAPI/NetworkedBehaviour.cs b/MLAPI/NetworkedBehaviour.cs deleted file mode 100644 index 9afa3f3..0000000 --- a/MLAPI/NetworkedBehaviour.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace MLAPI -{ - public abstract class NetworkedBehaviour : MonoBehaviour - { - protected bool isLocalPlayer; - protected bool isServer = NetworkingManager.singleton.isServer; - - //Change data type - private Dictionary registeredMessageHandlers = new Dictionary(); - - public int RegisterMessageHandler(string name, Action action) - { - int counter = NetworkingManager.singleton.AddIncomingMessageHandler(name, action); - registeredMessageHandlers.Add(name, counter); - return counter; - } - - public void DeregisterMessageHandler(string name, int counter) - { - NetworkingManager.singleton.RemoveIncomingMessageHandler(name, counter); - } - - private void OnDestroy() - { - foreach(KeyValuePair pair in registeredMessageHandlers) - { - DeregisterMessageHandler(pair.Key, pair.Value); - } - } - } -} diff --git a/MLAPI/NetworkedClient.cs b/MLAPI/NetworkedClient.cs deleted file mode 100644 index 900604c..0000000 --- a/MLAPI/NetworkedClient.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MLAPI -{ - public class NetworkedClient - { - public int ClientId; - } -} diff --git a/MLAPI/NetworkedObject.cs b/MLAPI/NetworkedObject.cs deleted file mode 100644 index 3ea6d1e..0000000 --- a/MLAPI/NetworkedObject.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UnityEngine; - -namespace MLAPI -{ - //TODO - //Will be used for objects which will be spawned automatically across clients - public class NetworkedObject : MonoBehaviour - { - - } -} diff --git a/MLAPI/NetworkingConfiguration.cs b/MLAPI/NetworkingConfiguration.cs deleted file mode 100644 index 02da1f5..0000000 --- a/MLAPI/NetworkingConfiguration.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine.Networking; - -namespace MLAPI -{ - public class NetworkingConfiguration - { - public ushort ProtocolVersion = 0; - public Dictionary Channels = new Dictionary(); - public List MessageTypes = new List(); - public int MessageBufferSize = 65536; - public int MaxMessagesPerFrame = 150; - public int MaxConnections = 100; - public int Port = 7777; - public int ClientConnectionBufferTimeout = 10; - public bool ConnectionApproval = false; - public Action> ConnectionApprovalCallback; - public byte[] ConnectionData; - } -} diff --git a/MLAPI/NetworkingManager.cs b/MLAPI/NetworkingManager.cs deleted file mode 100644 index 0af3fa3..0000000 --- a/MLAPI/NetworkingManager.cs +++ /dev/null @@ -1,320 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using UnityEngine; -using UnityEngine.Networking; - -namespace MLAPI -{ - public class NetworkingManager : MonoBehaviour - { - public static NetworkingManager singleton; - //Client only, what my connectionId is on the server - public int MyClientId; - //Server only - private Dictionary connectedClients; - private HashSet pendingClients; - internal bool isServer; - private bool isListening; - private byte[] messageBuffer; - private Dictionary channels; - private Dictionary messageTypes; - private Dictionary>> messageCallbacks; - private Dictionary messageHandlerCounter; - private Dictionary> releasedMessageHandlerCounters; - - public NetworkingConfiguration NetworkConfig; - - internal int AddIncomingMessageHandler(string name, Action action) - { - if(messageTypes.ContainsKey(name)) - { - if(messageCallbacks.ContainsKey(messageTypes[name])) - { - int handlerId = 0; - if (messageHandlerCounter.ContainsKey(messageTypes[name])) - { - if (!releasedMessageHandlerCounters.ContainsKey(messageTypes[name])) - releasedMessageHandlerCounters.Add(messageTypes[name], new Stack()); - - if(releasedMessageHandlerCounters[messageTypes[name]].Count == 0) - { - handlerId = messageHandlerCounter[messageTypes[name]]; - messageHandlerCounter[messageTypes[name]]++; - } - else - { - handlerId = releasedMessageHandlerCounters[messageTypes[name]].Pop(); - } - } - else - { - messageHandlerCounter.Add(messageTypes[name], handlerId + 1); - } - messageCallbacks[messageTypes[name]].Add(handlerId, action); - return handlerId; - } - else - { - messageCallbacks.Add(messageTypes[name], new Dictionary>()); - messageHandlerCounter.Add(messageTypes[name], 1); - messageCallbacks[messageTypes[name]].Add(0, action); - return 0; - } - } - else - { - Debug.LogWarning("MLAPI: The message type " + name + " has not been registered. Please define it in the netConfig"); - return -1; - } - } - - internal void RemoveIncomingMessageHandler(string name, int counter) - { - if (counter == -1) - return; - - if (messageTypes.ContainsKey(name) && messageCallbacks.ContainsKey(messageTypes[name]) && messageCallbacks[messageTypes[name]].ContainsKey(counter)) - { - messageCallbacks[messageTypes[name]].Remove(counter); - releasedMessageHandlerCounters[messageTypes[name]].Push(counter); - } - } - - private ConnectionConfig Init(NetworkingConfiguration netConfig) - { - NetworkConfig = netConfig; - - pendingClients = new HashSet(); - connectedClients = new Dictionary(); - messageBuffer = new byte[NetworkConfig.MessageBufferSize]; - channels = new Dictionary(); - messageTypes = new Dictionary(); - messageCallbacks = new Dictionary>>(); - messageHandlerCounter = new Dictionary(); - releasedMessageHandlerCounters = new Dictionary>(); - - if (NetworkConfig.ConnectionApproval) - { - if(NetworkConfig.ConnectionApprovalCallback == null) - { - Debug.LogWarning("MLAPI: No ConnectionAproval callback defined. Connection aproval will timeout"); - } - } - - NetworkTransport.Init(); - ConnectionConfig cConfig = new ConnectionConfig(); - - //MLAPI channels and messageTypes - NetworkConfig.Channels.Add("MLAPI_RELIABLE_FRAGMENTED", QosType.ReliableFragmented); - messageTypes.Add("CONNECTION_REQUEST", 0); - - - foreach (KeyValuePair pair in NetworkConfig.Channels) - { - channels.Add(pair.Key, cConfig.AddChannel(pair.Value)); - } - //0-32 are reserved for MLAPI messages - for (ushort i = 32; i < NetworkConfig.MessageTypes.Count; i++) - { - messageTypes.Add(NetworkConfig.MessageTypes[i], i); - } - return cConfig; - } - - - public void StartServer(NetworkingConfiguration netConfig) - { - ConnectionConfig cConfig = Init(netConfig); - HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); - NetworkTransport.AddHost(hostTopology, NetworkConfig.Port); - isServer = true; - isListening = true; - } - - private void OnEnable() - { - if (singleton != null) - { - Debug.LogWarning("MLAPI: Multiple NetworkingManagers"); - return; - } - singleton = this; - } - - private void OnDisable() - { - singleton = null; - } - - //Receive stuff - int hostId; - int connectionId; - int channelId; - int receivedSize; - byte error; - private void Update() - { - if(isListening) - { - NetworkEventType eventType; - int messagesProcessed = 0; - do - { - messagesProcessed++; - eventType = NetworkTransport.Receive(out hostId, out connectionId, out channelId, messageBuffer, NetworkConfig.MessageBufferSize, out receivedSize, out error); - NetworkError networkError = (NetworkError)error; - if (networkError != NetworkError.Ok) - { - Debug.LogWarning("MLAPI: NetworkTransport receive error: " + networkError.ToString()); - return; - } - switch (eventType) - { - case NetworkEventType.ConnectEvent: - if(isServer) - { - if (NetworkConfig.ConnectionApproval) - { - pendingClients.Add(connectionId); - StartCoroutine(ApprovalTimeout(connectionId)); - } - else - { - //Connect - HandleApproval(connectionId, false); - } - } - else - { - if (NetworkConfig.ConnectionApproval) - Send(connectionId, "CONNECTION_REQUEST", "MLAPI_RELIABLE_FRAGMENTED", NetworkConfig.ConnectionData); - } - break; - case NetworkEventType.DataEvent: - HandleIncomingData(connectionId, ref messageBuffer); - break; - } - } while (eventType != NetworkEventType.Nothing && - (messagesProcessed < NetworkConfig.MaxMessagesPerFrame || NetworkConfig.MaxMessagesPerFrame < 0)); - - } - } - - IEnumerator ApprovalTimeout(int connectionId) - { - float timeStarted = Time.time; - //We yield every frame incase a pending client disconnects and someone else gets its connection id - while (Time.time - timeStarted < NetworkConfig.ClientConnectionBufferTimeout && pendingClients.Contains(connectionId)) - { - yield return null; - } - if(pendingClients.Contains(connectionId)) - { - //Timeout - DisconnectClient(connectionId); - } - else - { - //If the key nolonger exist in pending and not in connected, they disconnected - if(!connectedClients.ContainsKey(connectionId)) - { - pendingClients.Remove(connectionId); - } - } - } - - private void HandleIncomingData(int connectonId, ref byte[] data) - { - using(MemoryStream stream = new MemoryStream(data)) - { - BinaryReader reader = new BinaryReader(stream); - ushort protocolVersion = reader.ReadUInt16(); - ushort messageType = reader.ReadUInt16(); - if(messageType >= 32) - { - //Custom message - if(protocolVersion != NetworkConfig.ProtocolVersion) - { - Debug.LogWarning("MLAPI: Protocol version not matching"); - DisconnectClient(connectionId); - } - } - else - { - //MLAPI message - switch(messageType) - { - case 0: //Client to server > sends connection buffer - byte[] connectionBuffer = reader.ReadBytes(int.MaxValue); - NetworkConfig.ConnectionApprovalCallback(connectionBuffer, connectionId, HandleApproval); - break; - case 1: //Server gives client it's connectionId - break; - case 2: //Server informs client of spawned objects - break; - case 3: //Server informs client of spawned players - break; - } - } - } - } - - protected void Send(int connectionId, string messageType, string channelName, byte[] data) - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - writer.Write(NetworkConfig.ProtocolVersion); - writer.Write(messageTypes[messageType]); - writer.Write(data); - NetworkTransport.Send(hostId, connectionId, channels[channelName], data, data.Length, out error); - } - } - - protected void Send(int[] connectonIds, string messageType, string channelName, byte[] data) - { - for (int i = 0; i < connectonIds.Length; i++) - { - Send(connectonIds[i], messageType, channelName, data); - } - } - - protected void Send(List connectonIds, string messageType, string channelName, byte[] data) - { - for (int i = 0; i < connectonIds.Count; i++) - { - Send(connectonIds[i], messageType, channelName, data); - } - } - - protected void DisconnectClient(int connectionId) - { - if (pendingClients.Contains(connectionId)) - pendingClients.Remove(connectionId); - if (connectedClients.ContainsKey(connectionId)) - connectedClients.Remove(connectionId); - NetworkTransport.Disconnect(hostId, connectionId, out error); - - } - - private void HandleApproval(int connectionId, bool approved) - { - if(approved) - { - NetworkedClient client = new NetworkedClient() - { - ClientId = connectionId - }; - connectedClients.Add(connectionId, client); - } - else - { - if (pendingClients.Contains(connectionId)) - pendingClients.Remove(connectionId); - NetworkTransport.Disconnect(hostId, connectionId, out error); - } - } - } -} \ No newline at end of file