Merge branch 'master' into workshop

This commit is contained in:
James King 2017-12-06 15:41:30 +00:00
commit 719bfbf8dd
27 changed files with 1476 additions and 1351 deletions

View File

@ -56,7 +56,7 @@ public void GetNames()
Console.WriteLine( " - - " + ach.Description );
Console.WriteLine( " - - " + ach.State );
Console.WriteLine( " - - " + ach.UnlockTime );
Console.WriteLine( " - - " + ach.Percentage );
Console.WriteLine( " - - " + ach.GlobalUnlockedPercentage );
if ( ach.Icon != null )
{

View File

@ -84,6 +84,8 @@ public void Update()
var sw = new Stopwatch();
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
Assert.IsTrue(client.IsValid);
for( int i=0; i<1024; i++ )
{
sw.Restart();

View File

@ -24,6 +24,8 @@ public void FriendList()
foreach ( var friend in client.Friends.All )
{
Console.WriteLine( "{0}: {1} (Friend:{2}) (Blocked:{3})", friend.Id, friend.Name, friend.IsFriend, friend.IsBlocked );
Assert.IsNotNull(friend.GetAvatar( Steamworks.Friends.AvatarSize.Medium ));
}
}
}
@ -50,21 +52,43 @@ public void Avatar()
Assert.IsTrue( client.IsValid );
var friend = client.Friends.All.First();
bool passed = false;
var img = client.Friends.GetAvatar( Steamworks.Friends.AvatarSize.Medium, friend.Id );
client.Friends.GetAvatar( Steamworks.Friends.AvatarSize.Medium, friend.Id, (avatar) =>
{
Assert.AreEqual(avatar.Width, 64);
Assert.AreEqual(avatar.Height, 64);
Assert.AreEqual(avatar.Data.Length, avatar.Width * avatar.Height * 4);
Assert.AreEqual( img.Width, 64 );
Assert.AreEqual( img.Height, 64 );
DrawImage(avatar);
passed = true;
});
while ( !img.IsLoaded && !img.IsError )
while (passed == false )
{
client.Update();
System.Threading.Thread.Sleep( 10 );
}
}
}
Assert.AreEqual( img.Data.Length, img.Width * img.Height * 4 );
[TestMethod]
public void CachedAvatar()
{
using (var client = new Facepunch.Steamworks.Client(252490))
{
Assert.IsTrue(client.IsValid);
DrawImage( img );
var friend = client.Friends.All.First();
var image = client.Friends.GetCachedAvatar( Steamworks.Friends.AvatarSize.Medium, friend.Id );
if (image != null)
{
Assert.AreEqual(image.Width, 64);
Assert.AreEqual(image.Height, 64);
Assert.AreEqual(image.Data.Length, image.Width * image.Height * 4);
}
}
}

View File

@ -273,6 +273,12 @@ public void SendChatMessage()
Assert.IsTrue(message == testString);
};
client.Lobby.OnChatStringRecieved = (steamID, message) =>
{
Console.WriteLine("message recieved");
Assert.IsTrue(message == testString);
};
client.Lobby.Create(Steamworks.Lobby.Type.Public, 10);
var sw = Stopwatch.StartNew();

View File

@ -433,6 +433,21 @@ public void CreatePublish()
item.Tags.Add( "Apple" );
item.Tags.Add( "Banana" );
// Make a folder
var testFolder = new System.IO.DirectoryInfo("BlahBlah");
if (!testFolder.Exists) testFolder.Create();
item.Folder = testFolder.FullName;
// Upload a file of random bytes
var rand = new Random();
var testFile = new byte[1024 * 1024 * 32];
rand.NextBytes(testFile);
System.IO.File.WriteAllBytes( testFolder.FullName + "/testfile1.bin", testFile);
Console.WriteLine(item.Folder);
try
{
item.Publish();
@ -440,7 +455,11 @@ public void CreatePublish()
while ( item.Publishing )
{
client.Update();
Thread.Sleep( 100 );
Thread.Sleep( 10 );
Console.WriteLine("Progress: " + item.Progress);
Console.WriteLine("BytesUploaded: " + item.BytesUploaded);
Console.WriteLine("BytesTotal: " + item.BytesTotal);
}
Assert.IsFalse( item.Publishing );
@ -469,6 +488,8 @@ public void CreatePublish()
{
Console.WriteLine( "Deleting: {0}", item.Id );
item.Delete();
System.IO.File.Delete(testFolder.FullName + "/testfile.bin");
}
}
}

View File

@ -92,25 +92,25 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Client\Achievements.cs" />
<Compile Include="Client\Client.cs" />
<Compile Include="Client\Lobby.cs" />
<Compile Include="Client\RichPresence.cs" />
<Compile Include="Client\Leaderboard.cs" />
<Compile Include="Client\App.cs" />
<Compile Include="Client\RemoteStorage.cs" />
<Compile Include="Client\Voice.cs" />
<Compile Include="Client\Inventory.cs" />
<Compile Include="Client\Workshop.cs" />
<Compile Include="Client\Networking.cs" />
<Compile Include="Client\AchievementsTest.cs" />
<Compile Include="Client\ClientTest.cs" />
<Compile Include="Client\LobbyTest.cs" />
<Compile Include="Client\RichPresenceTest.cs" />
<Compile Include="Client\LeaderboardTest.cs" />
<Compile Include="Client\AppTest.cs" />
<Compile Include="Client\RemoteStorageTest.cs" />
<Compile Include="Client\VoiceTest.cs" />
<Compile Include="Client\InventoryTest.cs" />
<Compile Include="Client\WorkshopTest.cs" />
<Compile Include="Client\NetworkingTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Client\Friends.cs" />
<Compile Include="Server\Server.cs" />
<Compile Include="Server\Stats.cs" />
<Compile Include="Client\FriendsTest.cs" />
<Compile Include="Client\Server\ServerTest.cs" />
<Compile Include="Client\Server\StatsTest.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Client\Serverlist.cs" />
<Compile Include="Client\Stats.cs" />
<Compile Include="Client\ServerlistTest.cs" />
<Compile Include="Client\StatsTest.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -143,9 +143,9 @@ public class Achievement
private int refreshCount = 0;
/// <summary>
/// If this achievement is linked to a stat this will return the progress.
/// Returns the percentage of users who have unlocked the specified achievement, or -1 if no data available.
/// </summary>
public float Percentage
public float GlobalUnlockedPercentage
{
get
{

View File

@ -127,6 +127,15 @@ public void Refresh()
Client.native.friends.RequestFriendRichPresence( Id );
}
/// <summary>
/// This will return null if you don't have the target user's avatar in your cache.
/// Which usually happens for people not on your friends list.
/// </summary>
public Image GetAvatar( Friends.AvatarSize size )
{
return Client.Friends.GetCachedAvatar( size, Id );
}
}
/// <summary>
@ -145,6 +154,8 @@ public class Friends
internal Friends( Client c )
{
client = c;
SteamNative.PersonaStateChange_t.RegisterCallback( client, OnPersonaStateChange );
}
/// <summary>
@ -253,20 +264,23 @@ public enum AvatarSize
Large
}
public Image GetAvatar( AvatarSize size, ulong steamid )
/// <summary>
/// Try to get the avatar immediately. This should work for people on your friends list.
/// </summary>
public Image GetCachedAvatar( AvatarSize size, ulong steamid )
{
var imageid = 0;
switch ( size )
switch (size)
{
case AvatarSize.Small:
imageid = client.native.friends.GetSmallFriendAvatar( steamid );
imageid = client.native.friends.GetSmallFriendAvatar(steamid);
break;
case AvatarSize.Medium:
imageid = client.native.friends.GetMediumFriendAvatar( steamid );
imageid = client.native.friends.GetMediumFriendAvatar(steamid);
break;
case AvatarSize.Large:
imageid = client.native.friends.GetLargeFriendAvatar( steamid );
imageid = client.native.friends.GetLargeFriendAvatar(steamid);
break;
}
@ -275,19 +289,53 @@ public Image GetAvatar( AvatarSize size, ulong steamid )
Id = imageid
};
if ( imageid == 0 )
if (imageid != 0 && img.TryLoad(client.native.utils))
return img;
if ( img.TryLoad( client.native.utils ) )
return img;
throw new System.NotImplementedException( "Deferred Avatar Loading Todo" );
// Add to image loading list
//return img;
return null;
}
/// <summary>
/// Callback will be called when the avatar is ready. If we fail to get an
/// avatar, it'll be called with a null Image.
/// </summary>
public void GetAvatar( AvatarSize size, ulong steamid, Action<Image> callback )
{
// Maybe we already have it downloaded?
var image = GetCachedAvatar(size, steamid);
if ( image != null )
{
callback(image);
return;
}
// Lets request it from Steam
if (!client.native.friends.RequestUserInformation(steamid, false))
{
// Steam told us to get fucked
callback(null);
return;
}
PersonaCallbacks.Add( new PersonaCallback
{
SteamId = steamid,
Callback = () =>
{
callback( GetCachedAvatar(size, steamid) );
}
});
}
private class PersonaCallback
{
public ulong SteamId;
public Action Callback;
}
List<PersonaCallback> PersonaCallbacks = new List<PersonaCallback>();
public SteamFriend Get( ulong steamid )
{
var f = new SteamFriend()
@ -300,5 +348,11 @@ public SteamFriend Get( ulong steamid )
return f;
}
private void OnPersonaStateChange( PersonaStateChange_t data, bool error )
{
}
}
}

View File

@ -100,6 +100,11 @@ private bool DeferOnCreated( Action onValid, FailureCallback onFailure = null )
return true;
}
/// <summary>
/// Called when the leaderboard information is successfully recieved from Steam
/// </summary>
public Action OnBoardInformation;
internal void OnBoardCreated( LeaderboardFindResult_t result, bool error )
{
if ( error || ( result.LeaderboardFound == 0 ) )
@ -114,6 +119,8 @@ internal void OnBoardCreated( LeaderboardFindResult_t result, bool error )
{
Name = client.native.userstats.GetLeaderboardName( BoardId );
TotalEntries = client.native.userstats.GetLeaderboardEntryCount( BoardId );
OnBoardInformation?.Invoke();
}
}
@ -125,7 +132,8 @@ internal void OnBoardCreated( LeaderboardFindResult_t result, bool error )
/// Subscores are totally optional, and can be used for other game defined data such as laps etc.. although
/// they have no bearing on sorting at all
/// If onlyIfBeatsOldScore is true, the score will only be updated if it beats the existing score, else it will always
/// be updated.
/// be updated. Beating the existing score is subjective - and depends on how your leaderboard was set up as to whether
/// that means higher or lower.
/// </summary>
public bool AddScore( bool onlyIfBeatsOldScore, int score, params int[] subscores )
{
@ -141,10 +149,9 @@ public bool AddScore( bool onlyIfBeatsOldScore, int score, params int[] subscore
}
/// <summary>
/// Callback invoked by <see cref="AddScore(bool, int, int[], AddScoreCallback)"/> when score submission
/// Callback invoked by <see cref="AddScore(bool, int, int[], AddScoreCallback, FailureCallback)"/> when score submission
/// is complete.
/// </summary>
/// <param name="success">If true, the score was submitted</param>
/// <param name="result">If successful, information about the new entry</param>
public delegate void AddScoreCallback( AddScoreResult result );
@ -278,13 +285,13 @@ private unsafe void ReadScores( LeaderboardScoresDownloaded_t result, List<Entry
[ThreadStatic] private static List<Entry> _sEntryBuffer;
/// <summary>
/// Callback invoked by <see cref="FetchScores(RequestType, int, int, FetchScoresCallback)"/> when
/// Callback invoked by <see cref="FetchScores(RequestType, int, int, FetchScoresCallback, FailureCallback)"/> when
/// a query is complete.
/// </summary>
public delegate void FetchScoresCallback( Entry[] results );
/// <summary>
/// Fetch a subset of scores. The scores are passed to <paramref name="callback"/>.
/// Fetch a subset of scores. The scores are passed to <paramref name="onSuccess"/>.
/// </summary>
/// <returns>Returns true if we have started the query</returns>
public bool FetchScores( RequestType RequestType, int start, int end, FetchScoresCallback onSuccess, FailureCallback onFailure = null )

View File

@ -318,28 +318,24 @@ public Type LobbyType
}
}
private static byte[] chatMessageData = new byte[1024 * 4];
private unsafe void OnLobbyChatMessageRecievedAPI(LobbyChatMsg_t callback, bool error)
{
//from Client.Networking
if(error || callback.SteamIDLobby != CurrentLobby) { return; }
if(error || callback.SteamIDLobby != CurrentLobby)
return;
byte[] ReceiveBuffer = new byte[1024];
SteamNative.CSteamID steamid = 1;
ChatEntryType chatEntryType; //not used
ChatEntryType chatEntryType; // "If set then this will just always return k_EChatEntryTypeChatMsg. This can usually just be set to NULL."
int readData = 0;
fixed (byte* p = ReceiveBuffer)
fixed (byte* p = chatMessageData)
{
readData = client.native.matchmaking.GetLobbyChatEntry(CurrentLobby, (int)callback.ChatID, out steamid, (IntPtr)p, ReceiveBuffer.Length, out chatEntryType);
while (ReceiveBuffer.Length < readData)
{
ReceiveBuffer = new byte[readData + 1024];
readData = client.native.matchmaking.GetLobbyChatEntry(CurrentLobby, (int)callback.ChatID, out steamid, (IntPtr)p, ReceiveBuffer.Length, out chatEntryType);
}
readData = client.native.matchmaking.GetLobbyChatEntry(CurrentLobby, (int)callback.ChatID, out steamid, (IntPtr)p, chatMessageData.Length, out chatEntryType);
}
if (OnChatMessageRecieved != null) { OnChatMessageRecieved(steamid, ReceiveBuffer, readData); }
OnChatMessageRecieved?.Invoke(steamid, chatMessageData, readData);
OnChatStringRecieved?.Invoke(steamid, Encoding.UTF8.GetString(chatMessageData));
}
/// <summary>
@ -347,6 +343,11 @@ private unsafe void OnLobbyChatMessageRecievedAPI(LobbyChatMsg_t callback, bool
/// </summary>
public Action<ulong, byte[], int> OnChatMessageRecieved;
/// <summary>
/// Like OnChatMessageRecieved but the data is converted to a string
/// </summary>
public Action<ulong, string> OnChatStringRecieved;
/// <summary>
/// Broadcasts a chat message to the all the users in the lobby users in the lobby (including the local user) will receive a LobbyChatMsg_t callback.
/// </summary>
@ -376,12 +377,14 @@ public enum MemberStateChange
internal void OnLobbyStateUpdatedAPI(LobbyChatUpdate_t callback, bool error)
{
if (error || callback.SteamIDLobby != CurrentLobby) { return; }
if (error || callback.SteamIDLobby != CurrentLobby)
return;
MemberStateChange change = (MemberStateChange)callback.GfChatMemberStateChange;
ulong initiator = callback.SteamIDMakingChange;
ulong affected = callback.SteamIDUserChanged;
if (OnLobbyStateChanged != null) { OnLobbyStateChanged(change, initiator, affected); }
OnLobbyStateChanged?.Invoke(change, initiator, affected);
}
/// <summary>
@ -487,7 +490,11 @@ public int NumMembers
/// </summary>
public void Leave()
{
if (CurrentLobby != 0) { client.native.matchmaking.LeaveLobby(CurrentLobby); }
if (CurrentLobby != 0)
{
client.native.matchmaking.LeaveLobby(CurrentLobby);
}
CurrentLobby = 0;
CurrentLobbyData = null;
}
@ -505,7 +512,6 @@ public void Dispose()
/// <returns>Array of member SteamIDs</returns>
public ulong[] GetMemberIDs()
{
ulong[] memIDs = new ulong[NumMembers];
for (int i = 0; i < NumMembers; i++)
{
@ -521,14 +527,15 @@ public ulong[] GetMemberIDs()
/// <returns></returns>
public bool UserIsInCurrentLobby(ulong steamID)
{
if(CurrentLobby == 0) { return false; }
if ( CurrentLobby == 0 )
return false;
ulong[] mems = GetMemberIDs();
for (int i = 0; i < mems.Length; i++)
{
if(mems[i] == steamID)
{
if ( mems[i] == steamID )
return true;
}
}
return false;

View File

@ -170,7 +170,7 @@ private unsafe bool ReadP2PPacket( int channel )
{
uint DataAvailable = 0;
if ( !networking.IsP2PPacketAvailable( out DataAvailable, channel ) || DataAvailable == 0 )
if ( !networking.IsP2PPacketAvailable( out DataAvailable, channel ) )
return false;
if ( ReceiveBuffer.Length < DataAvailable )

View File

@ -208,6 +208,7 @@ private void OnChangesSubmitted( SteamNative.SubmitItemUpdateResult_t obj, bool
else throw new System.Exception( "CreateItemResult_t Failed" );
}
UpdateHandle = 0;
SubmitItemUpdate = null;
NeedToAgreeToWorkshopLegal = obj.UserNeedsToAcceptWorkshopLegalAgreement;
Publishing = false;

View File

@ -29,11 +29,11 @@ internal class NativeInterface : IDisposable
private bool isServer;
private HSteamUser hUser;
private HSteamPipe hPipe;
internal bool InitClient( BaseSteamworks steamworks )
{
if ( Steamworks.Server.Instance != null )
throw new System.Exception("Steam client should be initialized before steam server - or there's big trouble.");
isServer = false;
api = new SteamNative.SteamApi();
@ -44,8 +44,8 @@ internal bool InitClient( BaseSteamworks steamworks )
return false;
}
hUser = api.SteamAPI_GetHSteamUser();
hPipe = api.SteamAPI_GetHSteamPipe();
var hUser = api.SteamAPI_GetHSteamUser();
var hPipe = api.SteamAPI_GetHSteamPipe();
if ( hPipe == 0 )
{
Console.Error.WriteLine( "InitClient: hPipe == 0" );
@ -83,8 +83,8 @@ internal bool InitServer( BaseSteamworks steamworks, uint IpAddress /*uint32*/,
return false;
}
hUser = api.SteamGameServer_GetHSteamUser();
hPipe = api.SteamGameServer_GetHSteamPipe();
var hUser = api.SteamGameServer_GetHSteamUser();
var hPipe = api.SteamGameServer_GetHSteamPipe();
if ( hPipe == 0 )
{
Console.Error.WriteLine( "InitServer: hPipe == 0" );
@ -216,6 +216,12 @@ public void Dispose()
remoteStorage = null;
}
if ( matchmaking != null )
{
matchmaking.Dispose();
matchmaking = null;
}
if ( applist != null )
{
applist.Dispose();
@ -242,9 +248,6 @@ public void Dispose()
// pointers we stored are not invalid.
//
hPipe = 0;
hUser = 0;
api.Dispose();
api = null;
}

View File

@ -71,9 +71,9 @@ void InstallVTable()
ThisVTable.InternalRulesFailedToRespond db = ( _ ) => InternalOnRulesFailedToRespond();
ThisVTable.InternalRulesRefreshComplete dc = ( _ ) => InternalOnRulesRefreshComplete();
RulesRespondPin = GCHandle.Alloc( da, GCHandleType.Pinned );
FailedRespondPin = GCHandle.Alloc( db, GCHandleType.Pinned );
CompletePin = GCHandle.Alloc( dc, GCHandleType.Pinned );
RulesRespondPin = GCHandle.Alloc( da );
FailedRespondPin = GCHandle.Alloc( db );
CompletePin = GCHandle.Alloc( dc );
var t = new ThisVTable()
{
@ -93,9 +93,9 @@ void InstallVTable()
StdVTable.InternalRulesFailedToRespond db = InternalOnRulesFailedToRespond;
StdVTable.InternalRulesRefreshComplete dc = InternalOnRulesRefreshComplete;
RulesRespondPin = GCHandle.Alloc( da, GCHandleType.Pinned );
FailedRespondPin = GCHandle.Alloc( db, GCHandleType.Pinned );
CompletePin = GCHandle.Alloc( dc, GCHandleType.Pinned );
RulesRespondPin = GCHandle.Alloc( da );
FailedRespondPin = GCHandle.Alloc( db );
CompletePin = GCHandle.Alloc( dc );
var t = new StdVTable()
{

File diff suppressed because it is too large Load Diff

View File

@ -317,9 +317,9 @@ private void CallresultFunctions( SteamApiDefinition.StructDef c, bool Result, s
WriteLine( "//" );
WriteLine( "// Allocate a handle to each function, so they don't get disposed" );
WriteLine( "//" );
WriteLine( "handle.FuncA = GCHandle.Alloc( funcA, GCHandleType.Pinned );" );
WriteLine( "handle.FuncB = GCHandle.Alloc( funcB, GCHandleType.Pinned );" );
WriteLine( "handle.FuncC = GCHandle.Alloc( funcC, GCHandleType.Pinned );" );
WriteLine( "handle.FuncA = GCHandle.Alloc( funcA );" );
WriteLine( "handle.FuncB = GCHandle.Alloc( funcB );" );
WriteLine( "handle.FuncC = GCHandle.Alloc( funcC );" );
WriteLine();
WriteLine( "//" );