Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 71 additions & 171 deletions Command/Upload/UploadCommand.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using KP_Steam_Uploader.Util;
Expand All @@ -20,10 +17,22 @@ public class UploadCommand: AbstractKpSteamCommand

[Option(CommandOptionType.SingleValue, ShortName = "i", LongName = "item", Description = "Workshop Id of item to update")]
public ulong ItemId { get; set; }

[Option(CommandOptionType.SingleValue, ShortName = "p", LongName = "path", Description = "Content path")]
public string Path { get; set; }


[Option(CommandOptionType.SingleValue, ShortName = "p", LongName = "contentPath", Description = "Content path")]
public string ContentPath { get; set; }

[Option(CommandOptionType.SingleValue, ShortName = "q", LongName = "previewPath", Description = "Preview file path")]
public string PreviewPath { get; set; }

[Option(CommandOptionType.SingleValue, ShortName = "t", LongName = "title", Description = "Title for workshop item")]
public string Title { get; set; }

[Option(CommandOptionType.SingleValue, ShortName = "d", LongName = "description", Description = "Description for workshop item")]
public string Description { get; set; }

[Option(CommandOptionType.SingleValue, ShortName = "t", LongName = "tags", Description = "Tags for workshop item (comma seperated list, Example: \"Senario,Multiplayer,Singleplayer\")")]
public string TagsString { get; set; }

[Option(CommandOptionType.SingleOrNoValue, LongName = "legacy", Description = "Legacy, single file based upload mode.")]
public (bool hasValue, string value) Legacy { get; }

Expand Down Expand Up @@ -51,192 +60,83 @@ protected override async Task<int> OnExecute(CommandLineApplication app)
var appId = Console.In.ReadLine();
AppId = uint.Parse(appId);
}

if (ItemId == 0)
if (ItemId == 0 && Title == "" && Description == "" && TagsString == "")
{
Console.Out.Write("Please specify ItemId: ");
var itemId = Console.In.ReadLine();
ItemId = uint.Parse(itemId);
Console.WriteLine("No ItemId or Item Attributes provided!");
Console.Write("Please specify an ItemId or Title: ");
var input = Console.In.ReadLine();
if (ulong.TryParse(input, out ulong result))
{
ItemId = result;
}
else
{
Console.WriteLine("String detected, assuming it was a title...");
Console.Write("Please specify a Description: ");
Description = Console.In.ReadLine();
Console.Write("Please specify the tags as a comma-separated list: ");
TagsString = Console.In.ReadLine();
}
}

var pTags = new List<string>(TagsString.Split(","));
var appRunning = true;
Thread callbackThread = null;
try
{
WriteSteamAppId(AppId);
InitializeSteam();


Logger.LogInformation($"Uploading item {ItemId} for app {AppId}");
callbackThread = new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (appRunning)
{
Logger.LogDebug("Running callbacks");
SteamAPI.RunCallbacks();
Thread.Sleep(100);
SteamAPI.RestartAppIfNecessary(new AppId_t(AppId));
}
});
callbackThread.Start();

Logger.LogInformation($"User has {SteamRemoteStorage.GetFileCount()} stale files in Steam Cloud.");
SteamUgcUtil.DeleteStaleFiles();

if (Legacy.hasValue)
{
await SteamRemoteStorageUpload();
if (ItemId != 0)
{
Logger.LogInformation($"Uploading item {ItemId} for app {AppId}");
await SteamUgcUtil.SteamRemoteStorageUpload(ContentPath, PreviewPath, Title, Description, pTags.ToArray(), ItemId);
}
else
{
Logger.LogInformation($"Publishing item \"{Title}\" for app {AppId}");
await SteamUgcUtil.SteamRemoteStorageUpload(ContentPath, PreviewPath, Title, Description, pTags.ToArray(), 0);
}
}
else
{
await SteamUGCUpload();
Logger.LogInformation($"Uploading item {ItemId} for app {AppId}");
await SteamUgcUtil.SteamUGCUpload(ContentPath, ItemId, ChangeNotes);
}

Logger.LogInformation($"Upload finished");

SteamUgcUtil.DeleteStaleFiles();
appRunning = false;
callbackThread.Join();
SteamAPI.Shutdown();
return 0;
}
catch (Exception ex)
{
OnException(ex);
SteamUgcUtil.DeleteStaleFiles();
appRunning = false;
callbackThread.Abort();
SteamAPI.Shutdown();
return 1;
}

}

protected async Task SteamRemoteStorageUpload()
{
if (ItemId == 0)
{
throw new Exception("Uploading new files without Workshop Id is not supported yet!");
}

if (!File.Exists(Path))
{
throw new Exception($"There is no file under: \"{Path}\"");
}

var fileName = (new FileInfo(Path)).Name;
var timestamp = DateTime.Now.ToString("yyyyMMddHHmmssffff");
var tempPath = NormalizePath($"kp_steam_cli_{timestamp}_{fileName}");
var fileContent = File.ReadAllBytes(Path);

Logger.LogDebug($"Copying file to temp path \"{tempPath}\"");
if (!SteamRemoteStorage.FileWrite(tempPath, fileContent, fileContent.Length))
{
throw new Exception("Could not move file to temporary path");
}

var updateRequest = SteamRemoteStorage.CreatePublishedFileUpdateRequest(new PublishedFileId_t(ItemId));
if (!SteamRemoteStorage.UpdatePublishedFileFile(updateRequest, tempPath))
{
throw new Exception("Steam file update failed!");
}

var uploadCall = SteamRemoteStorage.CommitPublishedFileUpdate(updateRequest);

_updatePublishedCallResultTask = new TaskCompletionSource<bool>();
_updatePublishedCallResult = CallResult<RemoteStorageUpdatePublishedFileResult_t>.Create(OnRemoteStorageUpdatePublishedFileResult);
_updatePublishedCallResult.Set(uploadCall);

// todo move callbacks loop to some better place
while (!_updatePublishedCallResultTask.Task.IsCompleted)
{
Logger.LogDebug("Running callbacks");
SteamAPI.RunCallbacks();
Thread.Sleep(100);
}

if (SteamRemoteStorage.FileExists(tempPath))
{
SteamRemoteStorage.FileDelete(tempPath);
}

SteamAPI.Shutdown();

// wait for task
await _updatePublishedCallResultTask.Task;
}

void OnRemoteStorageUpdatePublishedFileResult(RemoteStorageUpdatePublishedFileResult_t pCallback, bool bIOFailure) {
Logger.LogInformation("[RemoteStorageUpdatePublishedFileResult]" +
$" Result: {pCallback.m_eResult} " +
$"- Published file id: {pCallback.m_nPublishedFileId}" +
$"- Needs to accept workshop legal agreement: {pCallback.m_bUserNeedsToAcceptWorkshopLegalAgreement}");

if (pCallback.m_eResult != EResult.k_EResultOK)
{
_updatePublishedCallResultTask.TrySetException(
new Exception($"Uploading failed with result {pCallback.m_eResult}"));
return;
}

_updatePublishedCallResultTask.TrySetResult(true);
}

private CallResult<RemoteStorageUpdatePublishedFileResult_t> _updatePublishedCallResult;
private TaskCompletionSource<bool> _updatePublishedCallResultTask;

protected async Task SteamUGCUpload()
{
if (ItemId == 0)
{
throw new Exception("Uploading new items without Workshop Id is not supported yet!");
}

if (!Directory.Exists(Path))
{
throw new Exception($"There is no directory under: \"{Path}\"");
}

var update = SteamUGC.StartItemUpdate(new AppId_t(AppId), new PublishedFileId_t(ItemId));
if (!SteamUGC.SetItemContent(update, Path))
{
throw new Exception("Item Content could not be set!");
}

var itemDetails = await SteamUgcUtil.GetSingleQueryUgcResult(ItemId);
// If uploading for Arma check for Scenario tag.
// UGC upload over scenario is most likely a mistake which we will prevent.
if (AppId == (uint)AppIds.Arma)
{
var tags = itemDetails.m_rgchTags.Split(',');
if (tags.Contains("Scenario"))
{
throw new Exception("Scenarios can't be uploaded via UGC, use --legacy mode!");
}
}

var updateCall = SteamUGC.SubmitItemUpdate(update, ChangeNotes);

_submitItemUpdateResultTask = new TaskCompletionSource<bool>();
_submitItemUpdateResult = CallResult<SubmitItemUpdateResult_t>.Create(OnSubmitItemUpdateResult);
_submitItemUpdateResult.Set(updateCall);

// todo move callbacks loop to some better place
while (!_submitItemUpdateResultTask.Task.IsCompleted)
{
Logger.LogDebug("Running callbacks");
SteamAPI.RunCallbacks();
Thread.Sleep(100);
}

SteamAPI.Shutdown();

// wait for task
await _submitItemUpdateResultTask.Task;
}

void OnSubmitItemUpdateResult(SubmitItemUpdateResult_t pCallback, bool bIOFailure) {
Logger.LogDebug("[OnSubmitItemUpdateResult]" +
$" Result: {pCallback.m_eResult}" +
$"- Published file id: {pCallback.m_nPublishedFileId}" +
$"- Needs to accept workshop legal agreement: {pCallback.m_bUserNeedsToAcceptWorkshopLegalAgreement}");

if (pCallback.m_eResult != EResult.k_EResultOK)
{
_submitItemUpdateResultTask.TrySetException(
new Exception($"Uploading failed with result {pCallback.m_eResult}"));
return;
}

_submitItemUpdateResultTask.TrySetResult(true);
}

private CallResult<SubmitItemUpdateResult_t> _submitItemUpdateResult;
private TaskCompletionSource<bool> _submitItemUpdateResultTask;

// see https://github.com/Facepunch/Facepunch.Steamworks/blob/legacy/Facepunch.Steamworks/Client/RemoteStorage.cs#L15
public static string NormalizePath(string path)
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? new FileInfo($"x:/{path}").FullName.Substring(3)
: new FileInfo($"/x/{path}").FullName.Substring(3);
}

}
}
2 changes: 1 addition & 1 deletion KP-Steam.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
</ItemGroup>

<ItemGroup>
<Reference Include="Steamworks.NET, Version=12.0.0.0, Culture=neutral, PublicKeyToken=null">
<Reference Include="Steamworks.NET, Version=14.0.0.0, Culture=neutral, PublicKeyToken=null">
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@veteran29 This version of Steamworks.NET is the source from v14 with the changes I merged into v15 with rlabrecque/Steamworks.NET#440.

<HintPath>assembly\Steamworks.NET\Windows-x64\Steamworks.NET.dll</HintPath>
</Reference>
</ItemGroup>
Expand Down
Loading