P2P, rough cut

This commit is contained in:
Garry Newman 2016-07-13 17:14:16 +01:00
parent 1014178db6
commit 19385ed951
7 changed files with 197 additions and 6 deletions

View File

@ -0,0 +1,40 @@
using System;
using System.Text;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Facepunch.Steamworks.Test
{
public partial class Client
{
[TestMethod]
public void PeerToPeerSend()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
var TestString = "This string will be transformed to bytes, sent over the Steam P2P network, then converted back to a string.";
var OutputReceived = false;
var data = Encoding.UTF8.GetBytes( TestString );
client.Networking.OnP2PData = ( steamid, ms, channel ) =>
{
var str = Encoding.UTF8.GetString( ms.GetBuffer() );
Assert.AreEqual( str, TestString );
Assert.AreEqual( steamid, client.SteamId );
OutputReceived = true;
};
client.Networking.SendP2PPacket( client.SteamId, data, data.Length );
while( true )
{
Thread.Sleep( 10 );
client.Update();
if ( OutputReceived )
break;
}
}
}
}
}

View File

@ -6,7 +6,7 @@ namespace Facepunch.Steamworks.Test
[TestClass] [TestClass]
[DeploymentItem( "FacepunchSteamworksApi.dll" )] [DeploymentItem( "FacepunchSteamworksApi.dll" )]
[DeploymentItem( "steam_appid.txt" )] [DeploymentItem( "steam_appid.txt" )]
public class Client public partial class Client
{ {
[TestMethod] [TestMethod]
public void Init() public void Init()

View File

@ -87,6 +87,7 @@
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<Compile Include="Client.cs" /> <Compile Include="Client.cs" />
<Compile Include="Client.Networking.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Valve.Steamworks;
namespace Facepunch.Steamworks
{
public partial class Client : IDisposable
{
private Networking _net;
public Networking Networking
{
get
{
if ( _net == null )
_net = new Networking( this );
return _net;
}
}
}
public class Networking
{
public Action<ulong, MemoryStream, int> OnP2PData;
internal Client client;
internal class Callback
{
internal delegate void P2PSessionRequest( P2PSessionRequest_t a );
internal delegate void P2PSessionConnectFail( P2PSessionConnectFail_t a );
}
internal Networking( Client c )
{
client = c;
{
Callback.P2PSessionRequest cb = onP2PConnectionRequest;
client.InstallCallback( Valve.Steamworks.SteamAPI.k_iSteamNetworkingCallbacks + 2, cb );
}
{
Callback.P2PSessionConnectFail cb = onP2PConnectionFailed;
client.InstallCallback( Valve.Steamworks.SteamAPI.k_iSteamNetworkingCallbacks + 2, cb );
}
}
internal void Update()
{
for ( int i = 0; i < 32; i++ )
{
// POOL ME
using ( var ms = new MemoryStream() )
{
while( ReadP2PPacket( ms, i ) )
{
// Nothing Here.
}
}
}
}
private void onP2PConnectionRequest( P2PSessionRequest_t o )
{
Console.WriteLine( "onP2PConnectionRequest " + o.m_steamIDRemote );
}
private void onP2PConnectionFailed( P2PSessionConnectFail_t o )
{
Console.WriteLine( "onP2PConnectionFailed " + o.m_steamIDRemote );
}
public enum EP2PSend : int
{
/// <summary>
/// Basic UDP send. Packets can't be bigger than 1200 bytes (your typical MTU size). Can be lost, or arrive out of order (rare).
/// The sending API does have some knowledge of the underlying connection, so if there is no NAT-traversal accomplished or
/// there is a recognized adjustment happening on the connection, the packet will be batched until the connection is open again.
/// </summary>
Unreliable = 0,
/// <summary>
/// As above, but if the underlying p2p connection isn't yet established the packet will just be thrown away. Using this on the first
/// packet sent to a remote host almost guarantees the packet will be dropped.
/// This is only really useful for kinds of data that should never buffer up, i.e. voice payload packets
/// </summary>
UnreliableNoDelay = 1,
//// <summary>
/// Reliable message send. Can send up to 1MB of data in a single message.
/// Does fragmentation/re-assembly of messages under the hood, as well as a sliding window for efficient sends of large chunks of data.
/// </summary>
Reliable = 2,
/// <summary>
/// As above, but applies the Nagle algorithm to the send - sends will accumulate
/// until the current MTU size (typically ~1200 bytes, but can change) or ~200ms has passed (Nagle algorithm).
/// Useful if you want to send a set of smaller messages but have the coalesced into a single packet
/// Since the reliable stream is all ordered, you can do several small message sends with k_EP2PSendReliableWithBuffering and then
/// do a normal k_EP2PSendReliable to force all the buffered data to be sent.
/// </summary>
ReliableWithBuffering = 3,
}
public unsafe bool SendP2PPacket( ulong steamid, byte[] data, int length, EP2PSend eP2PSendType = EP2PSend.Reliable, int nChannel = 0 )
{
fixed ( byte* p = data )
{
return client._networking.SendP2PPacket( steamid, (IntPtr) p, (uint)length, (uint)eP2PSendType, nChannel );
}
}
private unsafe bool ReadP2PPacket( MemoryStream ms, int channel = 0 )
{
uint DataAvailable = 0;
if ( !client._networking.IsP2PPacketAvailable( ref DataAvailable, channel ) || DataAvailable == 0 )
return false;
if ( ms.Capacity < DataAvailable )
ms.Capacity = (int) DataAvailable;
ms.Position = 0;
ms.SetLength( DataAvailable );
fixed ( byte* p = ms.GetBuffer() )
{
ulong steamid = 1;
if ( !client._networking.ReadP2PPacket( (IntPtr)p, (uint)DataAvailable, ref DataAvailable, ref steamid, channel ) || DataAvailable == 0 )
return false;
ms.SetLength( DataAvailable );
OnP2PData?.Invoke( steamid, ms, channel );
return true;
}
}
}
}

View File

@ -15,6 +15,7 @@ public partial class Client : IDisposable
internal Valve.Steamworks.ISteamFriends _friends; internal Valve.Steamworks.ISteamFriends _friends;
internal Valve.Steamworks.ISteamMatchmakingServers _servers; internal Valve.Steamworks.ISteamMatchmakingServers _servers;
internal Valve.Steamworks.ISteamInventory _inventory; internal Valve.Steamworks.ISteamInventory _inventory;
internal Valve.Steamworks.ISteamNetworking _networking;
/// <summary> /// <summary>
/// Current running program's AppId /// Current running program's AppId
@ -62,6 +63,7 @@ public Client( uint appId )
_user = _client.GetISteamUser( _huser, _hpipe, "SteamUser019" ); _user = _client.GetISteamUser( _huser, _hpipe, "SteamUser019" );
_servers = _client.GetISteamMatchmakingServers( _huser, _hpipe, "SteamMatchMakingServers002" ); _servers = _client.GetISteamMatchmakingServers( _huser, _hpipe, "SteamMatchMakingServers002" );
_inventory = _client.GetISteamInventory( _huser, _hpipe, "STEAMINVENTORY_INTERFACE_V001" ); _inventory = _client.GetISteamInventory( _huser, _hpipe, "STEAMINVENTORY_INTERFACE_V001" );
_networking = _client.GetISteamNetworking( _huser, _hpipe, "SteamNetworking005" );
AppId = appId; AppId = appId;
Username = _friends.GetPersonaName(); Username = _friends.GetPersonaName();
@ -106,6 +108,7 @@ public void Update()
Valve.Steamworks.SteamAPI.RunCallbacks(); Valve.Steamworks.SteamAPI.RunCallbacks();
Voice.Update(); Voice.Update();
Inventory.Update(); Inventory.Update();
Networking.Update();
} }
public bool Valid public bool Valid
@ -113,7 +116,7 @@ public bool Valid
get { return _client != null; } get { return _client != null; }
} }
internal Action InstallCallback<T>( int type, Action<T> action ) internal Action InstallCallback( int type, Delegate action )
{ {
var ptr = Marshal.GetFunctionPointerForDelegate( action ); var ptr = Marshal.GetFunctionPointerForDelegate( action );
Valve.Steamworks.SteamAPI.RegisterCallback( ptr, type ); Valve.Steamworks.SteamAPI.RegisterCallback( ptr, type );

View File

@ -113,6 +113,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Client.Networking.cs" />
<Compile Include="Client.Overlay.cs" /> <Compile Include="Client.Overlay.cs" />
<Compile Include="Client.ServerList.cs" /> <Compile Include="Client.ServerList.cs" />
<Compile Include="Client.ServerList.Request.cs" /> <Compile Include="Client.ServerList.Request.cs" />

View File

@ -710,7 +710,7 @@ internal class NativeEntrypoints
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_IsP2PPacketAvailable" )] [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_IsP2PPacketAvailable" )]
internal static extern bool SteamAPI_ISteamNetworking_IsP2PPacketAvailable( IntPtr instancePtr, ref uint pcubMsgSize, int nChannel ); internal static extern bool SteamAPI_ISteamNetworking_IsP2PPacketAvailable( IntPtr instancePtr, ref uint pcubMsgSize, int nChannel );
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_ReadP2PPacket" )] [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_ReadP2PPacket" )]
internal static extern bool SteamAPI_ISteamNetworking_ReadP2PPacket( IntPtr instancePtr, IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref CSteamID psteamIDRemote, int nChannel ); internal static extern bool SteamAPI_ISteamNetworking_ReadP2PPacket( IntPtr instancePtr, IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref ulong psteamIDRemote, int nChannel );
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser" )] [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser" )]
internal static extern bool SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser( IntPtr instancePtr, ulong steamIDRemote ); internal static extern bool SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser( IntPtr instancePtr, ulong steamIDRemote );
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_CloseP2PSessionWithUser" )] [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamNetworking_CloseP2PSessionWithUser" )]
@ -1982,7 +1982,7 @@ internal abstract class ISteamNetworking
internal abstract IntPtr GetIntPtr(); internal abstract IntPtr GetIntPtr();
internal abstract bool SendP2PPacket( ulong steamIDRemote, IntPtr pubData, uint cubData, uint eP2PSendType, int nChannel ); internal abstract bool SendP2PPacket( ulong steamIDRemote, IntPtr pubData, uint cubData, uint eP2PSendType, int nChannel );
internal abstract bool IsP2PPacketAvailable( ref uint pcubMsgSize, int nChannel ); internal abstract bool IsP2PPacketAvailable( ref uint pcubMsgSize, int nChannel );
internal abstract bool ReadP2PPacket( IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref CSteamID psteamIDRemote, int nChannel ); internal abstract bool ReadP2PPacket( IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref ulong psteamIDRemote, int nChannel );
internal abstract bool AcceptP2PSessionWithUser( ulong steamIDRemote ); internal abstract bool AcceptP2PSessionWithUser( ulong steamIDRemote );
internal abstract bool CloseP2PSessionWithUser( ulong steamIDRemote ); internal abstract bool CloseP2PSessionWithUser( ulong steamIDRemote );
internal abstract bool CloseP2PChannelWithUser( ulong steamIDRemote, int nChannel ); internal abstract bool CloseP2PChannelWithUser( ulong steamIDRemote, int nChannel );
@ -2475,7 +2475,7 @@ internal override ISteamNetworking GetISteamNetworking( uint hSteamUser, uint hS
{ {
CheckIfUsable(); CheckIfUsable();
IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamNetworking(m_pSteamClient,hSteamUser,hSteamPipe,pchVersion); IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamNetworking(m_pSteamClient,hSteamUser,hSteamPipe,pchVersion);
return (ISteamNetworking)Marshal.PtrToStructure( result, typeof( ISteamNetworking ) ); return new CSteamNetworking( result );
} }
internal override ISteamRemoteStorage GetISteamRemoteStorage( uint hSteamuser, uint hSteamPipe, string pchVersion ) internal override ISteamRemoteStorage GetISteamRemoteStorage( uint hSteamuser, uint hSteamPipe, string pchVersion )
{ {
@ -4730,7 +4730,7 @@ internal override bool IsP2PPacketAvailable( ref uint pcubMsgSize, int nChannel
bool result = NativeEntrypoints.SteamAPI_ISteamNetworking_IsP2PPacketAvailable(m_pSteamNetworking,ref pcubMsgSize,nChannel); bool result = NativeEntrypoints.SteamAPI_ISteamNetworking_IsP2PPacketAvailable(m_pSteamNetworking,ref pcubMsgSize,nChannel);
return result; return result;
} }
internal override bool ReadP2PPacket( IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref CSteamID psteamIDRemote, int nChannel ) internal override bool ReadP2PPacket( IntPtr pubDest, uint cubDest, ref uint pcubMsgSize, ref ulong psteamIDRemote, int nChannel )
{ {
CheckIfUsable(); CheckIfUsable();
pcubMsgSize = 0; pcubMsgSize = 0;