CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

SteamworksUGC.cs (10656B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // The Command & Conquer Map Editor and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // The Command & Conquer Map Editor and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 using MobiusEditor.Model;
     16 using Steamworks;
     17 using System;
     18 using System.Collections.Generic;
     19 using System.Diagnostics;
     20 using System.IO;
     21 using System.Text;
     22 using System.Windows.Forms;
     23 
     24 namespace MobiusEditor.Utility
     25 {
     26     public interface ISteamworksOperation : IDisposable
     27     {
     28         bool Done { get; }
     29 
     30         bool Failed { get; }
     31 
     32         string Status { get; }
     33 
     34         void OnSuccess();
     35 
     36         void OnFailed();
     37     }
     38 
     39     public class SteamworksUGCPublishOperation : ISteamworksOperation
     40     {
     41         private readonly string ugcPath;
     42         private readonly IList<string> tags;
     43         private readonly Action onSuccess;
     44         private readonly Action<string> onFailed;
     45 
     46         private CallResult<CreateItemResult_t> createItemResult;
     47         private CallResult<SubmitItemUpdateResult_t> submitItemUpdateResult;
     48         private SteamSection steamSection = new SteamSection();
     49 
     50         public bool Done => !(createItemResult?.IsActive() ?? false) && !(submitItemUpdateResult?.IsActive() ?? false);
     51 
     52         public bool Failed { get; private set; }
     53 
     54         public string Status { get; private set; }
     55 
     56         public SteamworksUGCPublishOperation(string ugcPath, SteamSection steamSection, IList<string> tags, Action onSuccess, Action<string> onFailed)
     57         {
     58             this.ugcPath = ugcPath;
     59             this.steamSection = steamSection;
     60             this.tags = tags;
     61             this.onSuccess = onSuccess;
     62             this.onFailed = onFailed;
     63 
     64             if (steamSection.PublishedFileId == PublishedFileId_t.Invalid.m_PublishedFileId)
     65             {
     66                 CreateUGCItem();
     67             }
     68             else
     69             {
     70                 UpdateUGCItem();
     71             }
     72 
     73             Status = "Publishing UGC...";
     74         }
     75 
     76         public void OnSuccess() => onSuccess();
     77 
     78         public void OnFailed() => onFailed(Status);
     79 
     80         private void CreateUGCItem()
     81         {
     82             var steamAPICall = SteamUGC.CreateItem(SteamUtils.GetAppID(), EWorkshopFileType.k_EWorkshopFileTypeCommunity);
     83             if (steamAPICall == SteamAPICall_t.Invalid)
     84             {
     85                 Failed = true;
     86                 Status = "Publishing failed.";
     87                 return;
     88             }
     89 
     90             createItemResult = CallResult<CreateItemResult_t>.Create(OnCreateItemResult);
     91             createItemResult.Set(steamAPICall);
     92         }
     93 
     94         private void UpdateUGCItem()
     95         {
     96             var updateHandle = SteamUGC.StartItemUpdate(SteamUtils.GetAppID(), new PublishedFileId_t(steamSection.PublishedFileId));
     97             if (updateHandle == UGCUpdateHandle_t.Invalid)
     98             {
     99                 Failed = true;
    100                 Status = "Publishing failed.";
    101                 return;
    102             }
    103 
    104             bool success = true;
    105             success = success && SteamUGC.SetItemContent(updateHandle, ugcPath);
    106             success = success && SteamUGC.SetItemPreview(updateHandle, steamSection.PreviewFile);
    107             success = success && SteamUGC.SetItemUpdateLanguage(updateHandle, "English");
    108             success = success && SteamUGC.SetItemTitle(updateHandle, steamSection.Title);
    109             success = success && SteamUGC.SetItemDescription(updateHandle, steamSection.Description);
    110             success = success && SteamUGC.SetItemVisibility(updateHandle, steamSection.Visibility);
    111             success = success && SteamUGC.SetItemTags(updateHandle, tags);
    112             if (!success)
    113             {
    114                 Failed = true;
    115                 Status = "Publishing failed.";
    116                 return;
    117             }
    118 
    119             var steamAPICall = SteamUGC.SubmitItemUpdate(updateHandle, "");
    120             if (steamAPICall == SteamAPICall_t.Invalid)
    121             {
    122                 Failed = true;
    123                 Status = "Publishing failed.";
    124                 return;
    125             }
    126 
    127             submitItemUpdateResult = CallResult<SubmitItemUpdateResult_t>.Create(OnSubmitItemUpdateResult);
    128             submitItemUpdateResult.Set(steamAPICall);
    129         }
    130 
    131         private void OnCreateItemResult(CreateItemResult_t callback, bool ioFailure)
    132         {
    133             if (ioFailure)
    134             {
    135                 Failed = true;
    136                 Status = "Publishing failed.";
    137                 return;
    138             }
    139 
    140             switch (callback.m_eResult)
    141             {
    142                 case EResult.k_EResultOK:
    143                     steamSection.PublishedFileId = callback.m_nPublishedFileId.m_PublishedFileId;
    144                     UpdateUGCItem();
    145                     break;
    146                 case EResult.k_EResultFileNotFound:
    147                     Failed = true;
    148                     Status = "UGC not found.";
    149                     break;
    150                 case EResult.k_EResultNotLoggedOn:
    151                     Failed = true;
    152                     Status = "Not logged on.";
    153                     break;
    154                 default:
    155                     Failed = true;
    156                     Status = "Publishing failed.";
    157                     break;
    158             }
    159         }
    160 
    161         private void OnSubmitItemUpdateResult(SubmitItemUpdateResult_t callback, bool ioFailure)
    162         {
    163             if (ioFailure)
    164             {
    165                 Failed = true;
    166                 Status = "Publishing failed.";
    167                 return;
    168             }
    169 
    170             switch (callback.m_eResult)
    171             {
    172                 case EResult.k_EResultOK:
    173                     Status = "Done.";
    174                     steamSection.PublishedFileId = callback.m_nPublishedFileId.m_PublishedFileId;
    175                     break;
    176                 case EResult.k_EResultFileNotFound:
    177                     Failed = true;
    178                     Status = "UGC not found.";
    179                     break;
    180                 case EResult.k_EResultLimitExceeded:
    181                     Failed = true;
    182                     Status = "Size limit exceeded.";
    183                     break;
    184                 default:
    185                     Failed = true;
    186                     Status = string.Format("Publishing failed. ({0})", callback.m_eResult);
    187                     break;
    188             }
    189         }
    190 
    191         #region IDisposable Support
    192         private bool disposedValue = false;
    193 
    194         protected virtual void Dispose(bool disposing)
    195         {
    196             if (!disposedValue)
    197             {
    198                 if (disposing)
    199                 {
    200                     createItemResult?.Dispose();
    201                     submitItemUpdateResult?.Dispose();
    202                 }
    203                 disposedValue = true;
    204             }
    205         }
    206 
    207         public void Dispose()
    208         {
    209             Dispose(true);
    210         }
    211         #endregion
    212     }
    213 
    214     public static class SteamworksUGC
    215     {
    216         public static bool IsInit { get; private set; }
    217 
    218         public static ISteamworksOperation CurrentOperation { get; private set; }
    219 
    220         public static string WorkshopURL
    221         {
    222             get
    223             {
    224                 var app_id = IsInit ? SteamUtils.GetAppID() : AppId_t.Invalid;
    225                 if (app_id == AppId_t.Invalid)
    226                 {
    227                     return string.Empty;
    228                 }
    229                 return string.Format("http://steamcommunity.com/app/{0}/workshop/", app_id.ToString());
    230             }
    231         }
    232 
    233         public static bool IsSteamBuild => File.Exists("steam_appid.txt");
    234 
    235         private static Callback<GameLobbyJoinRequested_t> GameLobbyJoinRequested;
    236 
    237         public static bool Init()
    238         {
    239             if (IsInit)
    240             {
    241                 return true;
    242             }
    243 
    244             if (!IsSteamBuild)
    245             {
    246                 return false;
    247             }
    248 
    249             if (!Packsize.Test())
    250             {
    251                 return false;
    252             }
    253 
    254             if (!DllCheck.Test())
    255             {
    256                 return false;
    257             }
    258 
    259             if (!SteamAPI.Init())
    260             {
    261                 return false;
    262             }
    263 
    264             SteamClient.SetWarningMessageHook(new SteamAPIWarningMessageHook_t(SteamAPIDebugTextHook));
    265 
    266             GameLobbyJoinRequested = Callback<GameLobbyJoinRequested_t>.Create(OnGameLobbyJoinRequested);
    267 
    268             IsInit = true;
    269             return IsInit;
    270         }
    271 
    272         public static void Shutdown()
    273         {
    274             if (IsInit)
    275             {
    276                 GameLobbyJoinRequested?.Dispose();
    277                 GameLobbyJoinRequested = null;
    278 
    279                 CurrentOperation?.Dispose();
    280                 CurrentOperation = null;
    281 
    282                 SteamAPI.Shutdown();
    283                 IsInit = false;
    284             }
    285         }
    286 
    287         public static void Service()
    288         {
    289             SteamAPI.RunCallbacks();
    290 
    291             if (CurrentOperation?.Done ?? false)
    292             {
    293                 if (CurrentOperation.Failed)
    294                 {
    295                     CurrentOperation.OnFailed();
    296                 }
    297                 else
    298                 {
    299                     CurrentOperation.OnSuccess();
    300                 }
    301                 CurrentOperation.Dispose();
    302                 CurrentOperation = null;
    303             }
    304         }
    305 
    306         public static bool PublishUGC(string ugcPath, SteamSection steamSection, IList<string> tags, Action onSuccess, Action<string> onFailed)
    307         {
    308             if (CurrentOperation != null)
    309             {
    310                 return false;
    311             }
    312 
    313             CurrentOperation = new SteamworksUGCPublishOperation(ugcPath, steamSection, tags, onSuccess, onFailed);
    314 
    315             return true;
    316         }
    317 
    318         private static void SteamAPIDebugTextHook(int nSeverity, StringBuilder pchDebugText)
    319         {
    320             Debug.WriteLine(pchDebugText);
    321         }
    322 
    323         private static void OnGameLobbyJoinRequested(GameLobbyJoinRequested_t data)
    324         {
    325             MessageBox.Show("You cannot accept an invitation to a multiplayer game while using the map editor.", "Steam", MessageBoxButtons.OK, MessageBoxIcon.Information);
    326         }
    327     }
    328 }