Created
July 9, 2019 15:58
-
-
Save OndrejPetrzilka/390ff672de88e47e2a2a8b5d7fb18fb6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if UNITY_ANDROID || UNITY_IOS || UNITY_TIZEN || UNITY_TVOS || UNITY_WEBGL || UNITY_WSA || UNITY_PS4 || UNITY_WII || UNITY_XBOXONE || UNITY_SWITCH | |
#define DISABLESTEAMWORKS | |
#endif | |
#if !DISABLESTEAMWORKS | |
using System; | |
using System.Runtime.InteropServices; | |
using IntPtr = System.IntPtr; | |
namespace Steamworks | |
{ | |
public struct SteamNetworkingSockets | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
struct Vtbl | |
{ | |
public IntPtr CreateListenSocketIP; | |
public IntPtr ConnectByIPAddress; | |
public IntPtr CreateListenSocketP2P; | |
public IntPtr ConnectP2P; | |
public IntPtr AcceptConnection; | |
public IntPtr CloseConnection; | |
public IntPtr CloseListenSocket; | |
public IntPtr SetConnectionUserData; | |
public IntPtr GetConnectionUserData; | |
public IntPtr SetConnectionName; | |
public IntPtr GetConnectionName; | |
public IntPtr SendMessageToConnection; | |
public IntPtr FlushMessagesOnConnection; | |
public IntPtr ReceiveMessagesOnConnection; | |
public IntPtr ReceiveMessagesOnListenSocket; | |
public IntPtr GetConnectionInfo; | |
public IntPtr GetQuickConnectionStatus; | |
public IntPtr GetDetailedConnectionStatus; | |
public IntPtr GetListenSocketAddress; | |
public IntPtr CreateSocketPair; | |
public IntPtr GetIdentity; | |
public IntPtr ReceivedRelayAuthTicket; | |
public IntPtr FindRelayAuthTicketForServer; | |
public IntPtr ConnectToHostedDedicatedServer; | |
public IntPtr GetHostedDedicatedServerPort; | |
public IntPtr GetHostedDedicatedServerPOPID; | |
public IntPtr GetHostedDedicatedServerAddress; | |
public IntPtr CreateHostedDedicatedServerListenSocket; | |
} | |
public readonly IntPtr Ptr; | |
const string STEAMNETWORKINGSOCKETS_INTERFACE_VERSION = "SteamNetworkingSockets002"; | |
static IntPtr m_userInterface; | |
static IntPtr m_gameServerInterface; | |
public static SteamNetworkingSockets UserInstance | |
{ | |
get { return new SteamNetworkingSockets(InterfaceHelper.FindOrCreateUserInterface(ref m_userInterface, STEAMNETWORKINGSOCKETS_INTERFACE_VERSION)); } | |
} | |
public static SteamNetworkingSockets GameServerInterface | |
{ | |
get { return new SteamNetworkingSockets(InterfaceHelper.FindOrCreateGameServerInterface(ref m_gameServerInterface, STEAMNETWORKINGSOCKETS_INTERFACE_VERSION)); } | |
} | |
private SteamNetworkingSockets(IntPtr ptr) | |
{ | |
Ptr = ptr; | |
} | |
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] | |
delegate HSteamListenSocket CreasteListenSocketIPDelegate(IntPtr instance, in SteamNetworkingIPAddr localAddress); | |
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] | |
delegate bool GetListenSocketAddressDelegate(IntPtr instance, HSteamListenSocket hSocket, out SteamNetworkingIPAddr address); | |
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] | |
delegate bool CloseListenSocketDelegate(IntPtr instance, HSteamListenSocket hSocket); | |
/// Creates a "server" socket that listens for clients to connect to by | |
/// calling ConnectByIPAddress, over ordinary UDP (IPv4 or IPv6) | |
/// | |
/// You must select a specific local port to listen on and set it | |
/// the port field of the local address. | |
/// | |
/// Usually you wil set the IP portion of the address to zero, (SteamNetworkingIPAddr::Clear()). | |
/// This means that you will not bind to any particular local interface. In addition, | |
/// if possible the socket will be bound in "dual stack" mode, which means that it can | |
/// accept both IPv4 and IPv6 clients. If you wish to bind a particular interface, then | |
/// set the local address to the appropriate IPv4 or IPv6 IP. | |
/// | |
/// When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t | |
/// will be posted. The connection will be in the connecting state. | |
public HSteamListenSocket CreateListenSocketIP(in SteamNetworkingIPAddr localAddress) | |
{ | |
IntPtr vtblPtr = Marshal.ReadIntPtr(Ptr, 0); | |
var vtbl = Marshal.PtrToStructure<Vtbl>(vtblPtr); | |
CreasteListenSocketIPDelegate method = (CreasteListenSocketIPDelegate)Marshal.GetDelegateForFunctionPointer(vtbl.CreateListenSocketIP, typeof(CreasteListenSocketIPDelegate)); | |
return method(Ptr, in localAddress); | |
} | |
/// Creates a connection and begins talking to a "server" over UDP at the | |
/// given IPv4 or IPv6 address. The remote host must be listening with a | |
/// matching call to CreateListenSocketIP on the specified port. | |
/// | |
/// A SteamNetConnectionStatusChangedCallback_t callback will be triggered when we start | |
/// connecting, and then another one on either timeout or successful connection. | |
/// | |
/// If the server does not have any identity configured, then their network address | |
/// will be the only identity in use. Or, the network host may provide a platform-specific | |
/// identity with or without a valid certificate to authenticate that identity. (These | |
/// details will be contained in the SteamNetConnectionStatusChangedCallback_t.) It's | |
/// up to your application to decide whether to allow the connection. | |
/// | |
/// By default, all connections will get basic encryption sufficient to prevent | |
/// casual eavesdropping. But note that without certificates (or a shared secret | |
/// distributed through some other out-of-band mechanism), you don't have any | |
/// way of knowing who is actually on the other end, and thus are vulnerable to | |
/// man-in-the-middle attacks. | |
public HSteamNetConnection ConnectByIPAddress(in SteamNetworkingIPAddr address) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Like CreateListenSocketIP, but clients will connect using ConnectP2P | |
/// | |
/// nVirtualPort specifies how clients can connect to this socket using | |
/// ConnectP2P. It's very common for applications to only have one listening socket; | |
/// in that case, use zero. If you need to open multiple listen sockets and have clients | |
/// be able to connect to one or the other, then nVirtualPort should be a small integer (<1000) | |
/// unique to each listen socket you create. | |
/// | |
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess() | |
/// when your app initializes | |
public HSteamListenSocket CreateListenSocketP2P(int nVirtualPort) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Begin connecting to a server that is identified using a platform-specific identifier. | |
/// This requires some sort of third party rendezvous service, and will depend on the | |
/// platform and what other libraries and services you are integrating with. | |
/// | |
/// At the time of this writing, there is only one supported rendezvous service: Steam. | |
/// Set the SteamID (whether "user" or "gameserver") and Steam will determine if the | |
/// client is online and facilitate a relay connection. Note that all P2P connections on | |
/// Steam are currently relayed. | |
/// | |
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess() | |
/// when your app initializes | |
public HSteamNetConnection ConnectP2P(in SteamNetworkingIdentity identityRemote, int nVirtualPort) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Accept an incoming connection that has been received on a listen socket. | |
/// | |
/// When a connection attempt is received (perhaps after a few basic handshake | |
/// packets have been exchanged to prevent trivial spoofing), a connection interface | |
/// object is created in the k_ESteamNetworkingConnectionState_Connecting state | |
/// and a SteamNetConnectionStatusChangedCallback_t is posted. At this point, your | |
/// application MUST either accept or close the connection. (It may not ignore it.) | |
/// Accepting the connection will transition it either into the connected state, | |
/// or the finding route state, depending on the connection type. | |
/// | |
/// You should take action within a second or two, because accepting the connection is | |
/// what actually sends the reply notifying the client that they are connected. If you | |
/// delay taking action, from the client's perspective it is the same as the network | |
/// being unresponsive, and the client may timeout the connection attempt. In other | |
/// words, the client cannot distinguish between a delay caused by network problems | |
/// and a delay caused by the application. | |
/// | |
/// This means that if your application goes for more than a few seconds without | |
/// processing callbacks (for example, while loading a map), then there is a chance | |
/// that a client may attempt to connect in that interval and fail due to timeout. | |
/// | |
/// If the application does not respond to the connection attempt in a timely manner, | |
/// and we stop receiving communication from the client, the connection attempt will | |
/// be timed out locally, transitioning the connection to the | |
/// k_ESteamNetworkingConnectionState_ProblemDetectedLocally state. The client may also | |
/// close the connection before it is accepted, and a transition to the | |
/// k_ESteamNetworkingConnectionState_ClosedByPeer is also possible depending the exact | |
/// sequence of events. | |
/// | |
/// Returns k_EResultInvalidParam if the handle is invalid. | |
/// Returns k_EResultInvalidState if the connection is not in the appropriate state. | |
/// (Remember that the connection state could change in between the time that the | |
/// notification being posted to the queue and when it is received by the application.) | |
public EResult AcceptConnection(HSteamNetConnection hConn) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Disconnects from the remote host and invalidates the connection handle. | |
/// Any unread data on the connection is discarded. | |
/// | |
/// nReason is an application defined code that will be received on the other | |
/// end and recorded (when possible) in backend analytics. The value should | |
/// come from a restricted range. (See ESteamNetConnectionEnd.) If you don't need | |
/// to communicate any information to the remote host, and do not want analytics to | |
/// be able to distinguish "normal" connection terminations from "exceptional" ones, | |
/// You may pass zero, in which case the generic value of | |
/// k_ESteamNetConnectionEnd_App_Generic will be used. | |
/// | |
/// pszDebug is an optional human-readable diagnostic string that will be received | |
/// by the remote host and recorded (when possible) in backend analytics. | |
/// | |
/// If you wish to put the socket into a "linger" state, where an attempt is made to | |
/// flush any remaining sent data, use bEnableLinger=true. Otherwise reliable data | |
/// is not flushed. | |
/// | |
/// If the connection has already ended and you are just freeing up the | |
/// connection interface, the reason code, debug string, and linger flag are | |
/// ignored. | |
public bool CloseConnection(HSteamNetConnection hPeer, int nReason, IntPtr pszDebug, bool bEnableLinger) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Destroy a listen socket. All the connections that were accepting on the listen | |
/// socket are closed ungracefully. | |
public bool CloseListenSocket(HSteamListenSocket hSocket) | |
{ | |
IntPtr vtblPtr = Marshal.ReadIntPtr(Ptr, 0); | |
var vtbl = Marshal.PtrToStructure<Vtbl>(vtblPtr); | |
CloseListenSocketDelegate method = (CloseListenSocketDelegate)Marshal.GetDelegateForFunctionPointer(vtbl.CloseListenSocket, typeof(CloseListenSocketDelegate)); | |
return method(Ptr, hSocket); | |
} | |
/// Send a message to the remote host on the specified connection. | |
/// | |
/// nSendFlags determines the delivery guarantees that will be provided, | |
/// when data should be buffered, etc. E.g. k_nSteamNetworkingSend_Unreliable | |
/// | |
/// Note that the semantics we use for messages are not precisely | |
/// the same as the semantics of a standard "stream" socket. | |
/// (SOCK_STREAM) For an ordinary stream socket, the boundaries | |
/// between chunks are not considered relevant, and the sizes of | |
/// the chunks of data written will not necessarily match up to | |
/// the sizes of the chunks that are returned by the reads on | |
/// the other end. The remote host might read a partial chunk, | |
/// or chunks might be coalesced. For the message semantics | |
/// used here, however, the sizes WILL match. Each send call | |
/// will match a successful read call on the remote host | |
/// one-for-one. If you are porting existing stream-oriented | |
/// code to the semantics of reliable messages, your code should | |
/// work the same, since reliable message semantics are more | |
/// strict than stream semantics. The only caveat is related to | |
/// performance: there is per-message overhead to retain the | |
/// message sizes, and so if your code sends many small chunks | |
/// of data, performance will suffer. Any code based on stream | |
/// sockets that does not write excessively small chunks will | |
/// work without any changes. | |
/// | |
/// Returns: | |
/// - k_EResultInvalidParam: invalid connection handle, or the individual message is too big. | |
/// (See k_cbMaxSteamNetworkingSocketsMessageSizeSend) | |
/// - k_EResultInvalidState: connection is in an invalid state | |
/// - k_EResultNoConnection: connection has ended | |
/// - k_EResultIgnored: You used k_nSteamNetworkingSend_NoDelay, and the message was dropped because | |
/// we were not ready to send it. | |
/// - k_EResultLimitExceeded: there was already too much data queued to be sent. | |
/// (See k_ESteamNetworkingConfig_SendBufferSize) | |
public EResult SendMessageToConnection(HSteamNetConnection hConn, IntPtr pData, uint cbData, int nSendFlags) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Flush any messages waiting on the Nagle timer and send them | |
/// at the next transmission opportunity (often that means right now). | |
/// | |
/// If Nagle is enabled (it's on by default) then when calling | |
/// SendMessageToConnection the message will be buffered, up to the Nagle time | |
/// before being sent, to merge small messages into the same packet. | |
/// (See k_ESteamNetworkingConfig_NagleTime) | |
/// | |
/// Returns: | |
/// k_EResultInvalidParam: invalid connection handle | |
/// k_EResultInvalidState: connection is in an invalid state | |
/// k_EResultNoConnection: connection has ended | |
/// k_EResultIgnored: We weren't (yet) connected, so this operation has no effect. | |
public EResult FlushMessagesOnConnection(HSteamNetConnection hConn) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Fetch the next available message(s) from the connection, if any. | |
/// Returns the number of messages returned into your array, up to nMaxMessages. | |
/// If the connection handle is invalid, -1 is returned. | |
/// | |
/// The order of the messages returned in the array is relevant. | |
/// Reliable messages will be received in the order they were sent (and with the | |
/// same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket). | |
/// | |
/// Unreliable messages may be dropped, or delivered out of order withrespect to | |
/// each other or with respect to reliable messages. The same unreliable message | |
/// may be received multiple times. | |
/// | |
/// If any messages are returned, you MUST call SteamNetworkingMessage_t::Release() on each | |
/// of them free up resources after you are done. It is safe to keep the object alive for | |
/// a little while (put it into some queue, etc), and you may call Release() from any thread. | |
public int ReceiveMessagesOnConnection(HSteamNetConnection hConn, IntPtr ppOutMessages, int nMaxMessages) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Same as ReceiveMessagesOnConnection, but will return the next message available | |
/// on any connection that was accepted through the specified listen socket. Examine | |
/// SteamNetworkingMessage_t::m_conn to know which client connection. | |
/// | |
/// Delivery order of messages among different clients is not defined. They may | |
/// be returned in an order different from what they were actually received. (Delivery | |
/// order of messages from the same client is well defined, and thus the order of the | |
/// messages is relevant!) | |
public int ReceiveMessagesOnListenSocket(HSteamListenSocket hSocket, IntPtr ppOutMessages, int nMaxMessages) | |
{ | |
throw new System.NotImplementedException(); | |
} | |
/// Returns local IP and port that a listen socket created using CreateListenSocketIP is bound to. | |
/// | |
/// An IPv6 address of ::0 means "any IPv4 or IPv6" | |
/// An IPv6 address of ::ffff:0000:0000 means "any IPv4" | |
public bool GetListenSocketAddress(HSteamListenSocket hSocket, out SteamNetworkingIPAddr address) | |
{ | |
IntPtr vtblPtr = Marshal.ReadIntPtr(Ptr, 0); | |
var vtbl = Marshal.PtrToStructure<Vtbl>(vtblPtr); | |
GetListenSocketAddressDelegate method = (GetListenSocketAddressDelegate)Marshal.GetDelegateForFunctionPointer(vtbl.GetListenSocketAddress, typeof(GetListenSocketAddressDelegate)); | |
return method(Ptr, hSocket, out address); | |
} | |
} | |
} | |
#endif // !DISABLESTEAMWORKS |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment