// This file is provided under The MIT License as part of Steamworks.NET.
// Copyright (c) 2013-2022 Riley Labrecque
// Please see the included LICENSE.txt for additional information.

// This file is automatically generated.
// Changes to this file will be reverted when you update Steamworks.NET

#if !(UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX)
	#define DISABLESTEAMWORKS
#endif

#if !DISABLESTEAMWORKS

using System.Runtime.InteropServices;
using IntPtr = System.IntPtr;

namespace Steamworks {
	public static class SteamNetworkingSockets {
		/// <summary>
		/// <para>/ Creates a "server" socket that listens for clients to connect to by</para>
		/// <para>/ calling ConnectByIPAddress, over ordinary UDP (IPv4 or IPv6)</para>
		/// <para>/</para>
		/// <para>/ You must select a specific local port to listen on and set it</para>
		/// <para>/ the port field of the local address.</para>
		/// <para>/</para>
		/// <para>/ Usually you will set the IP portion of the address to zero (SteamNetworkingIPAddr::Clear()).</para>
		/// <para>/ This means that you will not bind to any particular local interface (i.e. the same</para>
		/// <para>/ as INADDR_ANY in plain socket code).  Furthermore, if possible the socket will be bound</para>
		/// <para>/ in "dual stack" mode, which means that it can accept both IPv4 and IPv6 client connections.</para>
		/// <para>/ If you really do wish to bind a particular interface, then set the local address to the</para>
		/// <para>/ appropriate IPv4 or IPv6 IP.</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// <para>/</para>
		/// <para>/ When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t</para>
		/// <para>/ will be posted.  The connection will be in the connecting state.</para>
		/// </summary>
		public static HSteamListenSocket CreateListenSocketIP(ref SteamNetworkingIPAddr localAddress, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamListenSocket)NativeMethods.ISteamNetworkingSockets_CreateListenSocketIP(CSteamAPIContext.GetSteamNetworkingSockets(), ref localAddress, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Creates a connection and begins talking to a "server" over UDP at the</para>
		/// <para>/ given IPv4 or IPv6 address.  The remote host must be listening with a</para>
		/// <para>/ matching call to CreateListenSocketIP on the specified port.</para>
		/// <para>/</para>
		/// <para>/ A SteamNetConnectionStatusChangedCallback_t callback will be triggered when we start</para>
		/// <para>/ connecting, and then another one on either timeout or successful connection.</para>
		/// <para>/</para>
		/// <para>/ If the server does not have any identity configured, then their network address</para>
		/// <para>/ will be the only identity in use.  Or, the network host may provide a platform-specific</para>
		/// <para>/ identity with or without a valid certificate to authenticate that identity.  (These</para>
		/// <para>/ details will be contained in the SteamNetConnectionStatusChangedCallback_t.)  It's</para>
		/// <para>/ up to your application to decide whether to allow the connection.</para>
		/// <para>/</para>
		/// <para>/ By default, all connections will get basic encryption sufficient to prevent</para>
		/// <para>/ casual eavesdropping.  But note that without certificates (or a shared secret</para>
		/// <para>/ distributed through some other out-of-band mechanism), you don't have any</para>
		/// <para>/ way of knowing who is actually on the other end, and thus are vulnerable to</para>
		/// <para>/ man-in-the-middle attacks.</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// </summary>
		public static HSteamNetConnection ConnectByIPAddress(ref SteamNetworkingIPAddr address, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamNetConnection)NativeMethods.ISteamNetworkingSockets_ConnectByIPAddress(CSteamAPIContext.GetSteamNetworkingSockets(), ref address, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Like CreateListenSocketIP, but clients will connect using ConnectP2P.</para>
		/// <para>/</para>
		/// <para>/ nLocalVirtualPort specifies how clients can connect to this socket using</para>
		/// <para>/ ConnectP2P.  It's very common for applications to only have one listening socket;</para>
		/// <para>/ in that case, use zero.  If you need to open multiple listen sockets and have clients</para>
		/// <para>/ be able to connect to one or the other, then nLocalVirtualPort should be a small</para>
		/// <para>/ integer (&lt;1000) unique to each listen socket you create.</para>
		/// <para>/</para>
		/// <para>/ If you use this, you probably want to call ISteamNetworkingUtils::InitRelayNetworkAccess()</para>
		/// <para>/ when your app initializes.</para>
		/// <para>/</para>
		/// <para>/ If you are listening on a dedicated servers in known data center,</para>
		/// <para>/ then you can listen using this function instead of CreateHostedDedicatedServerListenSocket,</para>
		/// <para>/ to allow clients to connect without a ticket.  Any user that owns</para>
		/// <para>/ the app and is signed into Steam will be able to attempt to connect to</para>
		/// <para>/ your server.  Also, a connection attempt may require the client to</para>
		/// <para>/ be connected to Steam, which is one more moving part that may fail.  When</para>
		/// <para>/ tickets are used, then once a ticket is obtained, a client can connect to</para>
		/// <para>/ your server even if they got disconnected from Steam or Steam is offline.</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// </summary>
		public static HSteamListenSocket CreateListenSocketP2P(int nLocalVirtualPort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamListenSocket)NativeMethods.ISteamNetworkingSockets_CreateListenSocketP2P(CSteamAPIContext.GetSteamNetworkingSockets(), nLocalVirtualPort, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Begin connecting to a peer that is identified using a platform-specific identifier.</para>
		/// <para>/ This uses the default rendezvous service, which depends on the platform and library</para>
		/// <para>/ configuration.  (E.g. on Steam, it goes through the steam backend.)</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// <para>/</para>
		/// <para>/ To use your own signaling service, see:</para>
		/// <para>/ - ConnectP2PCustomSignaling</para>
		/// <para>/ - k_ESteamNetworkingConfig_Callback_CreateConnectionSignaling</para>
		/// </summary>
		public static HSteamNetConnection ConnectP2P(ref SteamNetworkingIdentity identityRemote, int nRemoteVirtualPort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamNetConnection)NativeMethods.ISteamNetworkingSockets_ConnectP2P(CSteamAPIContext.GetSteamNetworkingSockets(), ref identityRemote, nRemoteVirtualPort, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Accept an incoming connection that has been received on a listen socket.</para>
		/// <para>/</para>
		/// <para>/ When a connection attempt is received (perhaps after a few basic handshake</para>
		/// <para>/ packets have been exchanged to prevent trivial spoofing), a connection interface</para>
		/// <para>/ object is created in the k_ESteamNetworkingConnectionState_Connecting state</para>
		/// <para>/ and a SteamNetConnectionStatusChangedCallback_t is posted.  At this point, your</para>
		/// <para>/ application MUST either accept or close the connection.  (It may not ignore it.)</para>
		/// <para>/ Accepting the connection will transition it either into the connected state,</para>
		/// <para>/ or the finding route state, depending on the connection type.</para>
		/// <para>/</para>
		/// <para>/ You should take action within a second or two, because accepting the connection is</para>
		/// <para>/ what actually sends the reply notifying the client that they are connected.  If you</para>
		/// <para>/ delay taking action, from the client's perspective it is the same as the network</para>
		/// <para>/ being unresponsive, and the client may timeout the connection attempt.  In other</para>
		/// <para>/ words, the client cannot distinguish between a delay caused by network problems</para>
		/// <para>/ and a delay caused by the application.</para>
		/// <para>/</para>
		/// <para>/ This means that if your application goes for more than a few seconds without</para>
		/// <para>/ processing callbacks (for example, while loading a map), then there is a chance</para>
		/// <para>/ that a client may attempt to connect in that interval and fail due to timeout.</para>
		/// <para>/</para>
		/// <para>/ If the application does not respond to the connection attempt in a timely manner,</para>
		/// <para>/ and we stop receiving communication from the client, the connection attempt will</para>
		/// <para>/ be timed out locally, transitioning the connection to the</para>
		/// <para>/ k_ESteamNetworkingConnectionState_ProblemDetectedLocally state.  The client may also</para>
		/// <para>/ close the connection before it is accepted, and a transition to the</para>
		/// <para>/ k_ESteamNetworkingConnectionState_ClosedByPeer is also possible depending the exact</para>
		/// <para>/ sequence of events.</para>
		/// <para>/</para>
		/// <para>/ Returns k_EResultInvalidParam if the handle is invalid.</para>
		/// <para>/ Returns k_EResultInvalidState if the connection is not in the appropriate state.</para>
		/// <para>/ (Remember that the connection state could change in between the time that the</para>
		/// <para>/ notification being posted to the queue and when it is received by the application.)</para>
		/// <para>/</para>
		/// <para>/ A note about connection configuration options.  If you need to set any configuration</para>
		/// <para>/ options that are common to all connections accepted through a particular listen</para>
		/// <para>/ socket, consider setting the options on the listen socket, since such options are</para>
		/// <para>/ inherited automatically.  If you really do need to set options that are connection</para>
		/// <para>/ specific, it is safe to set them on the connection before accepting the connection.</para>
		/// </summary>
		public static EResult AcceptConnection(HSteamNetConnection hConn) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_AcceptConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hConn);
		}

		/// <summary>
		/// <para>/ Disconnects from the remote host and invalidates the connection handle.</para>
		/// <para>/ Any unread data on the connection is discarded.</para>
		/// <para>/</para>
		/// <para>/ nReason is an application defined code that will be received on the other</para>
		/// <para>/ end and recorded (when possible) in backend analytics.  The value should</para>
		/// <para>/ come from a restricted range.  (See ESteamNetConnectionEnd.)  If you don't need</para>
		/// <para>/ to communicate any information to the remote host, and do not want analytics to</para>
		/// <para>/ be able to distinguish "normal" connection terminations from "exceptional" ones,</para>
		/// <para>/ You may pass zero, in which case the generic value of</para>
		/// <para>/ k_ESteamNetConnectionEnd_App_Generic will be used.</para>
		/// <para>/</para>
		/// <para>/ pszDebug is an optional human-readable diagnostic string that will be received</para>
		/// <para>/ by the remote host and recorded (when possible) in backend analytics.</para>
		/// <para>/</para>
		/// <para>/ If you wish to put the socket into a "linger" state, where an attempt is made to</para>
		/// <para>/ flush any remaining sent data, use bEnableLinger=true.  Otherwise reliable data</para>
		/// <para>/ is not flushed.</para>
		/// <para>/</para>
		/// <para>/ If the connection has already ended and you are just freeing up the</para>
		/// <para>/ connection interface, the reason code, debug string, and linger flag are</para>
		/// <para>/ ignored.</para>
		/// </summary>
		public static bool CloseConnection(HSteamNetConnection hPeer, int nReason, string pszDebug, bool bEnableLinger) {
			InteropHelp.TestIfAvailableClient();
			using (var pszDebug2 = new InteropHelp.UTF8StringHandle(pszDebug)) {
				return NativeMethods.ISteamNetworkingSockets_CloseConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hPeer, nReason, pszDebug2, bEnableLinger);
			}
		}

		/// <summary>
		/// <para>/ Destroy a listen socket.  All the connections that were accepting on the listen</para>
		/// <para>/ socket are closed ungracefully.</para>
		/// </summary>
		public static bool CloseListenSocket(HSteamListenSocket hSocket) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_CloseListenSocket(CSteamAPIContext.GetSteamNetworkingSockets(), hSocket);
		}

		/// <summary>
		/// <para>/ Set connection user data.  the data is returned in the following places</para>
		/// <para>/ - You can query it using GetConnectionUserData.</para>
		/// <para>/ - The SteamNetworkingmessage_t structure.</para>
		/// <para>/ - The SteamNetConnectionInfo_t structure.</para>
		/// <para>/   (Which is a member of SteamNetConnectionStatusChangedCallback_t -- but see WARNINGS below!!!!)</para>
		/// <para>/</para>
		/// <para>/ Do you need to set this atomically when the connection is created?</para>
		/// <para>/ See k_ESteamNetworkingConfig_ConnectionUserData.</para>
		/// <para>/</para>
		/// <para>/ WARNING: Be *very careful* when using the value provided in callbacks structs.</para>
		/// <para>/ Callbacks are queued, and the value that you will receive in your</para>
		/// <para>/ callback is the userdata that was effective at the time the callback</para>
		/// <para>/ was queued.  There are subtle race conditions that can happen if you</para>
		/// <para>/ don't understand this!</para>
		/// <para>/</para>
		/// <para>/ If any incoming messages for this connection are queued, the userdata</para>
		/// <para>/ field is updated, so that when when you receive messages (e.g. with</para>
		/// <para>/ ReceiveMessagesOnConnection), they will always have the very latest</para>
		/// <para>/ userdata.  So the tricky race conditions that can happen with callbacks</para>
		/// <para>/ do not apply to retrieving messages.</para>
		/// <para>/</para>
		/// <para>/ Returns false if the handle is invalid.</para>
		/// </summary>
		public static bool SetConnectionUserData(HSteamNetConnection hPeer, long nUserData) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_SetConnectionUserData(CSteamAPIContext.GetSteamNetworkingSockets(), hPeer, nUserData);
		}

		/// <summary>
		/// <para>/ Fetch connection user data.  Returns -1 if handle is invalid</para>
		/// <para>/ or if you haven't set any userdata on the connection.</para>
		/// </summary>
		public static long GetConnectionUserData(HSteamNetConnection hPeer) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetConnectionUserData(CSteamAPIContext.GetSteamNetworkingSockets(), hPeer);
		}

		/// <summary>
		/// <para>/ Set a name for the connection, used mostly for debugging</para>
		/// </summary>
		public static void SetConnectionName(HSteamNetConnection hPeer, string pszName) {
			InteropHelp.TestIfAvailableClient();
			using (var pszName2 = new InteropHelp.UTF8StringHandle(pszName)) {
				NativeMethods.ISteamNetworkingSockets_SetConnectionName(CSteamAPIContext.GetSteamNetworkingSockets(), hPeer, pszName2);
			}
		}

		/// <summary>
		/// <para>/ Fetch connection name.  Returns false if handle is invalid</para>
		/// </summary>
		public static bool GetConnectionName(HSteamNetConnection hPeer, out string pszName, int nMaxLen) {
			InteropHelp.TestIfAvailableClient();
			IntPtr pszName2 = Marshal.AllocHGlobal(nMaxLen);
			bool ret = NativeMethods.ISteamNetworkingSockets_GetConnectionName(CSteamAPIContext.GetSteamNetworkingSockets(), hPeer, pszName2, nMaxLen);
			pszName = ret ? InteropHelp.PtrToStringUTF8(pszName2) : null;
			Marshal.FreeHGlobal(pszName2);
			return ret;
		}

		/// <summary>
		/// <para>/ Send a message to the remote host on the specified connection.</para>
		/// <para>/</para>
		/// <para>/ nSendFlags determines the delivery guarantees that will be provided,</para>
		/// <para>/ when data should be buffered, etc.  E.g. k_nSteamNetworkingSend_Unreliable</para>
		/// <para>/</para>
		/// <para>/ Note that the semantics we use for messages are not precisely</para>
		/// <para>/ the same as the semantics of a standard "stream" socket.</para>
		/// <para>/ (SOCK_STREAM)  For an ordinary stream socket, the boundaries</para>
		/// <para>/ between chunks are not considered relevant, and the sizes of</para>
		/// <para>/ the chunks of data written will not necessarily match up to</para>
		/// <para>/ the sizes of the chunks that are returned by the reads on</para>
		/// <para>/ the other end.  The remote host might read a partial chunk,</para>
		/// <para>/ or chunks might be coalesced.  For the message semantics</para>
		/// <para>/ used here, however, the sizes WILL match.  Each send call</para>
		/// <para>/ will match a successful read call on the remote host</para>
		/// <para>/ one-for-one.  If you are porting existing stream-oriented</para>
		/// <para>/ code to the semantics of reliable messages, your code should</para>
		/// <para>/ work the same, since reliable message semantics are more</para>
		/// <para>/ strict than stream semantics.  The only caveat is related to</para>
		/// <para>/ performance: there is per-message overhead to retain the</para>
		/// <para>/ message sizes, and so if your code sends many small chunks</para>
		/// <para>/ of data, performance will suffer. Any code based on stream</para>
		/// <para>/ sockets that does not write excessively small chunks will</para>
		/// <para>/ work without any changes.</para>
		/// <para>/</para>
		/// <para>/ The pOutMessageNumber is an optional pointer to receive the</para>
		/// <para>/ message number assigned to the message, if sending was successful.</para>
		/// <para>/</para>
		/// <para>/ Returns:</para>
		/// <para>/ - k_EResultInvalidParam: invalid connection handle, or the individual message is too big.</para>
		/// <para>/   (See k_cbMaxSteamNetworkingSocketsMessageSizeSend)</para>
		/// <para>/ - k_EResultInvalidState: connection is in an invalid state</para>
		/// <para>/ - k_EResultNoConnection: connection has ended</para>
		/// <para>/ - k_EResultIgnored: You used k_nSteamNetworkingSend_NoDelay, and the message was dropped because</para>
		/// <para>/   we were not ready to send it.</para>
		/// <para>/ - k_EResultLimitExceeded: there was already too much data queued to be sent.</para>
		/// <para>/   (See k_ESteamNetworkingConfig_SendBufferSize)</para>
		/// </summary>
		public static EResult SendMessageToConnection(HSteamNetConnection hConn, IntPtr pData, uint cbData, int nSendFlags, out long pOutMessageNumber) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_SendMessageToConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, pData, cbData, nSendFlags, out pOutMessageNumber);
		}

		/// <summary>
		/// <para>/ Send one or more messages without copying the message payload.</para>
		/// <para>/ This is the most efficient way to send messages. To use this</para>
		/// <para>/ function, you must first allocate a message object using</para>
		/// <para>/ ISteamNetworkingUtils::AllocateMessage.  (Do not declare one</para>
		/// <para>/ on the stack or allocate your own.)</para>
		/// <para>/</para>
		/// <para>/ You should fill in the message payload.  You can either let</para>
		/// <para>/ it allocate the buffer for you and then fill in the payload,</para>
		/// <para>/ or if you already have a buffer allocated, you can just point</para>
		/// <para>/ m_pData at your buffer and set the callback to the appropriate function</para>
		/// <para>/ to free it.  Note that if you use your own buffer, it MUST remain valid</para>
		/// <para>/ until the callback is executed.  And also note that your callback can be</para>
		/// <para>/ invoked at any time from any thread (perhaps even before SendMessages</para>
		/// <para>/ returns!), so it MUST be fast and threadsafe.</para>
		/// <para>/</para>
		/// <para>/ You MUST also fill in:</para>
		/// <para>/ - m_conn - the handle of the connection to send the message to</para>
		/// <para>/ - m_nFlags - bitmask of k_nSteamNetworkingSend_xxx flags.</para>
		/// <para>/</para>
		/// <para>/ All other fields are currently reserved and should not be modified.</para>
		/// <para>/</para>
		/// <para>/ The library will take ownership of the message structures.  They may</para>
		/// <para>/ be modified or become invalid at any time, so you must not read them</para>
		/// <para>/ after passing them to this function.</para>
		/// <para>/</para>
		/// <para>/ pOutMessageNumberOrResult is an optional array that will receive,</para>
		/// <para>/ for each message, the message number that was assigned to the message</para>
		/// <para>/ if sending was successful.  If sending failed, then a negative EResult</para>
		/// <para>/ value is placed into the array.  For example, the array will hold</para>
		/// <para>/ -k_EResultInvalidState if the connection was in an invalid state.</para>
		/// <para>/ See ISteamNetworkingSockets::SendMessageToConnection for possible</para>
		/// <para>/ failure codes.</para>
		/// </summary>
		public static void SendMessages(int nMessages, IntPtr[] pMessages, long[] pOutMessageNumberOrResult) {
			InteropHelp.TestIfAvailableClient();
			NativeMethods.ISteamNetworkingSockets_SendMessages(CSteamAPIContext.GetSteamNetworkingSockets(), nMessages, pMessages, pOutMessageNumberOrResult);
		}

		/// <summary>
		/// <para>/ Flush any messages waiting on the Nagle timer and send them</para>
		/// <para>/ at the next transmission opportunity (often that means right now).</para>
		/// <para>/</para>
		/// <para>/ If Nagle is enabled (it's on by default) then when calling</para>
		/// <para>/ SendMessageToConnection the message will be buffered, up to the Nagle time</para>
		/// <para>/ before being sent, to merge small messages into the same packet.</para>
		/// <para>/ (See k_ESteamNetworkingConfig_NagleTime)</para>
		/// <para>/</para>
		/// <para>/ Returns:</para>
		/// <para>/ k_EResultInvalidParam: invalid connection handle</para>
		/// <para>/ k_EResultInvalidState: connection is in an invalid state</para>
		/// <para>/ k_EResultNoConnection: connection has ended</para>
		/// <para>/ k_EResultIgnored: We weren't (yet) connected, so this operation has no effect.</para>
		/// </summary>
		public static EResult FlushMessagesOnConnection(HSteamNetConnection hConn) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_FlushMessagesOnConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hConn);
		}

		/// <summary>
		/// <para>/ Fetch the next available message(s) from the connection, if any.</para>
		/// <para>/ Returns the number of messages returned into your array, up to nMaxMessages.</para>
		/// <para>/ If the connection handle is invalid, -1 is returned.</para>
		/// <para>/</para>
		/// <para>/ The order of the messages returned in the array is relevant.</para>
		/// <para>/ Reliable messages will be received in the order they were sent (and with the</para>
		/// <para>/ same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket).</para>
		/// <para>/</para>
		/// <para>/ Unreliable messages may be dropped, or delivered out of order with respect to</para>
		/// <para>/ each other or with respect to reliable messages.  The same unreliable message</para>
		/// <para>/ may be received multiple times.</para>
		/// <para>/</para>
		/// <para>/ If any messages are returned, you MUST call SteamNetworkingMessage_t::Release() on each</para>
		/// <para>/ of them free up resources after you are done.  It is safe to keep the object alive for</para>
		/// <para>/ a little while (put it into some queue, etc), and you may call Release() from any thread.</para>
		/// </summary>
		public static int ReceiveMessagesOnConnection(HSteamNetConnection hConn, IntPtr[] ppOutMessages, int nMaxMessages) {
			InteropHelp.TestIfAvailableClient();
			if (ppOutMessages != null && ppOutMessages.Length != nMaxMessages) {
				throw new System.ArgumentException("ppOutMessages must be the same size as nMaxMessages!");
			}
			return NativeMethods.ISteamNetworkingSockets_ReceiveMessagesOnConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, ppOutMessages, nMaxMessages);
		}

		/// <summary>
		/// <para>/ Returns basic information about the high-level state of the connection.</para>
		/// </summary>
		public static bool GetConnectionInfo(HSteamNetConnection hConn, out SteamNetConnectionInfo_t pInfo) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetConnectionInfo(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, out pInfo);
		}

		/// <summary>
		/// <para>/ Returns a small set of information about the real-time state of the connection</para>
		/// <para>/ and the queue status of each lane.</para>
		/// <para>/</para>
		/// <para>/ - pStatus may be NULL if the information is not desired.  (E.g. you are only interested</para>
		/// <para>/   in the lane information.)</para>
		/// <para>/ - On entry, nLanes specifies the length of the pLanes array.  This may be 0</para>
		/// <para>/   if you do not wish to receive any lane data.  It's OK for this to be smaller than</para>
		/// <para>/   the total number of configured lanes.</para>
		/// <para>/ - pLanes points to an array that will receive lane-specific info.  It can be NULL</para>
		/// <para>/   if this is not needed.</para>
		/// <para>/</para>
		/// <para>/ Return value:</para>
		/// <para>/ - k_EResultNoConnection - connection handle is invalid or connection has been closed.</para>
		/// <para>/ - k_EResultInvalidParam - nLanes is bad</para>
		/// </summary>
		public static EResult GetConnectionRealTimeStatus(HSteamNetConnection hConn, ref SteamNetConnectionRealTimeStatus_t pStatus, int nLanes, ref SteamNetConnectionRealTimeLaneStatus_t pLanes) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetConnectionRealTimeStatus(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, ref pStatus, nLanes, ref pLanes);
		}

		/// <summary>
		/// <para>/ Returns detailed connection stats in text format.  Useful</para>
		/// <para>/ for dumping to a log, etc.</para>
		/// <para>/</para>
		/// <para>/ Returns:</para>
		/// <para>/ -1 failure (bad connection handle)</para>
		/// <para>/ 0 OK, your buffer was filled in and '\0'-terminated</para>
		/// <para>/ &gt;0 Your buffer was either nullptr, or it was too small and the text got truncated.</para>
		/// <para>/    Try again with a buffer of at least N bytes.</para>
		/// </summary>
		public static int GetDetailedConnectionStatus(HSteamNetConnection hConn, out string pszBuf, int cbBuf) {
			InteropHelp.TestIfAvailableClient();
			IntPtr pszBuf2 = Marshal.AllocHGlobal(cbBuf);
			int ret = NativeMethods.ISteamNetworkingSockets_GetDetailedConnectionStatus(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, pszBuf2, cbBuf);
			pszBuf = ret != -1 ? InteropHelp.PtrToStringUTF8(pszBuf2) : null;
			Marshal.FreeHGlobal(pszBuf2);
			return ret;
		}

		/// <summary>
		/// <para>/ Returns local IP and port that a listen socket created using CreateListenSocketIP is bound to.</para>
		/// <para>/</para>
		/// <para>/ An IPv6 address of ::0 means "any IPv4 or IPv6"</para>
		/// <para>/ An IPv6 address of ::ffff:0000:0000 means "any IPv4"</para>
		/// </summary>
		public static bool GetListenSocketAddress(HSteamListenSocket hSocket, out SteamNetworkingIPAddr address) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetListenSocketAddress(CSteamAPIContext.GetSteamNetworkingSockets(), hSocket, out address);
		}

		/// <summary>
		/// <para>/ Create a pair of connections that are talking to each other, e.g. a loopback connection.</para>
		/// <para>/ This is very useful for testing, or so that your client/server code can work the same</para>
		/// <para>/ even when you are running a local "server".</para>
		/// <para>/</para>
		/// <para>/ The two connections will immediately be placed into the connected state, and no callbacks</para>
		/// <para>/ will be posted immediately.  After this, if you close either connection, the other connection</para>
		/// <para>/ will receive a callback, exactly as if they were communicating over the network.  You must</para>
		/// <para>/ close *both* sides in order to fully clean up the resources!</para>
		/// <para>/</para>
		/// <para>/ By default, internal buffers are used, completely bypassing the network, the chopping up of</para>
		/// <para>/ messages into packets, encryption, copying the payload, etc.  This means that loopback</para>
		/// <para>/ packets, by default, will not simulate lag or loss.  Passing true for bUseNetworkLoopback will</para>
		/// <para>/ cause the socket pair to send packets through the local network loopback device (127.0.0.1)</para>
		/// <para>/ on ephemeral ports.  Fake lag and loss are supported in this case, and CPU time is expended</para>
		/// <para>/ to encrypt and decrypt.</para>
		/// <para>/</para>
		/// <para>/ If you wish to assign a specific identity to either connection, you may pass a particular</para>
		/// <para>/ identity.  Otherwise, if you pass nullptr, the respective connection will assume a generic</para>
		/// <para>/ "localhost" identity.  If you use real network loopback, this might be translated to the</para>
		/// <para>/ actual bound loopback port.  Otherwise, the port will be zero.</para>
		/// </summary>
		public static bool CreateSocketPair(out HSteamNetConnection pOutConnection1, out HSteamNetConnection pOutConnection2, bool bUseNetworkLoopback, ref SteamNetworkingIdentity pIdentity1, ref SteamNetworkingIdentity pIdentity2) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_CreateSocketPair(CSteamAPIContext.GetSteamNetworkingSockets(), out pOutConnection1, out pOutConnection2, bUseNetworkLoopback, ref pIdentity1, ref pIdentity2);
		}

		/// <summary>
		/// <para>/ Configure multiple outbound messages streams ("lanes") on a connection, and</para>
		/// <para>/ control head-of-line blocking between them.  Messages within a given lane</para>
		/// <para>/ are always sent in the order they are queued, but messages from different</para>
		/// <para>/ lanes may be sent out of order.  Each lane has its own message number</para>
		/// <para>/ sequence.  The first message sent on each lane will be assigned the number 1.</para>
		/// <para>/</para>
		/// <para>/ Each lane has a "priority".  Lanes with higher numeric values will only be processed</para>
		/// <para>/ when all lanes with lower number values are empty.  The magnitudes of the priority</para>
		/// <para>/ values are not relevant, only their sort order.</para>
		/// <para>/</para>
		/// <para>/ Each lane also is assigned a weight, which controls the approximate proportion</para>
		/// <para>/ of the bandwidth that will be consumed by the lane, relative to other lanes</para>
		/// <para>/ of the same priority.  (This is assuming the lane stays busy.  An idle lane</para>
		/// <para>/ does not build up "credits" to be be spent once a message is queued.)</para>
		/// <para>/ This value is only meaningful as a proportion, relative to other lanes with</para>
		/// <para>/ the same priority.  For lanes with different priorities, the strict priority</para>
		/// <para>/ order will prevail, and their weights relative to each other are not relevant.</para>
		/// <para>/ Thus, if a lane has a unique priority value, the weight value for that lane is</para>
		/// <para>/ not relevant.</para>
		/// <para>/</para>
		/// <para>/ Example: 3 lanes, with priorities [ 0, 10, 10 ] and weights [ (NA), 20, 5 ].</para>
		/// <para>/ Messages sent on the first will always be sent first, before messages in the</para>
		/// <para>/ other two lanes.  Its weight value is irrelevant, since there are no other</para>
		/// <para>/ lanes with priority=0.  The other two lanes will share bandwidth, with the second</para>
		/// <para>/ and third lanes sharing bandwidth using a ratio of approximately 4:1.</para>
		/// <para>/ (The weights [ NA, 4, 1 ] would be equivalent.)</para>
		/// <para>/</para>
		/// <para>/ Notes:</para>
		/// <para>/ - At the time of this writing, some code has performance cost that is linear</para>
		/// <para>/   in the number of lanes, so keep the number of lanes to an absolute minimum.</para>
		/// <para>/   3 or so is fine; &gt;8 is a lot.  The max number of lanes on Steam is 255,</para>
		/// <para>/   which is a very large number and not recommended!  If you are compiling this</para>
		/// <para>/   library from source, see STEAMNETWORKINGSOCKETS_MAX_LANES.)</para>
		/// <para>/ - Lane priority values may be any int.  Their absolute value is not relevant,</para>
		/// <para>/   only the order matters.</para>
		/// <para>/ - Weights must be positive, and due to implementation details, they are restricted</para>
		/// <para>/   to 16-bit values.  The absolute magnitudes don't matter, just the proportions.</para>
		/// <para>/ - Messages sent on a lane index other than 0 have a small overhead on the wire,</para>
		/// <para>/   so for maximum wire efficiency, lane 0 should be the "most common" lane, regardless</para>
		/// <para>/   of priorities or weights.</para>
		/// <para>/ - A connection has a single lane by default.  Calling this function with</para>
		/// <para>/   nNumLanes=1 is legal, but pointless, since the priority and weight values are</para>
		/// <para>/   irrelevant in that case.</para>
		/// <para>/ - You may reconfigure connection lanes at any time, however reducing the number of</para>
		/// <para>/   lanes is not allowed.</para>
		/// <para>/ - Reconfiguring lanes might restart any bandwidth sharing balancing.  Usually you</para>
		/// <para>/   will call this function once, near the start of the connection, perhaps after</para>
		/// <para>/   exchanging a few messages.</para>
		/// <para>/ - To assign all lanes the same priority, you may use pLanePriorities=NULL.</para>
		/// <para>/ - If you wish all lanes with the same priority to share bandwidth equally (or</para>
		/// <para>/   if no two lanes have the same priority value, and thus priority values are</para>
		/// <para>/   irrelevant), you may use pLaneWeights=NULL</para>
		/// <para>/ - Priorities and weights determine the order that messages are SENT on the wire.</para>
		/// <para>/   There are NO GUARANTEES on the order that messages are RECEIVED!  Due to packet</para>
		/// <para>/   loss, out-of-order delivery, and subtle details of packet serialization, messages</para>
		/// <para>/   might still be received slightly out-of-order!  The *only* strong guarantee is that</para>
		/// <para>/   *reliable* messages on the *same lane* will be delivered in the order they are sent.</para>
		/// <para>/ - Each host configures the lanes for the packets they send; the lanes for the flow</para>
		/// <para>/   in one direction are completely unrelated to the lanes in the opposite direction.</para>
		/// <para>/</para>
		/// <para>/ Return value:</para>
		/// <para>/ - k_EResultNoConnection - bad hConn</para>
		/// <para>/ - k_EResultInvalidParam - Invalid number of lanes, bad weights, or you tried to reduce the number of lanes</para>
		/// <para>/ - k_EResultInvalidState - Connection is already dead, etc</para>
		/// <para>/</para>
		/// <para>/ See also:</para>
		/// <para>/ SteamNetworkingMessage_t::m_idxLane</para>
		/// </summary>
		public static EResult ConfigureConnectionLanes(HSteamNetConnection hConn, int nNumLanes, int[] pLanePriorities, ushort[] pLaneWeights) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_ConfigureConnectionLanes(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, nNumLanes, pLanePriorities, pLaneWeights);
		}

		/// <summary>
		/// <para> Identity and authentication</para>
		/// <para>/ Get the identity assigned to this interface.</para>
		/// <para>/ E.g. on Steam, this is the user's SteamID, or for the gameserver interface, the SteamID assigned</para>
		/// <para>/ to the gameserver.  Returns false and sets the result to an invalid identity if we don't know</para>
		/// <para>/ our identity yet.  (E.g. GameServer has not logged in.  On Steam, the user will know their SteamID</para>
		/// <para>/ even if they are not signed into Steam.)</para>
		/// </summary>
		public static bool GetIdentity(out SteamNetworkingIdentity pIdentity) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetIdentity(CSteamAPIContext.GetSteamNetworkingSockets(), out pIdentity);
		}

		/// <summary>
		/// <para>/ Indicate our desire to be ready participate in authenticated communications.</para>
		/// <para>/ If we are currently not ready, then steps will be taken to obtain the necessary</para>
		/// <para>/ certificates.   (This includes a certificate for us, as well as any CA certificates</para>
		/// <para>/ needed to authenticate peers.)</para>
		/// <para>/</para>
		/// <para>/ You can call this at program init time if you know that you are going to</para>
		/// <para>/ be making authenticated connections, so that we will be ready immediately when</para>
		/// <para>/ those connections are attempted.  (Note that essentially all connections require</para>
		/// <para>/ authentication, with the exception of ordinary UDP connections with authentication</para>
		/// <para>/ disabled using k_ESteamNetworkingConfig_IP_AllowWithoutAuth.)  If you don't call</para>
		/// <para>/ this function, we will wait until a feature is utilized that that necessitates</para>
		/// <para>/ these resources.</para>
		/// <para>/</para>
		/// <para>/ You can also call this function to force a retry, if failure has occurred.</para>
		/// <para>/ Once we make an attempt and fail, we will not automatically retry.</para>
		/// <para>/ In this respect, the behavior of the system after trying and failing is the same</para>
		/// <para>/ as before the first attempt: attempting authenticated communication or calling</para>
		/// <para>/ this function will call the system to attempt to acquire the necessary resources.</para>
		/// <para>/</para>
		/// <para>/ You can use GetAuthenticationStatus or listen for SteamNetAuthenticationStatus_t</para>
		/// <para>/ to monitor the status.</para>
		/// <para>/</para>
		/// <para>/ Returns the current value that would be returned from GetAuthenticationStatus.</para>
		/// </summary>
		public static ESteamNetworkingAvailability InitAuthentication() {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_InitAuthentication(CSteamAPIContext.GetSteamNetworkingSockets());
		}

		/// <summary>
		/// <para>/ Query our readiness to participate in authenticated communications.  A</para>
		/// <para>/ SteamNetAuthenticationStatus_t callback is posted any time this status changes,</para>
		/// <para>/ but you can use this function to query it at any time.</para>
		/// <para>/</para>
		/// <para>/ The value of SteamNetAuthenticationStatus_t::m_eAvail is returned.  If you only</para>
		/// <para>/ want this high level status, you can pass NULL for pDetails.  If you want further</para>
		/// <para>/ details, pass non-NULL to receive them.</para>
		/// </summary>
		public static ESteamNetworkingAvailability GetAuthenticationStatus(out SteamNetAuthenticationStatus_t pDetails) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetAuthenticationStatus(CSteamAPIContext.GetSteamNetworkingSockets(), out pDetails);
		}

		/// <summary>
		/// <para> Poll groups.  A poll group is a set of connections that can be polled efficiently.</para>
		/// <para> (In our API, to "poll" a connection means to retrieve all pending messages.  We</para>
		/// <para> actually don't have an API to "poll" the connection *state*, like BSD sockets.)</para>
		/// <para>/ Create a new poll group.</para>
		/// <para>/</para>
		/// <para>/ You should destroy the poll group when you are done using DestroyPollGroup</para>
		/// </summary>
		public static HSteamNetPollGroup CreatePollGroup() {
			InteropHelp.TestIfAvailableClient();
			return (HSteamNetPollGroup)NativeMethods.ISteamNetworkingSockets_CreatePollGroup(CSteamAPIContext.GetSteamNetworkingSockets());
		}

		/// <summary>
		/// <para>/ Destroy a poll group created with CreatePollGroup().</para>
		/// <para>/</para>
		/// <para>/ If there are any connections in the poll group, they are removed from the group,</para>
		/// <para>/ and left in a state where they are not part of any poll group.</para>
		/// <para>/ Returns false if passed an invalid poll group handle.</para>
		/// </summary>
		public static bool DestroyPollGroup(HSteamNetPollGroup hPollGroup) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_DestroyPollGroup(CSteamAPIContext.GetSteamNetworkingSockets(), hPollGroup);
		}

		/// <summary>
		/// <para>/ Assign a connection to a poll group.  Note that a connection may only belong to a</para>
		/// <para>/ single poll group.  Adding a connection to a poll group implicitly removes it from</para>
		/// <para>/ any other poll group it is in.</para>
		/// <para>/</para>
		/// <para>/ You can pass k_HSteamNetPollGroup_Invalid to remove a connection from its current</para>
		/// <para>/ poll group without adding it to a new poll group.</para>
		/// <para>/</para>
		/// <para>/ If there are received messages currently pending on the connection, an attempt</para>
		/// <para>/ is made to add them to the queue of messages for the poll group in approximately</para>
		/// <para>/ the order that would have applied if the connection was already part of the poll</para>
		/// <para>/ group at the time that the messages were received.</para>
		/// <para>/</para>
		/// <para>/ Returns false if the connection handle is invalid, or if the poll group handle</para>
		/// <para>/ is invalid (and not k_HSteamNetPollGroup_Invalid).</para>
		/// </summary>
		public static bool SetConnectionPollGroup(HSteamNetConnection hConn, HSteamNetPollGroup hPollGroup) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_SetConnectionPollGroup(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, hPollGroup);
		}

		/// <summary>
		/// <para>/ Same as ReceiveMessagesOnConnection, but will return the next messages available</para>
		/// <para>/ on any connection in the poll group.  Examine SteamNetworkingMessage_t::m_conn</para>
		/// <para>/ to know which connection.  (SteamNetworkingMessage_t::m_nConnUserData might also</para>
		/// <para>/ be useful.)</para>
		/// <para>/</para>
		/// <para>/ Delivery order of messages among different connections will usually match the</para>
		/// <para>/ order that the last packet was received which completed the message.  But this</para>
		/// <para>/ is not a strong guarantee, especially for packets received right as a connection</para>
		/// <para>/ is being assigned to poll group.</para>
		/// <para>/</para>
		/// <para>/ Delivery order of messages on the same connection is well defined and the</para>
		/// <para>/ same guarantees are present as mentioned in ReceiveMessagesOnConnection.</para>
		/// <para>/ (But the messages are not grouped by connection, so they will not necessarily</para>
		/// <para>/ appear consecutively in the list; they may be interleaved with messages for</para>
		/// <para>/ other connections.)</para>
		/// </summary>
		public static int ReceiveMessagesOnPollGroup(HSteamNetPollGroup hPollGroup, IntPtr[] ppOutMessages, int nMaxMessages) {
			InteropHelp.TestIfAvailableClient();
			if (ppOutMessages != null && ppOutMessages.Length != nMaxMessages) {
				throw new System.ArgumentException("ppOutMessages must be the same size as nMaxMessages!");
			}
			return NativeMethods.ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(CSteamAPIContext.GetSteamNetworkingSockets(), hPollGroup, ppOutMessages, nMaxMessages);
		}

		/// <summary>
		/// <para> Clients connecting to dedicated servers hosted in a data center,</para>
		/// <para> using tickets issued by your game coordinator.  If you are not</para>
		/// <para> issuing your own tickets to restrict who can attempt to connect</para>
		/// <para> to your server, then you won't use these functions.</para>
		/// <para>/ Call this when you receive a ticket from your backend / matchmaking system.  Puts the</para>
		/// <para>/ ticket into a persistent cache, and optionally returns the parsed ticket.</para>
		/// <para>/</para>
		/// <para>/ See stamdatagram_ticketgen.h for more details.</para>
		/// </summary>
		public static bool ReceivedRelayAuthTicket(IntPtr pvTicket, int cbTicket, out SteamDatagramRelayAuthTicket pOutParsedTicket) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_ReceivedRelayAuthTicket(CSteamAPIContext.GetSteamNetworkingSockets(), pvTicket, cbTicket, out pOutParsedTicket);
		}

		/// <summary>
		/// <para>/ Search cache for a ticket to talk to the server on the specified virtual port.</para>
		/// <para>/ If found, returns the number of seconds until the ticket expires, and optionally</para>
		/// <para>/ the complete cracked ticket.  Returns 0 if we don't have a ticket.</para>
		/// <para>/</para>
		/// <para>/ Typically this is useful just to confirm that you have a ticket, before you</para>
		/// <para>/ call ConnectToHostedDedicatedServer to connect to the server.</para>
		/// </summary>
		public static int FindRelayAuthTicketForServer(ref SteamNetworkingIdentity identityGameServer, int nRemoteVirtualPort, out SteamDatagramRelayAuthTicket pOutParsedTicket) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_FindRelayAuthTicketForServer(CSteamAPIContext.GetSteamNetworkingSockets(), ref identityGameServer, nRemoteVirtualPort, out pOutParsedTicket);
		}

		/// <summary>
		/// <para>/ Client call to connect to a server hosted in a Valve data center, on the specified virtual</para>
		/// <para>/ port.  You must have placed a ticket for this server into the cache, or else this connect</para>
		/// <para>/ attempt will fail!  If you are not issuing your own tickets, then to connect to a dedicated</para>
		/// <para>/ server via SDR in auto-ticket mode, use ConnectP2P.  (The server must be configured to allow</para>
		/// <para>/ this type of connection by listening using CreateListenSocketP2P.)</para>
		/// <para>/</para>
		/// <para>/ You may wonder why tickets are stored in a cache, instead of simply being passed as an argument</para>
		/// <para>/ here.  The reason is to make reconnection to a gameserver robust, even if the client computer loses</para>
		/// <para>/ connection to Steam or the central backend, or the app is restarted or crashes, etc.</para>
		/// <para>/</para>
		/// <para>/ If you use this, you probably want to call ISteamNetworkingUtils::InitRelayNetworkAccess()</para>
		/// <para>/ when your app initializes</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// </summary>
		public static HSteamNetConnection ConnectToHostedDedicatedServer(ref SteamNetworkingIdentity identityTarget, int nRemoteVirtualPort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamNetConnection)NativeMethods.ISteamNetworkingSockets_ConnectToHostedDedicatedServer(CSteamAPIContext.GetSteamNetworkingSockets(), ref identityTarget, nRemoteVirtualPort, nOptions, pOptions);
		}

		/// <summary>
		/// <para> Servers hosted in data centers known to the Valve relay network</para>
		/// <para>/ Returns the value of the SDR_LISTEN_PORT environment variable.  This</para>
		/// <para>/ is the UDP server your server will be listening on.  This will</para>
		/// <para>/ configured automatically for you in production environments.</para>
		/// <para>/</para>
		/// <para>/ In development, you'll need to set it yourself.  See</para>
		/// <para>/ https://partner.steamgames.com/doc/api/ISteamNetworkingSockets</para>
		/// <para>/ for more information on how to configure dev environments.</para>
		/// </summary>
		public static ushort GetHostedDedicatedServerPort() {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetHostedDedicatedServerPort(CSteamAPIContext.GetSteamNetworkingSockets());
		}

		/// <summary>
		/// <para>/ Returns 0 if SDR_LISTEN_PORT is not set.  Otherwise, returns the data center the server</para>
		/// <para>/ is running in.  This will be k_SteamDatagramPOPID_dev in non-production environment.</para>
		/// </summary>
		public static SteamNetworkingPOPID GetHostedDedicatedServerPOPID() {
			InteropHelp.TestIfAvailableClient();
			return (SteamNetworkingPOPID)NativeMethods.ISteamNetworkingSockets_GetHostedDedicatedServerPOPID(CSteamAPIContext.GetSteamNetworkingSockets());
		}

		/// <summary>
		/// <para>/ Return info about the hosted server.  This contains the PoPID of the server,</para>
		/// <para>/ and opaque routing information that can be used by the relays to send traffic</para>
		/// <para>/ to your server.</para>
		/// <para>/</para>
		/// <para>/ You will need to send this information to your backend, and put it in tickets,</para>
		/// <para>/ so that the relays will know how to forward traffic from</para>
		/// <para>/ clients to your server.  See SteamDatagramRelayAuthTicket for more info.</para>
		/// <para>/</para>
		/// <para>/ Also, note that the routing information is contained in SteamDatagramGameCoordinatorServerLogin,</para>
		/// <para>/ so if possible, it's preferred to use GetGameCoordinatorServerLogin to send this info</para>
		/// <para>/ to your game coordinator service, and also login securely at the same time.</para>
		/// <para>/</para>
		/// <para>/ On a successful exit, k_EResultOK is returned</para>
		/// <para>/</para>
		/// <para>/ Unsuccessful exit:</para>
		/// <para>/ - Something other than k_EResultOK is returned.</para>
		/// <para>/ - k_EResultInvalidState: We are not configured to listen for SDR (SDR_LISTEN_SOCKET</para>
		/// <para>/   is not set.)</para>
		/// <para>/ - k_EResultPending: we do not (yet) have the authentication information needed.</para>
		/// <para>/   (See GetAuthenticationStatus.)  If you use environment variables to pre-fetch</para>
		/// <para>/   the network config, this data should always be available immediately.</para>
		/// <para>/ - A non-localized diagnostic debug message will be placed in m_data that describes</para>
		/// <para>/   the cause of the failure.</para>
		/// <para>/</para>
		/// <para>/ NOTE: The returned blob is not encrypted.  Send it to your backend, but don't</para>
		/// <para>/       directly share it with clients.</para>
		/// </summary>
		public static EResult GetHostedDedicatedServerAddress(out SteamDatagramHostedAddress pRouting) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetHostedDedicatedServerAddress(CSteamAPIContext.GetSteamNetworkingSockets(), out pRouting);
		}

		/// <summary>
		/// <para>/ Create a listen socket on the specified virtual port.  The physical UDP port to use</para>
		/// <para>/ will be determined by the SDR_LISTEN_PORT environment variable.  If a UDP port is not</para>
		/// <para>/ configured, this call will fail.</para>
		/// <para>/</para>
		/// <para>/ This call MUST be made through the SteamGameServerNetworkingSockets() interface.</para>
		/// <para>/</para>
		/// <para>/ This function should be used when you are using the ticket generator library</para>
		/// <para>/ to issue your own tickets.  Clients connecting to the server on this virtual</para>
		/// <para>/ port will need a ticket, and they must connect using ConnectToHostedDedicatedServer.</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// </summary>
		public static HSteamListenSocket CreateHostedDedicatedServerListenSocket(int nLocalVirtualPort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamListenSocket)NativeMethods.ISteamNetworkingSockets_CreateHostedDedicatedServerListenSocket(CSteamAPIContext.GetSteamNetworkingSockets(), nLocalVirtualPort, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Generate an authentication blob that can be used to securely login with</para>
		/// <para>/ your backend, using SteamDatagram_ParseHostedServerLogin.  (See</para>
		/// <para>/ steamdatagram_gamecoordinator.h)</para>
		/// <para>/</para>
		/// <para>/ Before calling the function:</para>
		/// <para>/ - Populate the app data in pLoginInfo (m_cbAppData and m_appData).  You can leave</para>
		/// <para>/   all other fields uninitialized.</para>
		/// <para>/ - *pcbSignedBlob contains the size of the buffer at pBlob.  (It should be</para>
		/// <para>/   at least k_cbMaxSteamDatagramGameCoordinatorServerLoginSerialized.)</para>
		/// <para>/</para>
		/// <para>/ On a successful exit:</para>
		/// <para>/ - k_EResultOK is returned</para>
		/// <para>/ - All of the remaining fields of pLoginInfo will be filled out.</para>
		/// <para>/ - *pcbSignedBlob contains the size of the serialized blob that has been</para>
		/// <para>/   placed into pBlob.</para>
		/// <para>/</para>
		/// <para>/ Unsuccessful exit:</para>
		/// <para>/ - Something other than k_EResultOK is returned.</para>
		/// <para>/ - k_EResultNotLoggedOn: you are not logged in (yet)</para>
		/// <para>/ - See GetHostedDedicatedServerAddress for more potential failure return values.</para>
		/// <para>/ - A non-localized diagnostic debug message will be placed in pBlob that describes</para>
		/// <para>/   the cause of the failure.</para>
		/// <para>/</para>
		/// <para>/ This works by signing the contents of the SteamDatagramGameCoordinatorServerLogin</para>
		/// <para>/ with the cert that is issued to this server.  In dev environments, it's OK if you do</para>
		/// <para>/ not have a cert.  (You will need to enable insecure dev login in SteamDatagram_ParseHostedServerLogin.)</para>
		/// <para>/ Otherwise, you will need a signed cert.</para>
		/// <para>/</para>
		/// <para>/ NOTE: The routing blob returned here is not encrypted.  Send it to your backend</para>
		/// <para>/       and don't share it directly with clients.</para>
		/// </summary>
		public static EResult GetGameCoordinatorServerLogin(IntPtr pLoginInfo, out int pcbSignedBlob, IntPtr pBlob) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetGameCoordinatorServerLogin(CSteamAPIContext.GetSteamNetworkingSockets(), pLoginInfo, out pcbSignedBlob, pBlob);
		}

		/// <summary>
		/// <para> Relayed connections using custom signaling protocol</para>
		/// <para> This is used if you have your own method of sending out-of-band</para>
		/// <para> signaling / rendezvous messages through a mutually trusted channel.</para>
		/// <para>/ Create a P2P "client" connection that does signaling over a custom</para>
		/// <para>/ rendezvous/signaling channel.</para>
		/// <para>/</para>
		/// <para>/ pSignaling points to a new object that you create just for this connection.</para>
		/// <para>/ It must stay valid until Release() is called.  Once you pass the</para>
		/// <para>/ object to this function, it assumes ownership.  Release() will be called</para>
		/// <para>/ from within the function call if the call fails.  Furthermore, until Release()</para>
		/// <para>/ is called, you should be prepared for methods to be invoked on your</para>
		/// <para>/ object from any thread!  You need to make sure your object is threadsafe!</para>
		/// <para>/ Furthermore, you should make sure that dispatching the methods is done</para>
		/// <para>/ as quickly as possible.</para>
		/// <para>/</para>
		/// <para>/ This function will immediately construct a connection in the "connecting"</para>
		/// <para>/ state.  Soon after (perhaps before this function returns, perhaps in another thread),</para>
		/// <para>/ the connection will begin sending signaling messages by calling</para>
		/// <para>/ ISteamNetworkingConnectionSignaling::SendSignal.</para>
		/// <para>/</para>
		/// <para>/ When the remote peer accepts the connection (See</para>
		/// <para>/ ISteamNetworkingSignalingRecvContext::OnConnectRequest),</para>
		/// <para>/ it will begin sending signaling messages.  When these messages are received,</para>
		/// <para>/ you can pass them to the connection using ReceivedP2PCustomSignal.</para>
		/// <para>/</para>
		/// <para>/ If you know the identity of the peer that you expect to be on the other end,</para>
		/// <para>/ you can pass their identity to improve debug output or just detect bugs.</para>
		/// <para>/ If you don't know their identity yet, you can pass NULL, and their</para>
		/// <para>/ identity will be established in the connection handshake.</para>
		/// <para>/</para>
		/// <para>/ If you use this, you probably want to call ISteamNetworkingUtils::InitRelayNetworkAccess()</para>
		/// <para>/ when your app initializes</para>
		/// <para>/</para>
		/// <para>/ If you need to set any initial config options, pass them here.  See</para>
		/// <para>/ SteamNetworkingConfigValue_t for more about why this is preferable to</para>
		/// <para>/ setting the options "immediately" after creation.</para>
		/// </summary>
		public static HSteamNetConnection ConnectP2PCustomSignaling(out ISteamNetworkingConnectionSignaling pSignaling, ref SteamNetworkingIdentity pPeerIdentity, int nRemoteVirtualPort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamNetConnection)NativeMethods.ISteamNetworkingSockets_ConnectP2PCustomSignaling(CSteamAPIContext.GetSteamNetworkingSockets(), out pSignaling, ref pPeerIdentity, nRemoteVirtualPort, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ Called when custom signaling has received a message.  When your</para>
		/// <para>/ signaling channel receives a message, it should save off whatever</para>
		/// <para>/ routing information was in the envelope into the context object,</para>
		/// <para>/ and then pass the payload to this function.</para>
		/// <para>/</para>
		/// <para>/ A few different things can happen next, depending on the message:</para>
		/// <para>/</para>
		/// <para>/ - If the signal is associated with existing connection, it is dealt</para>
		/// <para>/   with immediately.  If any replies need to be sent, they will be</para>
		/// <para>/   dispatched using the ISteamNetworkingConnectionSignaling</para>
		/// <para>/   associated with the connection.</para>
		/// <para>/ - If the message represents a connection request (and the request</para>
		/// <para>/   is not redundant for an existing connection), a new connection</para>
		/// <para>/   will be created, and ReceivedConnectRequest will be called on your</para>
		/// <para>/   context object to determine how to proceed.</para>
		/// <para>/ - Otherwise, the message is for a connection that does not</para>
		/// <para>/   exist (anymore).  In this case, we *may* call SendRejectionReply</para>
		/// <para>/   on your context object.</para>
		/// <para>/</para>
		/// <para>/ In any case, we will not save off pContext or access it after this</para>
		/// <para>/ function returns.</para>
		/// <para>/</para>
		/// <para>/ Returns true if the message was parsed and dispatched without anything</para>
		/// <para>/ unusual or suspicious happening.  Returns false if there was some problem</para>
		/// <para>/ with the message that prevented ordinary handling.  (Debug output will</para>
		/// <para>/ usually have more information.)</para>
		/// <para>/</para>
		/// <para>/ If you expect to be using relayed connections, then you probably want</para>
		/// <para>/ to call ISteamNetworkingUtils::InitRelayNetworkAccess() when your app initializes</para>
		/// </summary>
		public static bool ReceivedP2PCustomSignal(IntPtr pMsg, int cbMsg, out ISteamNetworkingSignalingRecvContext pContext) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_ReceivedP2PCustomSignal(CSteamAPIContext.GetSteamNetworkingSockets(), pMsg, cbMsg, out pContext);
		}

		/// <summary>
		/// <para> Certificate provision by the application.  On Steam, we normally handle all this automatically</para>
		/// <para> and you will not need to use these advanced functions.</para>
		/// <para>/ Get blob that describes a certificate request.  You can send this to your game coordinator.</para>
		/// <para>/ Upon entry, *pcbBlob should contain the size of the buffer.  On successful exit, it will</para>
		/// <para>/ return the number of bytes that were populated.  You can pass pBlob=NULL to query for the required</para>
		/// <para>/ size.  (512 bytes is a conservative estimate.)</para>
		/// <para>/</para>
		/// <para>/ Pass this blob to your game coordinator and call SteamDatagram_CreateCert.</para>
		/// </summary>
		public static bool GetCertificateRequest(out int pcbBlob, IntPtr pBlob, out SteamNetworkingErrMsg errMsg) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetCertificateRequest(CSteamAPIContext.GetSteamNetworkingSockets(), out pcbBlob, pBlob, out errMsg);
		}

		/// <summary>
		/// <para>/ Set the certificate.  The certificate blob should be the output of</para>
		/// <para>/ SteamDatagram_CreateCert.</para>
		/// </summary>
		public static bool SetCertificate(IntPtr pCertificate, int cbCertificate, out SteamNetworkingErrMsg errMsg) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_SetCertificate(CSteamAPIContext.GetSteamNetworkingSockets(), pCertificate, cbCertificate, out errMsg);
		}

		/// <summary>
		/// <para>/ Reset the identity associated with this instance.</para>
		/// <para>/ Any open connections are closed.  Any previous certificates, etc are discarded.</para>
		/// <para>/ You can pass a specific identity that you want to use, or you can pass NULL,</para>
		/// <para>/ in which case the identity will be invalid until you set it using SetCertificate</para>
		/// <para>/</para>
		/// <para>/ NOTE: This function is not actually supported on Steam!  It is included</para>
		/// <para>/       for use on other platforms where the active user can sign out and</para>
		/// <para>/       a new user can sign in.</para>
		/// </summary>
		public static void ResetIdentity(ref SteamNetworkingIdentity pIdentity) {
			InteropHelp.TestIfAvailableClient();
			NativeMethods.ISteamNetworkingSockets_ResetIdentity(CSteamAPIContext.GetSteamNetworkingSockets(), ref pIdentity);
		}

		/// <summary>
		/// <para> Misc</para>
		/// <para>/ Invoke all callback functions queued for this interface.</para>
		/// <para>/ See k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, etc</para>
		/// <para>/</para>
		/// <para>/ You don't need to call this if you are using Steam's callback dispatch</para>
		/// <para>/ mechanism (SteamAPI_RunCallbacks and SteamGameserver_RunCallbacks).</para>
		/// </summary>
		public static void RunCallbacks() {
			InteropHelp.TestIfAvailableClient();
			NativeMethods.ISteamNetworkingSockets_RunCallbacks(CSteamAPIContext.GetSteamNetworkingSockets());
		}

		/// <summary>
		/// <para> "FakeIP" system.</para>
		/// <para> A FakeIP is essentially a temporary, arbitrary identifier that</para>
		/// <para> happens to be a valid IPv4 address.  The purpose of this system is to make it</para>
		/// <para> easy to integrate with existing code that identifies hosts using IPv4 addresses.</para>
		/// <para> The FakeIP address will never actually be used to send or receive any packets</para>
		/// <para> on the Internet, it is strictly an identifier.</para>
		/// <para> FakeIP addresses are designed to (hopefully) pass through existing code as</para>
		/// <para> transparently as possible, while conflicting with "real" addresses that might</para>
		/// <para> be in use on networks (both the Internet and LANs) in the same code as little</para>
		/// <para> as possible.  At the time this comment is being written, they come from the</para>
		/// <para> 169.254.0.0/16 range, and the port number will always be &gt;1024.  HOWEVER,</para>
		/// <para> this is subject to change!  Do not make assumptions about these addresses,</para>
		/// <para> or your code might break in the future.  In particular, you should use</para>
		/// <para> functions such as  ISteamNetworkingUtils::IsFakeIP to determine if an IP</para>
		/// <para> address is a "fake" one used by this system.</para>
		/// <para>/ Begin asynchronous process of allocating a fake IPv4 address that other</para>
		/// <para>/ peers can use to contact us via P2P.  IP addresses returned by this</para>
		/// <para>/ function are globally unique for a given appid.</para>
		/// <para>/</para>
		/// <para>/ nNumPorts is the numbers of ports you wish to reserve.  This is useful</para>
		/// <para>/ for the same reason that listening on multiple UDP ports is useful for</para>
		/// <para>/ different types of traffic.  Because these allocations come from a global</para>
		/// <para>/ namespace, there is a relatively strict limit on the maximum number of</para>
		/// <para>/ ports you may request.  (At the time of this writing, the limit is 4.)</para>
		/// <para>/ The port assignments are *not* guaranteed to have any particular order</para>
		/// <para>/ or relationship!  Do *not* assume they are contiguous, even though that</para>
		/// <para>/ may often occur in practice.</para>
		/// <para>/</para>
		/// <para>/ Returns false if a request was already in progress, true if a new request</para>
		/// <para>/ was started.  A SteamNetworkingFakeIPResult_t will be posted when the request</para>
		/// <para>/ completes.</para>
		/// <para>/</para>
		/// <para>/ For gameservers, you *must* call this after initializing the SDK but before</para>
		/// <para>/ beginning login.  Steam needs to know in advance that FakeIP will be used.</para>
		/// <para>/ Everywhere your public IP would normally appear (such as the server browser) will be</para>
		/// <para>/ replaced by the FakeIP, and the fake port at index 0.  The request is actually queued</para>
		/// <para>/ until the logon completes, so you must not wait until the allocation completes</para>
		/// <para>/ before logging in.  Except for trivial failures that can be detected locally</para>
		/// <para>/ (e.g. invalid parameter), a SteamNetworkingFakeIPResult_t callback (whether success or</para>
		/// <para>/ failure) will not be posted until after we have logged in.  Furthermore, it is assumed</para>
		/// <para>/ that FakeIP allocation is essential for your application to function, and so failure</para>
		/// <para>/ will not be reported until *several* retries have been attempted.  This process may</para>
		/// <para>/ last several minutes.  It is *highly* recommended to treat failure as fatal.</para>
		/// <para>/</para>
		/// <para>/ To communicate using a connection-oriented (TCP-style) API:</para>
		/// <para>/ - Server creates a listen socket using CreateListenSocketP2PFakeIP</para>
		/// <para>/ - Client connects using ConnectByIPAddress, passing in the FakeIP address.</para>
		/// <para>/ - The connection will behave mostly like a P2P connection.  The identities</para>
		/// <para>/   that appear in SteamNetConnectionInfo_t will be the FakeIP identity until</para>
		/// <para>/   we know the real identity.  Then it will be the real identity.  If the</para>
		/// <para>/   SteamNetConnectionInfo_t::m_addrRemote is valid, it will be a real IPv4</para>
		/// <para>/   address of a NAT-punched connection.  Otherwise, it will not be valid.</para>
		/// <para>/</para>
		/// <para>/ To communicate using an ad-hoc sendto/recv from (UDP-style) API,</para>
		/// <para>/ use CreateFakeUDPPort.</para>
		/// </summary>
		public static bool BeginAsyncRequestFakeIP(int nNumPorts) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_BeginAsyncRequestFakeIP(CSteamAPIContext.GetSteamNetworkingSockets(), nNumPorts);
		}

		/// <summary>
		/// <para>/ Return info about the FakeIP and port(s) that we have been assigned,</para>
		/// <para>/ if any.  idxFirstPort is currently reserved and must be zero.</para>
		/// <para>/ Make sure and check SteamNetworkingFakeIPResult_t::m_eResult</para>
		/// </summary>
		public static void GetFakeIP(int idxFirstPort, out SteamNetworkingFakeIPResult_t pInfo) {
			InteropHelp.TestIfAvailableClient();
			NativeMethods.ISteamNetworkingSockets_GetFakeIP(CSteamAPIContext.GetSteamNetworkingSockets(), idxFirstPort, out pInfo);
		}

		/// <summary>
		/// <para>/ Create a listen socket that will listen for P2P connections sent</para>
		/// <para>/ to our FakeIP.  A peer can initiate connections to this listen</para>
		/// <para>/ socket by calling ConnectByIPAddress.</para>
		/// <para>/</para>
		/// <para>/ idxFakePort refers to the *index* of the fake port requested,</para>
		/// <para>/ not the actual port number.  For example, pass 0 to refer to the</para>
		/// <para>/ first port in the reservation.  You must call this only after calling</para>
		/// <para>/ BeginAsyncRequestFakeIP.  However, you do not need to wait for the</para>
		/// <para>/ request to complete before creating the listen socket.</para>
		/// </summary>
		public static HSteamListenSocket CreateListenSocketP2PFakeIP(int idxFakePort, int nOptions, SteamNetworkingConfigValue_t[] pOptions) {
			InteropHelp.TestIfAvailableClient();
			return (HSteamListenSocket)NativeMethods.ISteamNetworkingSockets_CreateListenSocketP2PFakeIP(CSteamAPIContext.GetSteamNetworkingSockets(), idxFakePort, nOptions, pOptions);
		}

		/// <summary>
		/// <para>/ If the connection was initiated using the "FakeIP" system, then we</para>
		/// <para>/ we can get an IP address for the remote host.  If the remote host had</para>
		/// <para>/ a global FakeIP at the time the connection was established, this</para>
		/// <para>/ function will return that global IP.  Otherwise, a FakeIP that is</para>
		/// <para>/ unique locally will be allocated from the local FakeIP address space,</para>
		/// <para>/ and that will be returned.</para>
		/// <para>/</para>
		/// <para>/ The allocation of local FakeIPs attempts to assign addresses in</para>
		/// <para>/ a consistent manner.  If multiple connections are made to the</para>
		/// <para>/ same remote host, they *probably* will return the same FakeIP.</para>
		/// <para>/ However, since the namespace is limited, this cannot be guaranteed.</para>
		/// <para>/</para>
		/// <para>/ On failure, returns:</para>
		/// <para>/ - k_EResultInvalidParam: invalid connection handle</para>
		/// <para>/ - k_EResultIPNotFound: This connection wasn't made using FakeIP system</para>
		/// </summary>
		public static EResult GetRemoteFakeIPForConnection(HSteamNetConnection hConn, out SteamNetworkingIPAddr pOutAddr) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_GetRemoteFakeIPForConnection(CSteamAPIContext.GetSteamNetworkingSockets(), hConn, out pOutAddr);
		}

		/// <summary>
		/// <para>/ Get an interface that can be used like a UDP port to send/receive</para>
		/// <para>/ datagrams to a FakeIP address.  This is intended to make it easy</para>
		/// <para>/ to port existing UDP-based code to take advantage of SDR.</para>
		/// <para>/</para>
		/// <para>/ idxFakeServerPort refers to the *index* of the port allocated using</para>
		/// <para>/ BeginAsyncRequestFakeIP and is used to create "server" ports.  You may</para>
		/// <para>/ call this before the allocation has completed.  However, any attempts</para>
		/// <para>/ to send packets will fail until the allocation has succeeded.  When</para>
		/// <para>/ the peer receives packets sent from this interface, the from address</para>
		/// <para>/ of the packet will be the globally-unique FakeIP.  If you call this</para>
		/// <para>/ function multiple times and pass the same (nonnegative) fake port index,</para>
		/// <para>/ the same object will be returned, and this object is not reference counted.</para>
		/// <para>/</para>
		/// <para>/ To create a "client" port (e.g. the equivalent of an ephemeral UDP port)</para>
		/// <para>/ pass -1.  In this case, a distinct object will be returned for each call.</para>
		/// <para>/ When the peer receives packets sent from this interface, the peer will</para>
		/// <para>/ assign a FakeIP from its own locally-controlled namespace.</para>
		/// </summary>
		public static IntPtr CreateFakeUDPPort(int idxFakeServerPort) {
			InteropHelp.TestIfAvailableClient();
			return NativeMethods.ISteamNetworkingSockets_CreateFakeUDPPort(CSteamAPIContext.GetSteamNetworkingSockets(), idxFakeServerPort);
		}
	}
}

#endif // !DISABLESTEAMWORKS
