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;
}
}