mirror of
https://github.com/Facepunch/Facepunch.Steamworks.git
synced 2024-12-24 13:45:37 +03:00
Merge remote-tracking branch 'refs/remotes/Facepunch/master'
This commit is contained in:
commit
9516d1ed7a
@ -19,7 +19,7 @@ public void StatsGet()
|
||||
|
||||
bool GotStats = false;
|
||||
|
||||
server.Stats.Refresh( MySteamId, success =>
|
||||
server.Stats.Refresh( MySteamId, (steamid, success) =>
|
||||
{
|
||||
GotStats = true;
|
||||
Assert.IsTrue( success );
|
||||
|
@ -13,21 +13,57 @@ public class Achievements : IDisposable
|
||||
public Achievement[] All { get; private set; }
|
||||
|
||||
public event Action OnUpdated;
|
||||
public event Action<Achievement> OnAchievementStateChanged;
|
||||
|
||||
private List<Achievement> unlockedRecently = new List<Achievement>();
|
||||
|
||||
internal Achievements( Client c )
|
||||
{
|
||||
client = c;
|
||||
|
||||
All = new Achievement[0];
|
||||
|
||||
SteamNative.UserStatsReceived_t.RegisterCallback( c, UserStatsReceived );
|
||||
SteamNative.UserStatsStored_t.RegisterCallback( c, UserStatsStored );
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
var old = All;
|
||||
|
||||
All = Enumerable.Range( 0, (int)client.native.userstats.GetNumAchievements() )
|
||||
.Select( x => new Achievement( client, x ) )
|
||||
.Select( x =>
|
||||
{
|
||||
if ( old != null )
|
||||
{
|
||||
var name = client.native.userstats.GetAchievementName( (uint)x );
|
||||
var found = old.FirstOrDefault( y => y.Id == name );
|
||||
if ( found != null )
|
||||
{
|
||||
if ( found.Refresh() )
|
||||
{
|
||||
unlockedRecently.Add( found );
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return new Achievement( client, x );
|
||||
} )
|
||||
.ToArray();
|
||||
|
||||
foreach ( var i in unlockedRecently )
|
||||
{
|
||||
OnUnlocked( i );
|
||||
}
|
||||
|
||||
unlockedRecently.Clear();
|
||||
}
|
||||
|
||||
internal void OnUnlocked( Achievement a )
|
||||
{
|
||||
OnAchievementStateChanged?.Invoke( a );
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -50,14 +86,10 @@ public Achievement Find( string identifier )
|
||||
/// </summary>
|
||||
public bool Trigger( string identifier, bool apply = true )
|
||||
{
|
||||
var r = client.native.userstats.SetAchievement( identifier );
|
||||
var a = Find( identifier );
|
||||
if ( a == null ) return false;
|
||||
|
||||
if ( apply )
|
||||
{
|
||||
client.Stats.StoreStats();
|
||||
}
|
||||
|
||||
return r;
|
||||
return a.Trigger( apply );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -71,6 +103,17 @@ public bool Reset( string identifier )
|
||||
private void UserStatsReceived( UserStatsReceived_t stats, bool isError )
|
||||
{
|
||||
if ( isError ) return;
|
||||
if ( stats.GameID != client.AppId ) return;
|
||||
|
||||
Refresh();
|
||||
|
||||
OnUpdated?.Invoke();
|
||||
}
|
||||
|
||||
private void UserStatsStored( UserStatsStored_t stats, bool isError )
|
||||
{
|
||||
if ( isError ) return;
|
||||
if ( stats.GameID != client.AppId ) return;
|
||||
|
||||
Refresh();
|
||||
|
||||
@ -97,6 +140,7 @@ public class Achievement
|
||||
public DateTime UnlockTime { get; private set; }
|
||||
|
||||
private int iconId { get; set; } = -1;
|
||||
private int refreshCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// If this achievement is linked to a stat this will return the progress.
|
||||
@ -159,6 +203,9 @@ public Achievement( Client client, int index )
|
||||
/// </summary>
|
||||
public bool Trigger( bool apply = true )
|
||||
{
|
||||
if ( State )
|
||||
return false;
|
||||
|
||||
State = true;
|
||||
UnlockTime = DateTime.Now;
|
||||
|
||||
@ -169,6 +216,8 @@ public bool Trigger( bool apply = true )
|
||||
client.Stats.StoreStats();
|
||||
}
|
||||
|
||||
client.Achievements.OnUnlocked( this );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -185,10 +234,12 @@ public bool Reset()
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the unlock state. You shouldn't need to call this manually
|
||||
/// but it's here if you have to for some reason.
|
||||
/// but it's here if you have to for some reason. Retuns true if state changed (meaning, probably unlocked)
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
public bool Refresh()
|
||||
{
|
||||
bool previousState = State;
|
||||
|
||||
bool state = false;
|
||||
uint unlockTime;
|
||||
|
||||
@ -199,6 +250,15 @@ public void Refresh()
|
||||
State = state;
|
||||
UnlockTime = Utility.Epoch.ToDateTime( unlockTime );
|
||||
}
|
||||
|
||||
refreshCount++;
|
||||
|
||||
if ( previousState != State && refreshCount > 1 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,8 @@ public enum RequestType
|
||||
internal ulong BoardId;
|
||||
internal Client client;
|
||||
|
||||
private readonly Queue<Action> _onCreated = new Queue<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// The results from the last query. Can be null.
|
||||
/// </summary>
|
||||
@ -77,21 +79,45 @@ public void Dispose()
|
||||
client = null;
|
||||
}
|
||||
|
||||
private void DispatchOnCreatedCallbacks()
|
||||
{
|
||||
while ( _onCreated.Count > 0 )
|
||||
{
|
||||
_onCreated.Dequeue()();
|
||||
}
|
||||
}
|
||||
|
||||
private bool DeferOnCreated( Action onValid, FailureCallback onFailure = null )
|
||||
{
|
||||
if ( IsValid || IsError ) return false;
|
||||
|
||||
_onCreated.Enqueue( () =>
|
||||
{
|
||||
if ( IsValid ) onValid();
|
||||
else onFailure?.Invoke( Callbacks.Result.Fail );
|
||||
} );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void OnBoardCreated( LeaderboardFindResult_t result, bool error )
|
||||
{
|
||||
if ( error || ( result.LeaderboardFound == 0 ) )
|
||||
{
|
||||
IsError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
BoardId = result.SteamLeaderboard;
|
||||
|
||||
if ( IsValid )
|
||||
else
|
||||
{
|
||||
Name = client.native.userstats.GetLeaderboardName( BoardId );
|
||||
TotalEntries = client.native.userstats.GetLeaderboardEntryCount( BoardId );
|
||||
BoardId = result.SteamLeaderboard;
|
||||
|
||||
if ( IsValid )
|
||||
{
|
||||
Name = client.native.userstats.GetLeaderboardName( BoardId );
|
||||
TotalEntries = client.native.userstats.GetLeaderboardEntryCount( BoardId );
|
||||
}
|
||||
}
|
||||
|
||||
DispatchOnCreatedCallbacks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -103,7 +129,8 @@ internal void OnBoardCreated( LeaderboardFindResult_t result, bool error )
|
||||
/// </summary>
|
||||
public bool AddScore( bool onlyIfBeatsOldScore, int score, params int[] subscores )
|
||||
{
|
||||
if ( !IsValid ) return false;
|
||||
if ( IsError ) return false;
|
||||
if ( !IsValid ) return DeferOnCreated( () => AddScore( onlyIfBeatsOldScore, score, subscores ) );
|
||||
|
||||
var flags = LeaderboardUploadScoreMethod.ForceUpdate;
|
||||
if ( onlyIfBeatsOldScore ) flags = LeaderboardUploadScoreMethod.KeepBest;
|
||||
@ -142,7 +169,8 @@ public struct AddScoreResult
|
||||
/// </summary>
|
||||
public bool AddScore( bool onlyIfBeatsOldScore, int score, int[] subscores = null, AddScoreCallback onSuccess = null, FailureCallback onFailure = null )
|
||||
{
|
||||
if ( !IsValid ) return false;
|
||||
if ( IsError ) return false;
|
||||
if ( !IsValid ) return DeferOnCreated( () => AddScore( onlyIfBeatsOldScore, score, subscores, onSuccess, onFailure ), onFailure );
|
||||
|
||||
if ( subscores == null ) subscores = new int[0];
|
||||
|
||||
@ -182,7 +210,8 @@ public bool AddScore( bool onlyIfBeatsOldScore, int score, int[] subscores = nul
|
||||
/// <returns>True if the file attachment process has started</returns>
|
||||
public bool AttachRemoteFile( RemoteFile file, AttachRemoteFileCallback onSuccess = null, FailureCallback onFailure = null )
|
||||
{
|
||||
if ( !IsValid ) return false;
|
||||
if ( IsError ) return false;
|
||||
if ( !IsValid ) return DeferOnCreated( () => AttachRemoteFile( file, onSuccess, onFailure ), onFailure );
|
||||
|
||||
if ( file.IsShared )
|
||||
{
|
||||
@ -260,7 +289,8 @@ private unsafe void ReadScores( LeaderboardScoresDownloaded_t result, List<Entry
|
||||
/// <returns>Returns true if we have started the query</returns>
|
||||
public bool FetchScores( RequestType RequestType, int start, int end, FetchScoresCallback onSuccess, FailureCallback onFailure = null )
|
||||
{
|
||||
if ( !IsValid ) return false;
|
||||
if ( IsError ) return false;
|
||||
if ( !IsValid ) return DeferOnCreated( () => FetchScores( RequestType, start, end, onSuccess, onFailure ), onFailure );
|
||||
|
||||
client.native.userstats.DownloadLeaderboardEntries( BoardId, (LeaderboardDataRequest) RequestType, start, end, ( result, error ) =>
|
||||
{
|
||||
|
@ -35,5 +35,36 @@ public void Trigger()
|
||||
client.native.screenshots.TriggerScreenshot();
|
||||
}
|
||||
|
||||
public unsafe void Write( byte[] rgbData, int width, int height )
|
||||
{
|
||||
if ( rgbData == null )
|
||||
{
|
||||
throw new ArgumentNullException( nameof(rgbData) );
|
||||
}
|
||||
|
||||
if ( width < 1 )
|
||||
{
|
||||
throw new ArgumentOutOfRangeException( nameof(width), width,
|
||||
$"Expected {nameof(width)} to be at least 1." );
|
||||
}
|
||||
|
||||
if ( height < 1 )
|
||||
{
|
||||
throw new ArgumentOutOfRangeException( nameof(height), height,
|
||||
$"Expected {nameof(height)} to be at least 1." );
|
||||
}
|
||||
|
||||
var size = width * height * 3;
|
||||
if ( rgbData.Length < size )
|
||||
{
|
||||
throw new ArgumentException( nameof(rgbData),
|
||||
$"Expected {nameof(rgbData)} to contain at least {size} elements (actual size: {rgbData.Length})." );
|
||||
}
|
||||
|
||||
fixed ( byte* ptr = rgbData )
|
||||
{
|
||||
client.native.screenshots.WriteScreenshot( (IntPtr) ptr, (uint) rgbData.Length, width, height );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,19 @@ public void Download( bool highPriority = true )
|
||||
workshop.OnItemInstalled += OnItemInstalled;
|
||||
}
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
workshop.ugc.SubscribeItem(Id);
|
||||
SubscriptionCount++;
|
||||
}
|
||||
|
||||
public void UnSubscribe()
|
||||
{
|
||||
workshop.ugc.UnsubscribeItem(Id);
|
||||
SubscriptionCount--;
|
||||
}
|
||||
|
||||
|
||||
private void OnFileDownloaded( ulong fileid, Callbacks.Result result )
|
||||
{
|
||||
if ( fileid != Id ) return;
|
||||
|
@ -29,8 +29,7 @@ public partial class Workshop : IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Called when an item has been downloaded. This could have been
|
||||
/// because of a call to Download or because of a subscription triggered
|
||||
/// via the browser/app.
|
||||
/// because of a call to Download.
|
||||
/// </summary>
|
||||
public event Action<ulong, Callbacks.Result> OnFileDownloaded;
|
||||
|
||||
@ -39,7 +38,7 @@ public partial class Workshop : IDisposable
|
||||
/// because of a call to Download or because of a subscription triggered
|
||||
/// via the browser/app.
|
||||
/// </summary>
|
||||
internal event Action<ulong> OnItemInstalled;
|
||||
public event Action<ulong> OnItemInstalled;
|
||||
|
||||
internal Workshop( BaseSteamworks steamworks, SteamNative.SteamUGC ugc, SteamNative.SteamRemoteStorage remoteStorage )
|
||||
{
|
||||
@ -67,7 +66,7 @@ public void Dispose()
|
||||
|
||||
private void onItemInstalled( SteamNative.ItemInstalled_t obj, bool failed )
|
||||
{
|
||||
if ( OnItemInstalled != null )
|
||||
if ( OnItemInstalled != null && obj.AppID == Client.Instance.AppId )
|
||||
OnItemInstalled( obj.PublishedFileId );
|
||||
}
|
||||
|
||||
@ -123,7 +122,6 @@ public Item GetItem( ulong itemid )
|
||||
return new Item( itemid, this );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// How a query should be ordered.
|
||||
/// </summary>
|
||||
|
@ -31,7 +31,7 @@ public struct StatsReceived
|
||||
/// this will be called when the stats are recieved, the bool will signify whether
|
||||
/// it was successful or not.
|
||||
/// </summary>
|
||||
public void Refresh( ulong steamid, Action<bool> Callback = null )
|
||||
public void Refresh( ulong steamid, Action<ulong, bool> Callback = null )
|
||||
{
|
||||
if ( Callback == null )
|
||||
{
|
||||
@ -41,7 +41,7 @@ public void Refresh( ulong steamid, Action<bool> Callback = null )
|
||||
|
||||
server.native.gameServerStats.RequestUserStats( steamid, ( o, failed ) =>
|
||||
{
|
||||
Callback( o.Result == SteamNative.Result.OK && !failed );
|
||||
Callback( steamid, o.Result == SteamNative.Result.OK && !failed );
|
||||
} );
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ public void Refresh( ulong steamid, Action<bool> Callback = null )
|
||||
/// You can do that using this function. The callback will let you know if
|
||||
/// your action succeeded, but most of the time you can fire and forget.
|
||||
/// </summary>
|
||||
public void Commit( ulong steamid, Action<bool> Callback = null )
|
||||
public void Commit( ulong steamid, Action<ulong, bool> Callback = null )
|
||||
{
|
||||
if ( Callback == null )
|
||||
{
|
||||
@ -60,7 +60,7 @@ public void Commit( ulong steamid, Action<bool> Callback = null )
|
||||
|
||||
server.native.gameServerStats.StoreUserStats( steamid, ( o, failed ) =>
|
||||
{
|
||||
Callback( o.Result == SteamNative.Result.OK && !failed );
|
||||
Callback( steamid, o.Result == SteamNative.Result.OK && !failed );
|
||||
} );
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ public void Commit( ulong steamid, Action<bool> Callback = null )
|
||||
/// Set the named stat for this user. Setting stats should follow the rules
|
||||
/// you defined in Steamworks.
|
||||
/// </summary>
|
||||
public bool Set( ulong steamid, string name, int stat )
|
||||
public bool SetInt( ulong steamid, string name, int stat )
|
||||
{
|
||||
return server.native.gameServerStats.SetUserStat( steamid, name, stat );
|
||||
}
|
||||
@ -77,7 +77,7 @@ public bool Set( ulong steamid, string name, int stat )
|
||||
/// Set the named stat for this user. Setting stats should follow the rules
|
||||
/// you defined in Steamworks.
|
||||
/// </summary>
|
||||
public bool Set( ulong steamid, string name, float stat )
|
||||
public bool SetFloat( ulong steamid, string name, float stat )
|
||||
{
|
||||
return server.native.gameServerStats.SetUserStat0( steamid, name, stat );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user