Skip to content

Instantly share code, notes, and snippets.

@OndrejPetrzilka
Created July 9, 2019 15:58
Show Gist options
  • Save OndrejPetrzilka/390ff672de88e47e2a2a8b5d7fb18fb6 to your computer and use it in GitHub Desktop.
Save OndrejPetrzilka/390ff672de88e47e2a2a8b5d7fb18fb6 to your computer and use it in GitHub Desktop.
#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