mirror of
https://github.com/Facepunch/Facepunch.Steamworks.git
synced 2025-01-14 15:48:06 +03:00
NetworkingUtils (Ping stuff)
This commit is contained in:
parent
1c1925bae9
commit
16ce5f4d63
@ -100,6 +100,7 @@
|
|||||||
<Compile Include="UserTest.cs" />
|
<Compile Include="UserTest.cs" />
|
||||||
<Compile Include="UserStatsTest.cs" />
|
<Compile Include="UserStatsTest.cs" />
|
||||||
<Compile Include="UgcQuery.cs" />
|
<Compile Include="UgcQuery.cs" />
|
||||||
|
<Compile Include="NetworkingUtils.cs" />
|
||||||
<Compile Include="UtilsTest.cs" />
|
<Compile Include="UtilsTest.cs" />
|
||||||
<Compile Include="AppTest.cs" />
|
<Compile Include="AppTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
62
Facepunch.Steamworks.Test/NetworkingUtils.cs
Normal file
62
Facepunch.Steamworks.Test/NetworkingUtils.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace Steamworks
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[DeploymentItem( "steam_api64.dll" )]
|
||||||
|
public class NetworkUtilsTest
|
||||||
|
{
|
||||||
|
static string GarrysLocation = "lhr=19+1,ams=25+2/25+1,par=29+2,fra=31+3/30+1,lux=33+3,vie=44+4/41+1,waw=47+4/45+1,sto2=48+4/46+2,sto=50+5/46+2,iad=107+10/91+1,sgp=186+18,gru=252+25/234+1";
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task LocalPingLocation()
|
||||||
|
{
|
||||||
|
await SteamNetworkingUtils.WaitForPingDataAsync();
|
||||||
|
|
||||||
|
for ( int i = 0; i < 10; i++ )
|
||||||
|
{
|
||||||
|
var pl = SteamNetworkingUtils.LocalPingLocation;
|
||||||
|
if ( !pl.HasValue )
|
||||||
|
{
|
||||||
|
await Task.Delay( 1000 );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine( $"{i} Seconds Until Result: {pl}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PingLocationParse()
|
||||||
|
{
|
||||||
|
var pl = Data.PingLocation.TryParseFromString( GarrysLocation );
|
||||||
|
|
||||||
|
Assert.IsTrue( pl.HasValue );
|
||||||
|
|
||||||
|
Console.WriteLine( $"Parsed OKAY! {pl}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task GetEstimatedPing()
|
||||||
|
{
|
||||||
|
await SteamNetworkingUtils.WaitForPingDataAsync();
|
||||||
|
|
||||||
|
var garrysping = Data.PingLocation.TryParseFromString( GarrysLocation );
|
||||||
|
Assert.IsTrue( garrysping.HasValue );
|
||||||
|
|
||||||
|
var ping = SteamNetworkingUtils.EstimatePingTo( garrysping.Value );
|
||||||
|
Assert.IsTrue( ping > 0 );
|
||||||
|
|
||||||
|
Console.WriteLine( $"Ping returned: {ping}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -45,7 +45,7 @@ namespace Steamworks
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task UploadBigFile()
|
public async Task UploadBigishFile()
|
||||||
{
|
{
|
||||||
var created = Ugc.Editor.NewCommunityFile
|
var created = Ugc.Editor.NewCommunityFile
|
||||||
.WithTitle( "Unit Test Upload Item" )
|
.WithTitle( "Unit Test Upload Item" )
|
||||||
@ -65,7 +65,7 @@ namespace Steamworks
|
|||||||
|
|
||||||
// Upload a file of random bytes
|
// Upload a file of random bytes
|
||||||
var rand = new Random();
|
var rand = new Random();
|
||||||
var testFile = new byte[1024 * 1024 * 256];
|
var testFile = new byte[1024 * 1024 * 32];
|
||||||
rand.NextBytes( testFile );
|
rand.NextBytes( testFile );
|
||||||
System.IO.File.WriteAllBytes( testFolder.FullName + "/testfile1.bin", testFile );
|
System.IO.File.WriteAllBytes( testFolder.FullName + "/testfile1.bin", testFile );
|
||||||
|
|
||||||
|
@ -34,58 +34,58 @@ namespace Steamworks
|
|||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
||||||
private delegate float FGetLocalPingLocation( IntPtr self, SteamNetworkPingLocation_t result );
|
private delegate float FGetLocalPingLocation( IntPtr self, ref PingLocation result );
|
||||||
private FGetLocalPingLocation _GetLocalPingLocation;
|
private FGetLocalPingLocation _GetLocalPingLocation;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
internal float GetLocalPingLocation( SteamNetworkPingLocation_t result )
|
internal float GetLocalPingLocation( ref PingLocation result )
|
||||||
{
|
{
|
||||||
return _GetLocalPingLocation( Self, result );
|
return _GetLocalPingLocation( Self, ref result );
|
||||||
}
|
}
|
||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
||||||
private delegate int FEstimatePingTimeBetweenTwoLocations( IntPtr self, SteamNetworkPingLocation_t location1, SteamNetworkPingLocation_t location2 );
|
private delegate int FEstimatePingTimeBetweenTwoLocations( IntPtr self, ref PingLocation location1, ref PingLocation location2 );
|
||||||
private FEstimatePingTimeBetweenTwoLocations _EstimatePingTimeBetweenTwoLocations;
|
private FEstimatePingTimeBetweenTwoLocations _EstimatePingTimeBetweenTwoLocations;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
internal int EstimatePingTimeBetweenTwoLocations( SteamNetworkPingLocation_t location1, SteamNetworkPingLocation_t location2 )
|
internal int EstimatePingTimeBetweenTwoLocations( ref PingLocation location1, ref PingLocation location2 )
|
||||||
{
|
{
|
||||||
return _EstimatePingTimeBetweenTwoLocations( Self, location1, location2 );
|
return _EstimatePingTimeBetweenTwoLocations( Self, ref location1, ref location2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
||||||
private delegate int FEstimatePingTimeFromLocalHost( IntPtr self, SteamNetworkPingLocation_t remoteLocation );
|
private delegate int FEstimatePingTimeFromLocalHost( IntPtr self, ref PingLocation remoteLocation );
|
||||||
private FEstimatePingTimeFromLocalHost _EstimatePingTimeFromLocalHost;
|
private FEstimatePingTimeFromLocalHost _EstimatePingTimeFromLocalHost;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
internal int EstimatePingTimeFromLocalHost( SteamNetworkPingLocation_t remoteLocation )
|
internal int EstimatePingTimeFromLocalHost( ref PingLocation remoteLocation )
|
||||||
{
|
{
|
||||||
return _EstimatePingTimeFromLocalHost( Self, remoteLocation );
|
return _EstimatePingTimeFromLocalHost( Self, ref remoteLocation );
|
||||||
}
|
}
|
||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
||||||
private delegate void FConvertPingLocationToString( IntPtr self, SteamNetworkPingLocation_t location, StringBuilder pszBuf, int cchBufSize );
|
private delegate void FConvertPingLocationToString( IntPtr self, ref PingLocation location, StringBuilder pszBuf, int cchBufSize );
|
||||||
private FConvertPingLocationToString _ConvertPingLocationToString;
|
private FConvertPingLocationToString _ConvertPingLocationToString;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
internal void ConvertPingLocationToString( SteamNetworkPingLocation_t location, StringBuilder pszBuf, int cchBufSize )
|
internal void ConvertPingLocationToString( ref PingLocation location, StringBuilder pszBuf, int cchBufSize )
|
||||||
{
|
{
|
||||||
_ConvertPingLocationToString( Self, location, pszBuf, cchBufSize );
|
_ConvertPingLocationToString( Self, ref location, pszBuf, cchBufSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
||||||
[return: MarshalAs( UnmanagedType.I1 )]
|
[return: MarshalAs( UnmanagedType.I1 )]
|
||||||
private delegate bool FParsePingLocationString( IntPtr self, string pszString, SteamNetworkPingLocation_t result );
|
private delegate bool FParsePingLocationString( IntPtr self, string pszString, ref PingLocation result );
|
||||||
private FParsePingLocationString _ParsePingLocationString;
|
private FParsePingLocationString _ParsePingLocationString;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
internal bool ParsePingLocationString( string pszString, SteamNetworkPingLocation_t result )
|
internal bool ParsePingLocationString( string pszString, ref PingLocation result )
|
||||||
{
|
{
|
||||||
return _ParsePingLocationString( Self, pszString, result );
|
return _ParsePingLocationString( Self, pszString, ref result );
|
||||||
}
|
}
|
||||||
|
|
||||||
#region FunctionMeta
|
#region FunctionMeta
|
||||||
|
@ -84,6 +84,7 @@ namespace Steamworks
|
|||||||
SteamNetworking.Shutdown();
|
SteamNetworking.Shutdown();
|
||||||
SteamMatchmaking.Shutdown();
|
SteamMatchmaking.Shutdown();
|
||||||
SteamParties.Shutdown();
|
SteamParties.Shutdown();
|
||||||
|
SteamNetworkingUtils.Shutdown();
|
||||||
|
|
||||||
SteamAPI.Shutdown();
|
SteamAPI.Shutdown();
|
||||||
}
|
}
|
||||||
|
82
Facepunch.Steamworks/SteamNetworkingUtils.cs
Normal file
82
Facepunch.Steamworks/SteamNetworkingUtils.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Steamworks.Data;
|
||||||
|
|
||||||
|
namespace Steamworks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Undocumented Parental Settings
|
||||||
|
/// </summary>
|
||||||
|
public static class SteamNetworkingUtils
|
||||||
|
{
|
||||||
|
static ISteamNetworkingUtils _internal;
|
||||||
|
internal static ISteamNetworkingUtils Internal
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ( _internal == null )
|
||||||
|
{
|
||||||
|
_internal = new ISteamNetworkingUtils();
|
||||||
|
_internal.InitUserless();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _internal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Shutdown()
|
||||||
|
{
|
||||||
|
_internal = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return location info for the current host.
|
||||||
|
///
|
||||||
|
/// It takes a few seconds to initialize access to the relay network. If
|
||||||
|
/// you call this very soon after startup the data may not be available yet.
|
||||||
|
///
|
||||||
|
/// This always return the most up-to-date information we have available
|
||||||
|
/// right now, even if we are in the middle of re-calculating ping times.
|
||||||
|
/// </summary>
|
||||||
|
public static PingLocation? LocalPingLocation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
PingLocation location = default;
|
||||||
|
var age = Internal.GetLocalPingLocation( ref location );
|
||||||
|
if ( age < 0 )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Same as PingLocation.EstimatePingTo, but assumes that one location is the local host.
|
||||||
|
/// This is a bit faster, especially if you need to calculate a bunch of
|
||||||
|
/// these in a loop to find the fastest one.
|
||||||
|
/// </summary>
|
||||||
|
public static int EstimatePingTo( PingLocation target )
|
||||||
|
{
|
||||||
|
return Internal.EstimatePingTimeFromLocalHost( ref target );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If you need ping information straight away, wait on this. It will return
|
||||||
|
/// immediately if you already have up to date ping data
|
||||||
|
/// </summary>
|
||||||
|
public static async Task WaitForPingDataAsync( float maxAgeInSeconds = 60 * 5 )
|
||||||
|
{
|
||||||
|
if ( Internal.CheckPingDataUpToDate( 60.0f ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ( Internal.IsPingMeasurementInProgress() )
|
||||||
|
{
|
||||||
|
await Task.Delay( 10 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,9 +58,54 @@ namespace Steamworks.Data
|
|||||||
/// ISteamNetworkingUtils().
|
/// ISteamNetworkingUtils().
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct SteamNetworkPingLocation_t
|
public struct PingLocation
|
||||||
{
|
{
|
||||||
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.U8 )]
|
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.U8 )]
|
||||||
public ushort[] Data;
|
public ushort[] Data;
|
||||||
|
|
||||||
|
public static PingLocation? TryParseFromString( string str )
|
||||||
|
{
|
||||||
|
var result = default( PingLocation );
|
||||||
|
if ( !SteamNetworkingUtils.Internal.ParsePingLocationString( str, ref result ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var sb = Helpers.TakeStringBuilder();
|
||||||
|
SteamNetworkingUtils.Internal.ConvertPingLocationToString( ref this, sb, sb.Capacity );
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Estimate the round-trip latency between two arbitrary locations, in
|
||||||
|
/// milliseconds. This is a conservative estimate, based on routing through
|
||||||
|
/// the relay network. For most basic relayed connections, this ping time
|
||||||
|
/// will be pretty accurate, since it will be based on the route likely to
|
||||||
|
/// be actually used.
|
||||||
|
///
|
||||||
|
/// If a direct IP route is used (perhaps via NAT traversal), then the route
|
||||||
|
/// will be different, and the ping time might be better. Or it might actually
|
||||||
|
/// be a bit worse! Standard IP routing is frequently suboptimal!
|
||||||
|
///
|
||||||
|
/// But even in this case, the estimate obtained using this method is a
|
||||||
|
/// reasonable upper bound on the ping time. (Also it has the advantage
|
||||||
|
/// of returning immediately and not sending any packets.)
|
||||||
|
///
|
||||||
|
/// In a few cases we might not able to estimate the route. In this case
|
||||||
|
/// a negative value is returned. k_nSteamNetworkingPing_Failed means
|
||||||
|
/// the reason was because of some networking difficulty. (Failure to
|
||||||
|
/// ping, etc) k_nSteamNetworkingPing_Unknown is returned if we cannot
|
||||||
|
/// currently answer the question for some other reason.
|
||||||
|
///
|
||||||
|
/// Do you need to be able to do this from a backend/matchmaking server?
|
||||||
|
/// You are looking for the "ticketgen" library.
|
||||||
|
public int EstimatePingTo( PingLocation target )
|
||||||
|
{
|
||||||
|
return SteamNetworkingUtils.Internal.EstimatePingTimeBetweenTwoLocations( ref this, ref target );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,6 +26,7 @@ public static class Cleanup
|
|||||||
type = type.Replace( "SteamItemInstanceID_t", "InventoryItemId" );
|
type = type.Replace( "SteamItemInstanceID_t", "InventoryItemId" );
|
||||||
type = type.Replace( "SteamItemDef_t", "InventoryDefId" );
|
type = type.Replace( "SteamItemDef_t", "InventoryDefId" );
|
||||||
type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" );
|
type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" );
|
||||||
|
type = type.Replace( "SteamNetworkPingLocation_t", "PingLocation" );
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ internal class BaseType
|
|||||||
{
|
{
|
||||||
type = Cleanup.ConvertType( type );
|
type = Cleanup.ConvertType( type );
|
||||||
|
|
||||||
type = type.Trim( '&' );
|
|
||||||
|
|
||||||
if ( type == "SteamAPIWarningMessageHook_t" ) return new PointerType { NativeType = type, VarName = varname };
|
if ( type == "SteamAPIWarningMessageHook_t" ) return new PointerType { NativeType = type, VarName = varname };
|
||||||
if ( type == "intptr_t" ) return new PointerType { NativeType = type, VarName = varname };
|
if ( type == "intptr_t" ) return new PointerType { NativeType = type, VarName = varname };
|
||||||
|
|
||||||
@ -28,7 +26,7 @@ internal class BaseType
|
|||||||
if ( type.Replace( " ", "" ).StartsWith( "constchar*" ) ) return new ConstCharType { NativeType = type, VarName = varname };
|
if ( type.Replace( " ", "" ).StartsWith( "constchar*" ) ) return new ConstCharType { NativeType = type, VarName = varname };
|
||||||
if ( type == "char *" ) return new StringBuilderType { NativeType = type, VarName = varname };
|
if ( type == "char *" ) return new StringBuilderType { NativeType = type, VarName = varname };
|
||||||
|
|
||||||
var basicType = type.Replace( "const ", "" ).Trim( ' ', '*' );
|
var basicType = type.Replace( "const ", "" ).Trim( ' ', '*', '&' );
|
||||||
|
|
||||||
if ( basicType == "void" ) return new PointerType { NativeType = type, VarName = varname };
|
if ( basicType == "void" ) return new PointerType { NativeType = type, VarName = varname };
|
||||||
if ( basicType.StartsWith( "ISteam" ) ) return new PointerType { NativeType = type, VarName = varname };
|
if ( basicType.StartsWith( "ISteam" ) ) return new PointerType { NativeType = type, VarName = varname };
|
||||||
@ -49,6 +47,7 @@ internal class BaseType
|
|||||||
if ( basicType.EndsWith( "_t" ) ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
if ( basicType.EndsWith( "_t" ) ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
||||||
if ( basicType == "InventoryItemId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
if ( basicType == "InventoryItemId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
||||||
if ( basicType == "InventoryDefId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
if ( basicType == "InventoryDefId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
||||||
|
if ( basicType == "PingLocation" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType };
|
||||||
if ( basicType.StartsWith( "E" ) && char.IsUpper( basicType[1] ) ) return new EnumType { NativeType = type.Substring( 1 ), VarName = varname };
|
if ( basicType.StartsWith( "E" ) && char.IsUpper( basicType[1] ) ) return new EnumType { NativeType = type.Substring( 1 ), VarName = varname };
|
||||||
|
|
||||||
return new BaseType { NativeType = type, VarName = varname };
|
return new BaseType { NativeType = type, VarName = varname };
|
||||||
@ -62,7 +61,7 @@ internal class BaseType
|
|||||||
|
|
||||||
public virtual string ReturnType => TypeName;
|
public virtual string ReturnType => TypeName;
|
||||||
|
|
||||||
public virtual string Ref => !IsVector && NativeType.EndsWith( "*" ) || NativeType.EndsWith( "**" ) ? "ref " : "";
|
public virtual string Ref => !IsVector && NativeType.EndsWith( "*" ) || NativeType.EndsWith( "**" ) || NativeType.Contains( "&" ) ? "ref " : "";
|
||||||
public virtual bool IsVector
|
public virtual bool IsVector
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
Loading…
x
Reference in New Issue
Block a user