using System; using System.Runtime.InteropServices; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Networking.Transport { internal interface INetworkProtocol : IDisposable { /// /// This is call when initializing the NetworkDriver. If the protocol requires custom paramters, they can be passed /// to the NetworkDriver initialization. /// void Initialize(INetworkParameter[] parameters); /// /// Returns a burst compatible NetworkProtocol struct containing the function pointers and custom UserData for the protocol. /// NetworkProtocol CreateProtocolInterface(); /// /// This method should bind the NetworkInterface to the local endpoint and perform any /// custom binding behaviour for the protocol. /// int Bind(INetworkInterface networkInterface, ref NetworkInterfaceEndPoint localEndPoint); /// /// Create a new connection address for the endPoint using the passed NetworkInterface. /// Some protocols - as relay - could decide to use virtual addressed that not necessarily /// maps 1 - 1 to a endpoint. /// int Connect(INetworkInterface networkInterface, NetworkEndPoint endPoint, out NetworkInterfaceEndPoint address); NetworkEndPoint GetRemoteEndPoint(INetworkInterface networkInterface, NetworkInterfaceEndPoint address); } /// /// This is a Burst compatible struct that contains all the function pointers that the NetworkDriver /// uses for processing messages with a particular protocol. /// internal struct NetworkProtocol { /// /// Computes the size required for allocating a packet for the connection with this protocol. The dataCapacity received /// can be modified to reflect the resulting payload capacity of the packet, if it gets reduced the NetworkDriver will /// return a NetworkPacketOverflow error. The payloadOffset return value is the possition where the payload data needs /// to be stored in the packet. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int ComputePacketAllocationSizeDelegate(ref NetworkDriver.Connection connection, ref int dataCapacity, out int payloadOffset); /// /// Process a receiving packet and returns a ProcessPacketCommand that will indicate to the NetworkDriver what actions /// need to be performed and what to do with the message. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessReceiveDelegate(IntPtr stream, ref NetworkInterfaceEndPoint address, int size, ref NetworkSendInterface sendInterface, ref NetworkSendQueueHandle queueHandle, IntPtr userData, ref ProcessPacketCommand command); /// /// Process a sending packet. When this method is called, the packet is ready to be sent through the sendInterface. /// Here the protocol could perform some final steps as, for instance, filling some header fields. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int ProcessSendDelegate(ref NetworkDriver.Connection connection, bool hasPipeline, ref NetworkSendInterface sendInterface, ref NetworkInterfaceSendHandle sendHandle, ref NetworkSendQueueHandle queueHandle, IntPtr userData); /// /// This method should send a protocol specific connect confirmation message from a server to a client using the connection. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessSendConnectionAcceptDelegate(ref NetworkDriver.Connection connection, ref NetworkSendInterface sendInterface, ref NetworkSendQueueHandle queueHandle, IntPtr userData); /// /// This method should send a protocol specific connect request message from a client to a server. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessSendConnectionRequestDelegate(ref NetworkDriver.Connection connection, ref NetworkSendInterface sendInterface, ref NetworkSendQueueHandle queueHandle, IntPtr userData); /// /// This method should send a protocol specific disconnect request message from a client to a server. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessSendDisconnectDelegate(ref NetworkDriver.Connection connection, ref NetworkSendInterface sendInterface, ref NetworkSendQueueHandle queueHandle, IntPtr userData); /// /// This method is called every NetworkDriver tick and can be used for performing protocol update operations. /// One common case is sending protocol specific packets for keeping the connections alive or retrying failed ones. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void UpdateDelegate(long updateTime, ref NetworkSendInterface sendInterface, ref NetworkSendQueueHandle queueHandle, IntPtr userData); public TransportFunctionPointer ComputePacketAllocationSize; public TransportFunctionPointer ProcessReceive; public TransportFunctionPointer ProcessSend; public TransportFunctionPointer ProcessSendConnectionAccept; public TransportFunctionPointer ProcessSendConnectionRequest; public TransportFunctionPointer ProcessSendDisconnect; public TransportFunctionPointer Update; /// /// Raw pointer that is going to be passed to the function pointer and that can contain protocol specific data. /// [NativeDisableUnsafePtrRestriction] public IntPtr UserData; /// /// The maximun size of the header of a data packet for this protocol. /// public int MaxHeaderSize; /// /// The maximun size of the footer of a data packet for this protocol. /// public int MaxFooterSize; /// /// The maximun amount of bytes that are not payload data for a packet for this protocol. /// public int PaddingSize => MaxHeaderSize + MaxFooterSize; /// /// If true - UpdateDelegate will be called /// public bool NeedsUpdate; public NetworkProtocol( TransportFunctionPointer computePacketAllocationSize, TransportFunctionPointer processReceive, TransportFunctionPointer processSend, TransportFunctionPointer processSendConnectionAccept, TransportFunctionPointer processSendConnectionRequest, TransportFunctionPointer processSendDisconnect, TransportFunctionPointer update, bool needsUpdate, IntPtr userData, int maxHeaderSize, int maxFooterSize ) { ComputePacketAllocationSize = computePacketAllocationSize; ProcessReceive = processReceive; ProcessSend = processSend; ProcessSendConnectionAccept = processSendConnectionAccept; ProcessSendConnectionRequest = processSendConnectionRequest; ProcessSendDisconnect = processSendDisconnect; Update = update; NeedsUpdate = needsUpdate; UserData = userData; MaxHeaderSize = maxHeaderSize; MaxFooterSize = maxFooterSize; } } /// /// The type of commands that the NetworkDriver can process from a received packet after it is proccessed /// by the protocol. /// public enum ProcessPacketCommandType : byte { /// /// Do not perform any extra action. /// Drop = 0, // keep Drop = 0 to make it the default. /// /// Find and update the address for a connection. /// AddressUpdate, /// /// Complete the binding proccess. /// BindAccept, /// /// The connection has been accepted by the server and can be completed. /// ConnectionAccept, /// /// The connection has been rejected by the server. /// ConnectionReject, /// /// A connection request comming from a client has been received by the server. /// ConnectionRequest, /// /// A Data message has been received for a well stablished connection. /// Data, /// /// The connection is requesting to disconnect. /// Disconnect, /// /// A simultanious Data + Connection accept command. /// DataWithImplicitConnectionAccept, } /// /// Contains the command type and command data required by the NetworkDriver to process a packet. /// [StructLayout(LayoutKind.Explicit)] internal unsafe struct ProcessPacketCommand { /// /// The type of the command to proccess /// [FieldOffset(0)] public ProcessPacketCommandType Type; // The following fields behaves like a C++ union. All command types data should start with the Address field. [FieldOffset(1)] public NetworkInterfaceEndPoint ConnectionAddress; [FieldOffset(1)] public ProcessPacketCommandAddressUpdate AsAddressUpdate; [FieldOffset(1)] public ProcessPacketCommandConnectionAccept AsConnectionAccept; [FieldOffset(1)] public ProcessPacketCommandConnectionRequest AsConnectionRequest; [FieldOffset(1)] public ProcessPacketCommandData AsData; [FieldOffset(1)] public ProcessPacketCommandDataWithImplicitConnectionAccept AsDataWithImplicitConnectionAccept; [FieldOffset(1)] public ProcessPacketCommandDisconnect AsDisconnect; } internal struct ProcessPacketCommandAddressUpdate { public NetworkInterfaceEndPoint Address; public NetworkInterfaceEndPoint NewAddress; public ushort SessionToken; } internal struct ProcessPacketCommandConnectionRequest { public NetworkInterfaceEndPoint Address; public ushort SessionId; } internal struct ProcessPacketCommandConnectionAccept { public NetworkInterfaceEndPoint Address; public ushort SessionId; public ushort ConnectionToken; } internal struct ProcessPacketCommandDisconnect { public NetworkInterfaceEndPoint Address; public ushort SessionId; } internal struct ProcessPacketCommandData { public NetworkInterfaceEndPoint Address; public ushort SessionId; public int Offset; public int Length; public byte HasPipelineByte; public bool HasPipeline => HasPipelineByte != 0; } internal struct ProcessPacketCommandDataWithImplicitConnectionAccept { public NetworkInterfaceEndPoint Address; public ushort SessionId; public int Offset; public int Length; public byte HasPipelineByte; public ushort ConnectionToken; public bool HasPipeline => HasPipelineByte != 0; } }