Workshop file downloading

This commit is contained in:
Garry Newman 2016-10-06 14:43:52 +01:00
parent 1a61766b2b
commit 3ff2b304a5
7 changed files with 297 additions and 28 deletions

View File

@ -30,6 +30,9 @@ public void Query()
Assert.IsTrue( Query.TotalResults > 0 );
Assert.IsTrue( Query.Items.Length > 0 );
Console.WriteLine( "Query.TotalResults: {0}", Query.TotalResults );
Console.WriteLine( "Query.Items.Length: {0}", Query.Items.Length );
// results
Console.WriteLine( "Searching" );
@ -196,7 +199,7 @@ public void DownloadFile()
using ( var Query = client.Workshop.CreateQuery() )
{
Query.FileId.Add( 751993251 );
Query.FileId.Add( 661319648 );
Query.Run();
Assert.IsTrue( Query.IsRunning );
@ -209,7 +212,26 @@ public void DownloadFile()
var item = Query.Items[0];
item.Download();
if ( !item.Installed )
{
item.Download();
while ( item.Downloading )
{
Thread.Sleep( 500 );
client.Update();
Console.WriteLine( "Download Progress: {0}", item.DownloadProgress );
}
}
Assert.IsNotNull( item.Directory );
Assert.AreNotEqual( 0, item.Size );
Console.WriteLine( "item.Installed: {0}", item.Installed );
Console.WriteLine( "item.Downloading: {0}", item.Downloading );
Console.WriteLine( "item.DownloadPending: {0}", item.DownloadPending );
Console.WriteLine( "item.Directory: {0}", item.Directory );
Console.WriteLine( "item.Size: {0}mb", (item.Size / 1024 / 1024) );
}
}

View File

@ -65,7 +65,7 @@ public enum MessageType : int
/// </summary>
internal void AddCallback<T>( Action<T> Callback, int id )
{
var callback = new Facepunch.Steamworks.Interop.Callback<T>( IsGameServer, id, Callback );
var callback = new Callback<T>( IsGameServer, id, Callback );
Disposables.Add( callback );
}

View File

@ -12,4 +12,113 @@ internal static class Index
internal const int RemoteStorage = 1300;
internal const int UGC = 3400;
}
public enum Result : int
{
OK = 1, // success
Fail = 2, // generic failure
NoConnection = 3, // no/failed network connection
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
InvalidPassword = 5, // password/ticket is invalid
LoggedInElsewhere = 6, // same user logged in elsewhere
InvalidProtocolVer = 7, // protocol version is incorrect
InvalidParam = 8, // a parameter is incorrect
FileNotFound = 9, // file was not found
Busy = 10, // called method busy - action not taken
InvalidState = 11, // called object was in an invalid state
InvalidName = 12, // name is invalid
InvalidEmail = 13, // email is invalid
DuplicateName = 14, // name is not unique
AccessDenied = 15, // access is denied
Timeout = 16, // operation timed out
Banned = 17, // VAC2 banned
AccountNotFound = 18, // account not found
InvalidSteamID = 19, // steamID is invalid
ServiceUnavailable = 20, // The requested service is currently unavailable
NotLoggedOn = 21, // The user is not logged on
Pending = 22, // Request is pending (may be in process, or waiting on third party)
EncryptionFailure = 23, // Encryption or Decryption failed
InsufficientPrivilege = 24, // Insufficient privilege
LimitExceeded = 25, // Too much of a good thing
Revoked = 26, // Access has been revoked (used for revoked guest passes)
Expired = 27, // License/Guest pass the user is trying to access is expired
AlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
DuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
AlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
IPNotFound = 31, // IP address not found
PersistFailed = 32, // failed to write change to the data store
LockingFailed = 33, // failed to acquire access lock for this operation
LogonSessionReplaced = 34,
ConnectFailed = 35,
HandshakeFailed = 36,
IOFailure = 37,
RemoteDisconnect = 38,
ShoppingCartNotFound = 39, // failed to find the shopping cart requested
Blocked = 40, // a user didn't allow it
Ignored = 41, // target is ignoring sender
NoMatch = 42, // nothing matching the request found
AccountDisabled = 43,
ServiceReadOnly = 44, // this service is not accepting content changes right now
AccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
AdministratorOK = 46, // allowed to take this action, but only because requester is admin
ContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
TryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
PasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
AlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
Suspended = 51, // Long running operation (content download) suspended/paused
Cancelled = 52, // Operation canceled (typically by user: content download)
DataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
DiskFull = 54, // Operation canceled - not enough disk space.
RemoteCallFailed = 55, // an remote call or IPC call failed
PasswordUnset = 56, // Password could not be verified as it's unset server side
ExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
PSNTicketInvalid = 58, // PSN ticket was invalid
ExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
RemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
IllegalPassword = 61, // The requested new password is not legal
SameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
AccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
CannotUseOldPassword = 64, // The requested new password is not legal
InvalidLoginAuthCode = 65, // account login denied due to auth code invalid
AccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
HardwareNotCapableOfIPT = 67, //
IPTInitError = 68, //
ParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
FacebookQueryError = 70, // Facebook query returned an error
ExpiredLoginAuthCode = 71, // account login denied due to auth code expired
IPLoginRestrictionFailed = 72,
AccountLockedDown = 73,
AccountLogonDeniedVerifiedEmailRequired = 74,
NoMatchingURL = 75,
BadResponse = 76, // parse failure, missing field, etc.
RequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
ValueOutOfRange = 78, // the value entered is outside the acceptable range
UnexpectedError = 79, // something happened that we didn't expect to ever happen
Disabled = 80, // The requested service has been configured to be unavailable
InvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid !
RestrictedDevice = 82, // The device being used is not allowed to perform this action
RegionLocked = 83, // The action could not be complete because it is region restricted
RateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent
AccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login
ItemDeleted = 86, // The thing we're trying to access has been deleted
AccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker
TwoFactorCodeMismatch = 88, // two factor code mismatch
TwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match
AccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners
NotModified = 91, // data not modified
NoMobileDevice = 92, // the account does not have a mobile device associated with it
TimeNotSynced = 93, // the time presented is out of range or tolerance
SmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.)
AccountLimitExceeded = 95, // Too many accounts access this resource
AccountActivityLimitExceeded = 96, // Too many changes to this account
PhoneActivityLimitExceeded = 97, // Too many changes to this phone
RefundToWallet = 98, // Cannot refund to payment method, must use wallet
EmailSendFailure = 99, // Cannot send an email
NotSettled = 100, // Can't perform operation till payment has settled
NeedCaptcha = 101, // Needs to provide a valid captcha
GSLTDenied = 102, // a game server login token owned by this token's owner has been banned
GSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone)
InvalidItemType = 104, // the type of thing we were requested to act on is invalid
IPBanned = 105, // the ip address has been banned from taking this action
};
}

View File

@ -7,9 +7,36 @@
namespace Facepunch.Steamworks.Callbacks.Workshop
{
[StructLayout( LayoutKind.Explicit )]
internal class ItemInstalled
{
[FieldOffset(0)]
public uint AppId;
[FieldOffset(4)]
public ulong FileId;
public const int CallbackId = Index.UGC + 5;
};
[StructLayout( LayoutKind.Explicit )]
internal class DownloadResult
{
[FieldOffset(0)]
public uint AppId;
[FieldOffset(4)]
public ulong FileId;
[FieldOffset(12)]
public Result Result;
public const int CallbackId = Index.UGC + 6;
};
internal class QueryCompleted : CallResult<QueryCompleted.Data>
{
public override int CallbackId { get { return Callbacks.Index.UGC + 1; } }
public override int CallbackId { get { return Index.UGC + 1; } }
[StructLayout( LayoutKind.Sequential )]
internal struct Data

View File

@ -15,7 +15,7 @@ public partial class Workshop
{
public class Item
{
internal ISteamUGC ugc;
internal Workshop workshop;
public string Description { get; private set; }
public ulong Id { get; private set; }
@ -26,11 +26,11 @@ public class Item
public uint VotesDown { get; private set; }
public uint VotesUp { get; private set; }
internal static Item From( SteamUGCDetails_t details, ISteamUGC ugc )
internal static Item From( SteamUGCDetails_t details, Workshop workshop )
{
var item = new Item();
item.ugc = ugc;
item.workshop = workshop;
item.Id = details.m_nPublishedFileId;
item.Title = details.m_rgchTitle;
item.Description = details.m_rgchDescription;
@ -39,13 +39,103 @@ internal static Item From( SteamUGCDetails_t details, ISteamUGC ugc )
item.Score = details.m_flScore;
item.VotesUp = details.m_unVotesUp;
item.VotesDown = details.m_unVotesDown;
item.UpdateState();
return item;
}
public void Download()
public void Download( bool highPriority = true )
{
UpdateState();
if ( Installed ) return;
if ( Downloading ) return;
if ( !workshop.ugc.DownloadItem( Id, highPriority ) )
{
Console.WriteLine( "Download Failed" );
return;
}
workshop.OnFileDownloaded += OnFileDownloaded;
UpdateState();
Downloading = true;
}
private void OnFileDownloaded( ulong fileid, Callbacks.Result result )
{
workshop.OnFileDownloaded -= OnFileDownloaded;
UpdateState();
}
public ulong BytesDownloaded { get { UpdateDownloadProgress(); return _BytesDownloaded; } }
public ulong BytesTotalDownload { get { UpdateDownloadProgress(); return _BytesTotal; } }
public double DownloadProgress
{
get
{
UpdateDownloadProgress();
if ( _BytesTotal == 0 ) return 0;
return (double)_BytesDownloaded / (double)_BytesTotal;
}
}
public bool Installed { get; private set; }
public bool Downloading { get; private set; }
public bool DownloadPending { get; private set; }
public bool Subscribed { get; private set; }
public bool NeedsUpdate { get; private set; }
public DirectoryInfo Directory { get; private set; }
public ulong Size { get; private set; }
private ulong _BytesDownloaded, _BytesTotal;
internal void UpdateDownloadProgress()
{
workshop.ugc.GetItemDownloadInfo( Id, out _BytesDownloaded, out _BytesTotal );
}
internal void UpdateState()
{
var state = workshop.ugc.GetItemState( Id );
Installed = ( state & (uint) EItemState.k_EItemStateInstalled ) != 0;
Downloading = ( state & (uint) EItemState.k_EItemStateDownloading ) != 0;
DownloadPending = ( state & (uint) EItemState.k_EItemStateDownloadPending ) != 0;
Subscribed = ( state & (uint) EItemState.k_EItemStateSubscribed ) != 0;
NeedsUpdate = ( state & (uint) EItemState.k_EItemStateNeedsUpdate ) != 0;
if ( Installed && Directory == null )
{
ulong sizeOnDisk;
string folder;
uint timestamp;
workshop.ugc.GetItemInstallInfo( Id, out sizeOnDisk, out folder, out timestamp );
Directory = new DirectoryInfo( folder );
Size = sizeOnDisk;
if ( !Directory.Exists )
{
Size = 0;
Directory = null;
Installed = false;
}
}
}
public void VoteUp()
{
workshop.ugc.SetUserItemVote( Id, true );
}
public void VoteDown()
{
workshop.ugc.SetUserItemVote( Id, false );
}
}
}

View File

@ -4,7 +4,6 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Facepunch.Steamworks.Callbacks.Networking;
using Facepunch.Steamworks.Callbacks.Workshop;
using Facepunch.Steamworks.Interop;
using Valve.Steamworks;
@ -18,13 +17,32 @@ public partial class Workshop
internal ISteamUGC ugc;
internal BaseSteamworks steamworks;
internal event Action<ulong, Callbacks.Result> OnFileDownloaded;
internal event Action<ulong> OnItemInstalled;
internal Workshop( BaseSteamworks sw, ISteamUGC ugc )
{
this.ugc = ugc;
this.steamworks = sw;
// sw.AddCallback<P2PSessionRequest>( onP2PConnectionRequest, P2PSessionRequest.CallbackId );
sw.AddCallback<DownloadResult>( onDownloadResult, DownloadResult.CallbackId );
sw.AddCallback<ItemInstalled>( onItemInstalled, ItemInstalled.CallbackId );
}
private void onItemInstalled( ItemInstalled obj )
{
Console.WriteLine( "OnItemInstalled" );
if ( OnItemInstalled != null )
OnItemInstalled( obj.FileId );
}
private void onDownloadResult( DownloadResult obj )
{
Console.WriteLine( "onDownloadResult" );
if ( OnFileDownloaded != null )
OnFileDownloaded( obj.FileId, obj.Result );
}
public enum Order
@ -148,7 +166,7 @@ void OnResult( QueryCompleted.Data data )
SteamUGCDetails_t details = new SteamUGCDetails_t();
workshop.ugc.GetQueryUGCResult( data.Handle, (uint) i, ref details );
Items[i] = Item.From( details, workshop.ugc );
Items[i] = Item.From( details, workshop );
}
TotalResults = (int) data.m_unTotalMatchingResults;
@ -199,7 +217,7 @@ public void Block()
public void Dispose()
{
// ReleaseQueryUGCRequest
}
}
}

View File

@ -732,7 +732,7 @@ internal class Extended
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetLaunchQueryParam" )]
internal static extern IntPtr SteamAPI_ISteamApps_GetLaunchQueryParam( IntPtr instancePtr, string pchKey );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetDlcDownloadProgress" )]
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamApps_GetDlcDownloadProgress( IntPtr instancePtr, uint nAppID, ref ulong punBytesDownloaded, ref ulong punBytesTotal );
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamApps_GetDlcDownloadProgress( IntPtr instancePtr, uint nAppID, out ulong punBytesDownloaded, out ulong punBytesTotal );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetAppBuildId" )]
internal static extern int SteamAPI_ISteamApps_GetAppBuildId( IntPtr instancePtr );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_RequestAllProofOfPurchaseKeys" )]
@ -1088,9 +1088,9 @@ internal class Extended
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamUGC_GetItemState" )]
internal static extern uint SteamAPI_ISteamUGC_GetItemState( IntPtr instancePtr, ulong nPublishedFileID );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamUGC_GetItemInstallInfo" )]
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamUGC_GetItemInstallInfo( IntPtr instancePtr, ulong nPublishedFileID, ref ulong punSizeOnDisk, System.Text.StringBuilder pchFolder, uint cchFolderSize, ref uint punTimeStamp );
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamUGC_GetItemInstallInfo( IntPtr instancePtr, ulong nPublishedFileID, out ulong punSizeOnDisk, System.Text.StringBuilder pchFolder, uint cchFolderSize, out uint punTimeStamp );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamUGC_GetItemDownloadInfo" )]
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamUGC_GetItemDownloadInfo( IntPtr instancePtr, ulong nPublishedFileID, ref ulong punBytesDownloaded, ref ulong punBytesTotal );
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamUGC_GetItemDownloadInfo( IntPtr instancePtr, ulong nPublishedFileID, out ulong punBytesDownloaded, out ulong punBytesTotal );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamUGC_DownloadItem" )]
[return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ISteamUGC_DownloadItem( IntPtr instancePtr, ulong nPublishedFileID, bool bHighPriority );
[DllImportAttribute( Config.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamUGC_BInitWorkshopForGameServer" )]
@ -2003,7 +2003,7 @@ internal abstract class ISteamApps
internal abstract bool BIsAppInstalled( uint appID );
internal abstract ulong GetAppOwner();
internal abstract string GetLaunchQueryParam( string pchKey );
internal abstract bool GetDlcDownloadProgress( uint nAppID, ref ulong punBytesDownloaded, ref ulong punBytesTotal );
internal abstract bool GetDlcDownloadProgress( uint nAppID, out ulong punBytesDownloaded, out ulong punBytesTotal );
internal abstract int GetAppBuildId();
internal abstract void RequestAllProofOfPurchaseKeys();
}
@ -2229,8 +2229,8 @@ internal abstract class ISteamUGC
internal abstract uint GetNumSubscribedItems();
internal abstract uint GetSubscribedItems( ref ulong pvecPublishedFileID, uint cMaxEntries );
internal abstract uint GetItemState( ulong nPublishedFileID );
internal abstract bool GetItemInstallInfo( ulong nPublishedFileID, ref ulong punSizeOnDisk, out string pchFolder, ref uint punTimeStamp );
internal abstract bool GetItemDownloadInfo( ulong nPublishedFileID, ref ulong punBytesDownloaded, ref ulong punBytesTotal );
internal abstract bool GetItemInstallInfo( ulong nPublishedFileID, out ulong punSizeOnDisk, out string pchFolder, out uint punTimeStamp );
internal abstract bool GetItemDownloadInfo( ulong nPublishedFileID, out ulong punBytesDownloaded, out ulong punBytesTotal );
internal abstract bool DownloadItem( ulong nPublishedFileID, bool bHighPriority );
internal abstract bool BInitWorkshopForGameServer( uint unWorkshopDepotID, string pszFolder );
internal abstract void SuspendDownloads( bool bSuspend );
@ -4726,12 +4726,12 @@ internal override string GetLaunchQueryParam( string pchKey )
IntPtr result = NativeEntrypoints.SteamAPI_ISteamApps_GetLaunchQueryParam(m_pSteamApps,pchKey);
return Marshal.PtrToStringAnsi( result );
}
internal override bool GetDlcDownloadProgress( uint nAppID, ref ulong punBytesDownloaded, ref ulong punBytesTotal )
internal override bool GetDlcDownloadProgress( uint nAppID, out ulong punBytesDownloaded, out ulong punBytesTotal )
{
CheckIfUsable();
punBytesDownloaded = 0;
punBytesTotal = 0;
bool result = NativeEntrypoints.SteamAPI_ISteamApps_GetDlcDownloadProgress(m_pSteamApps,nAppID,ref punBytesDownloaded,ref punBytesTotal);
bool result = NativeEntrypoints.SteamAPI_ISteamApps_GetDlcDownloadProgress(m_pSteamApps,nAppID,out punBytesDownloaded,out punBytesTotal);
return result;
}
internal override int GetAppBuildId()
@ -5985,22 +5985,25 @@ internal override uint GetItemState( ulong nPublishedFileID )
uint result = NativeEntrypoints.SteamAPI_ISteamUGC_GetItemState(m_pSteamUGC,nPublishedFileID);
return result;
}
internal override bool GetItemInstallInfo( ulong nPublishedFileID, ref ulong punSizeOnDisk, out string pchFolder, ref uint punTimeStamp )
static System.Text.StringBuilder StringBuffer = new System.Text.StringBuilder(1024 * 16);
internal override bool GetItemInstallInfo( ulong nPublishedFileID, out ulong punSizeOnDisk, out string pchFolder, out uint punTimeStamp )
{
CheckIfUsable();
punSizeOnDisk = 0;
punTimeStamp = 0;
System.Text.StringBuilder pStrBuffer1 = new System.Text.StringBuilder(2048);
bool result = NativeEntrypoints.SteamAPI_ISteamUGC_GetItemInstallInfo(m_pSteamUGC,nPublishedFileID,ref punSizeOnDisk,pStrBuffer1,2048,ref punTimeStamp);
pchFolder = pStrBuffer1.ToString();
bool result = NativeEntrypoints.SteamAPI_ISteamUGC_GetItemInstallInfo (m_pSteamUGC,nPublishedFileID, out punSizeOnDisk, StringBuffer, (uint)StringBuffer.Capacity, out punTimeStamp );
pchFolder = StringBuffer.ToString();
return result;
}
internal override bool GetItemDownloadInfo( ulong nPublishedFileID, ref ulong punBytesDownloaded, ref ulong punBytesTotal )
internal override bool GetItemDownloadInfo( ulong nPublishedFileID, out ulong punBytesDownloaded, out ulong punBytesTotal )
{
CheckIfUsable();
punBytesDownloaded = 0;
punBytesTotal = 0;
bool result = NativeEntrypoints.SteamAPI_ISteamUGC_GetItemDownloadInfo(m_pSteamUGC,nPublishedFileID,ref punBytesDownloaded,ref punBytesTotal);
bool result = NativeEntrypoints.SteamAPI_ISteamUGC_GetItemDownloadInfo(m_pSteamUGC,nPublishedFileID,out punBytesDownloaded,out punBytesTotal);
return result;
}
internal override bool DownloadItem( ulong nPublishedFileID, bool bHighPriority )