Client LuaCsForBarotrauma
PublishTab.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Net.Mime;
7 using System.Threading.Tasks;
9 using Barotrauma.IO;
10 using Microsoft.Xna.Framework;
11 using Microsoft.Xna.Framework.Graphics;
12 using Steamworks;
13 using Directory = Barotrauma.IO.Directory;
14 using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
15 using Path = Barotrauma.IO.Path;
16 
17 namespace Barotrauma.Steam
18 {
19  sealed partial class MutableWorkshopMenu : WorkshopMenu
20  {
21  private class LocalThumbnail : IDisposable
22  {
23  public Texture2D? Texture { get; private set; } = null;
24  public bool Loading = true;
25 
26  public LocalThumbnail(string path)
27  {
28  TaskPool.Add($"LocalThumbnail {path}",
29  Task.Run(async () =>
30  {
31  await Task.Yield();
32  return TextureLoader.FromFile(path, compress: false, mipmap: false);
33  }),
34  (t) =>
35  {
36  Loading = false;
37  Task<Texture2D?> texTask = (t as Task<Texture2D?>)!;
38  if (disposed)
39  {
40  texTask.Result?.Dispose();
41  }
42  else
43  {
44  Texture = texTask.Result;
45  }
46  });
47  }
48 
49  private bool disposed = false;
50  public void Dispose()
51  {
52  if (disposed) { return; }
53 
54  disposed = true;
55  Texture?.Dispose();
56  }
57  }
58 
59  private LocalThumbnail? localThumbnail = null;
60 
61  private void CreateLocalThumbnail(string path, GUIFrame thumbnailContainer)
62  {
63  thumbnailContainer.ClearChildren();
64  localThumbnail?.Dispose();
65  localThumbnail = new LocalThumbnail(path);
66  CreateAsyncThumbnailComponent(thumbnailContainer, () => localThumbnail?.Texture, () => localThumbnail is { Loading: true });
67  }
68 
69  private static async Task<(int FileCount, int ByteCount)> GetModDirInfo(string dir, GUITextBlock label)
70  {
71  int fileCount = 0;
72  int byteCount = 0;
73 
74  var files = Directory.GetFiles(dir, pattern: "*", option: System.IO.SearchOption.AllDirectories);
75  foreach (var file in files)
76  {
77  await Task.Yield();
78  fileCount++;
79  byteCount += (int)(new Barotrauma.IO.FileInfo(file).Length);
80  label.Text = TextManager.GetWithVariables(
81  "ModDirInfo",
82  ("[filecount]", fileCount.ToString(CultureInfo.InvariantCulture)),
83  ("[size]", MathUtils.GetBytesReadable(byteCount)));
84  }
85 
86  return (fileCount, byteCount);
87  }
88 
89  private void DeselectPublishedItem()
90  {
91  if (selfModsListOption.TryUnwrap(out var selfModsList))
92  {
93  var deselectCarrier = selfModsList.Parent.FindChild(c => c.UserData is ActionCarrier { Id: var id } && id == "deselect");
94  Action? deselectAction = deselectCarrier.UserData is ActionCarrier { Action: var action }
95  ? action
96  : null;
97  deselectAction?.Invoke();
98  }
99 
100  SelectTab(Tab.Publish);
101  }
102 
103  private static bool PackageMatchesItem(ContentPackage p, Steamworks.Ugc.Item workshopItem)
104  => p.TryExtractSteamWorkshopId(out var workshopId) && workshopId.Value == workshopItem.Id;
105 
106  private void PopulatePublishTab(ItemOrPackage itemOrPackage, GUIFrame parentFrame)
107  {
108  ContentPackageManager.LocalPackages.Refresh();
109  ContentPackageManager.WorkshopPackages.Refresh();
110 
111  parentFrame.ClearChildren();
112  GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(Vector2.One, parentFrame.RectTransform),
113  childAnchor: Anchor.TopCenter);
114 
115  Steamworks.Ugc.Item workshopItem = itemOrPackage.TryGet(out Steamworks.Ugc.Item item) ? item : default;
116 
117  ContentPackage? localPackage = itemOrPackage.TryGet(out ContentPackage package)
118  ? package
119  : ContentPackageManager.LocalPackages.FirstOrDefault(p => PackageMatchesItem(p, workshopItem));
120  ContentPackage? workshopPackage
121  = ContentPackageManager.WorkshopPackages.FirstOrDefault(p => PackageMatchesItem(p, workshopItem));
122  if (localPackage is null)
123  {
124  new GUIFrame(new RectTransform((1.0f, 0.15f), mainLayout.RectTransform), style: null);
125 
126  //Local copy does not exist; check for Workshop copy
127  bool workshopCopyExists =
128  ContentPackageManager.WorkshopPackages.Any(p => PackageMatchesItem(p, workshopItem));
129 
130  new GUITextBlock(new RectTransform((0.7f, 0.4f), mainLayout.RectTransform),
131  TextManager.Get(workshopCopyExists ? "LocalCopyRequired" : "ItemInstallRequired"),
132  wrap: true);
133 
134  var buttonLayout = new GUILayoutGroup(new RectTransform((0.6f, 0.1f), mainLayout.RectTransform),
135  isHorizontal: true);
136  var yesButton = new GUIButton(new RectTransform((0.5f, 1.0f), buttonLayout.RectTransform),
137  text: TextManager.Get("Yes"))
138  {
139  OnClicked = (button, o) =>
140  {
141  CoroutineManager.StartCoroutine(MessageBoxCoroutine((currentStepText, messageBox)
142  => CreateLocalCopy(currentStepText, workshopItem, parentFrame)),
143  $"CreateLocalCopy {workshopItem.Id}");
144  return false;
145  }
146  };
147  var noButton = new GUIButton(new RectTransform((0.5f, 1.0f), buttonLayout.RectTransform),
148  text: TextManager.Get("No"))
149  {
150  OnClicked = (button, o) =>
151  {
152  DeselectPublishedItem();
153  return false;
154  }
155  };
156  }
157  else
158  {
159  if (!ContentPackageManager.LocalPackages.Contains(localPackage))
160  {
161  throw new Exception($"Content package \"{localPackage.Name}\" is not a local package!");
162  }
163 
164  var selectedTitle =
165  new GUITextBlock(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), localPackage.Name,
166  font: GUIStyle.LargeFont);
167  if (workshopItem.Id != 0)
168  {
169  var showInSteamButton = CreateShowInSteamButton(workshopItem, new RectTransform((0.2f, 1.0f), selectedTitle.RectTransform, Anchor.CenterRight));
170  }
171 
172  Spacer(mainLayout, height: 0.03f);
173 
174  var (leftTop, _, rightTop)
175  = CreateSidebars(mainLayout, leftWidth: 0.2f, centerWidth: 0.01f, rightWidth: 0.79f,
176  height: 0.4f);
177  leftTop.Stretch = true;
178  rightTop.Stretch = true;
179 
180  Label(leftTop, TextManager.Get("WorkshopItemPreviewImage"), GUIStyle.SubHeadingFont);
181  string? thumbnailPath = null;
182  var thumbnailContainer = CreateThumbnailContainer(leftTop, Vector2.One, ScaleBasis.BothWidth);
183  if (workshopItem.Id != 0)
184  {
185  CreateItemThumbnail(workshopItem, taskCancelSrc.Token, thumbnailContainer);
186  }
187 
188  var browseThumbnail =
189  new GUIButton(NewItemRectT(leftTop),
190  TextManager.Get("WorkshopItemBrowse"), style: "GUIButtonSmall")
191  {
192  OnClicked = (button, o) =>
193  {
194  FileSelection.ClearFileTypeFilters();
195  FileSelection.AddFileTypeFilter("PNG", "*.png");
196  FileSelection.AddFileTypeFilter("JPEG", "*.jpg, *.jpeg");
197  FileSelection.AddFileTypeFilter("All files", "*.*");
198  FileSelection.SelectFileTypeFilter("*.png");
199  FileSelection.CurrentDirectory
200  = Path.GetFullPath(Path.GetDirectoryName(localPackage.Path)!);
201 
202  FileSelection.OnFileSelected = (fn) =>
203  {
204  if (new FileInfo(fn).Length > SteamManager.Workshop.MaxThumbnailSize)
205  {
206  new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("WorkshopItemPreviewImageTooLarge"));
207  return;
208  }
209  thumbnailPath = fn;
210  CreateLocalThumbnail(thumbnailPath, thumbnailContainer);
211  };
212 
213  FileSelection.Open = true;
214 
215  return false;
216  }
217  };
218 
219  Label(rightTop, TextManager.Get("WorkshopItemTitle"), GUIStyle.SubHeadingFont);
220  var titleTextBox = new GUITextBox(NewItemRectT(rightTop), localPackage.Name);
221 
222  Label(rightTop, TextManager.Get("WorkshopItemDescription"), GUIStyle.SubHeadingFont);
223  var descriptionTextBox
224  = ScrollableTextBox(rightTop, 6.0f, workshopItem.Description ?? string.Empty);
225 
226  if (workshopItem.Id != 0)
227  {
228  TaskPool.Add(
229  $"GetFullDescription{workshopItem.Id}",
230  SteamManager.Workshop.GetItemAsap(workshopItem.Id.Value, withLongDescription: true),
231  t =>
232  {
233  if (!t.TryGetResult(out Option<Steamworks.Ugc.Item> itemWithDescriptionOption)) { return; }
234 
235  descriptionTextBox.Text =
236  itemWithDescriptionOption.TryUnwrap(out var itemWithDescription)
237  ? itemWithDescription.Description ?? descriptionTextBox.Text
238  : descriptionTextBox.Text;
239  descriptionTextBox.Deselect();
240  });
241  }
242 
243  var (leftBottom, _, rightBottom)
244  = CreateSidebars(mainLayout, leftWidth: 0.49f, centerWidth: 0.01f, rightWidth: 0.5f, height: 0.5f);
245  leftBottom.Stretch = true;
246  rightBottom.Stretch = true;
247 
248  Label(leftBottom, TextManager.Get("WorkshopItemVersion"), GUIStyle.SubHeadingFont);
249  var modVersion = localPackage.ModVersion;
250  if (workshopPackage is { ModVersion: { } workshopVersion } &&
251  modVersion.Equals(workshopVersion, StringComparison.OrdinalIgnoreCase))
252  {
253  modVersion = ModProject.IncrementModVersion(modVersion);
254  }
255 
256  char[] forbiddenVersionCharacters = { ';', '=' };
257  var versionTextBox = new GUITextBox(NewItemRectT(leftBottom), modVersion);
258  versionTextBox.OnTextChanged += (box, text) =>
259  {
260  if (text.Any(c => forbiddenVersionCharacters.Contains(c)))
261  {
262  foreach (var c in forbiddenVersionCharacters)
263  {
264  text = text.Replace($"{c}", "");
265  }
266 
267  box.Text = text;
268  box.Flash(GUIStyle.Red);
269  }
270 
271  return true;
272  };
273 
274  Label(leftBottom, TextManager.Get("WorkshopItemChangeNote"), GUIStyle.SubHeadingFont);
275  var changeNoteTextBox = ScrollableTextBox(leftBottom, 5.0f, "");
276 
277  Label(rightBottom, TextManager.Get("WorkshopItemTags"), GUIStyle.SubHeadingFont);
278  var tagsList = CreateTagsList(SteamManager.Workshop.Tags, NewItemRectT(rightBottom, heightScale: 4.0f),
279  canBeFocused: true);
280  Dictionary<Identifier, GUIButton> tagButtons = tagsList.Content.Children.Cast<GUIButton>()
281  .Select(b => ((Identifier)b.UserData, b)).ToDictionary();
282  if (workshopItem.Tags != null)
283  {
284  foreach (Identifier tag in workshopItem.Tags.ToIdentifiers())
285  {
286  if (tagButtons.TryGetValue(tag, out var button)) { button.Selected = true; }
287  }
288  }
289 
290  GUILayoutGroup visibilityLayout = new GUILayoutGroup(NewItemRectT(rightBottom), isHorizontal: true);
291 
292  var visibilityLabel = Label(visibilityLayout, TextManager.Get("WorkshopItemVisibility"), GUIStyle.SubHeadingFont);
293  visibilityLabel.RectTransform.RelativeSize = (0.6f, 1.0f);
294  visibilityLabel.TextAlignment = Alignment.CenterRight;
295 
296  Steamworks.Ugc.Visibility visibility = workshopItem.Visibility;
297  var visibilityDropdown = DropdownEnum(
298  visibilityLayout,
299  (v) => TextManager.Get($"WorkshopItemVisibility.{v}"),
300  visibility,
301  (v) => visibility = v);
302  visibilityDropdown.RectTransform.RelativeSize = (0.4f, 1.0f);
303 
304  var fileInfoLabel = Label(rightBottom, "", GUIStyle.Font, heightScale: 1.0f);
305  fileInfoLabel.TextAlignment = Alignment.CenterRight;
306  TaskPool.AddWithResult($"FileInfoLabel{workshopItem.Id}", GetModDirInfo(localPackage.Dir, fileInfoLabel), t => { });
307 
308  GUILayoutGroup buttonLayout = new GUILayoutGroup(NewItemRectT(rightBottom), isHorizontal: true, childAnchor: Anchor.CenterRight);
309 
310  RectTransform newButtonRectT()
311  => new RectTransform((0.4f, 1.0f), buttonLayout.RectTransform);
312 
313  var publishItemButton = new GUIButton(newButtonRectT(), TextManager.Get(workshopItem.Id != 0 ? "WorkshopItemUpdate" : "WorkshopItemPublish"))
314  {
315  OnClicked = (button, o) =>
316  {
317  //Reload the package to force hash recalculation
318  string packageName = localPackage.Name;
319  var result = ContentPackageManager.ReloadContentPackage(localPackage);
320  if (!result.TryUnwrapSuccess(out localPackage))
321  {
322  throw new Exception($"\"{packageName}\" was removed upon reload",
323  result.TryUnwrapFailure(out var exception) ? exception : null);
324  }
325 
326  //Set up the Ugc.Editor object that we'll need to publish
327  Steamworks.Ugc.Editor ugcEditor =
328  workshopItem.Id == 0
329  ? Steamworks.Ugc.Editor.NewCommunityFile
330  : new Steamworks.Ugc.Editor(workshopItem.Id);
331  ugcEditor = ugcEditor
332  .InLanguage(SteamUtils.SteamUILanguage ?? string.Empty)
333  .WithTitle(titleTextBox.Text)
334  .WithDescription(descriptionTextBox.Text)
335  .WithTags(tagButtons.Where(kvp => kvp.Value.Selected).Select(kvp => kvp.Key.Value))
336  .WithChangeLog(changeNoteTextBox.Text)
337  .WithMetaData($"gameversion={localPackage.GameVersion};modversion={versionTextBox.Text}")
338  .WithVisibility(visibility)
339  .WithPreviewFile(thumbnailPath);
340 
341  CoroutineManager.StartCoroutine(
342  MessageBoxCoroutine((currentStepText, messageBox)
343  => PublishItem(currentStepText, messageBox, versionTextBox.Text, ugcEditor, localPackage)));
344 
345  return false;
346  }
347  };
348 
349  if (workshopItem.Id != 0)
350  {
351  var deleteItemButton = new GUIButton(newButtonRectT(), TextManager.Get("WorkshopItemDelete"), color: GUIStyle.Red)
352  {
353  OnClicked = (button, o) =>
354  {
355  var confirmDeletion = new GUIMessageBox(
356  headerText: TextManager.Get("WorkshopItemDelete"),
357  text: TextManager.GetWithVariable("WorkshopItemDeleteVerification", "[itemname]", workshopItem.Title!),
358  buttons: new[] { TextManager.Get("Yes"), TextManager.Get("No") });
359  confirmDeletion.Buttons[0].OnClicked = (yesBuffer, o1) =>
360  {
361  TaskPool.AddWithResult($"Delete{workshopItem.Id}", Steamworks.SteamUGC.DeleteFileAsync(workshopItem.Id),
362  t =>
363  {
364  SteamManager.Workshop.Uninstall(workshopItem);
365  confirmDeletion.Close();
366  DeselectPublishedItem();
367  });
368  return false;
369  };
370  confirmDeletion.Buttons[1].OnClicked = (noButton, o1) =>
371  {
372  confirmDeletion.Close();
373  return false;
374  };
375 
376  return false;
377  },
378  HoverColor = Color.Lerp(GUIStyle.Red, Color.White, 0.3f),
379  PressedColor = Color.Lerp(GUIStyle.Red, Color.Black, 0.3f),
380  };
381  deleteItemButton.TextBlock.TextColor = Color.Black;
382  deleteItemButton.TextBlock.HoverTextColor = Color.Black;
383  }
384  }
385  }
386 
387  private IEnumerable<CoroutineStatus> MessageBoxCoroutine(Func<GUITextBlock, GUIMessageBox, IEnumerable<CoroutineStatus>> subcoroutine)
388  {
389  var messageBox = new GUIMessageBox("", TextManager.Get("ellipsis").Fallback("..."), buttons: new [] { TextManager.Get("Cancel") });
390  messageBox.Buttons[0].OnClicked = (button, o) =>
391  {
392  messageBox.Close();
393  return false;
394  };
395 
396  var coroutineEval = subcoroutine(messageBox.Text, messageBox).GetEnumerator();
397  while (true)
398  {
399  var status = coroutineEval.Current;
400  if (messageBox.Closed)
401  {
402  yield return CoroutineStatus.Success;
403  yield break;
404  }
405  else if (status == CoroutineStatus.Failure || status == CoroutineStatus.Success)
406  {
407  messageBox.Close();
408  yield return status;
409  yield break;
410  }
411  else
412  {
413  yield return status;
414  }
415  bool moveNext = true;
416  try
417  {
418  moveNext = coroutineEval.MoveNext();
419  }
420  catch (Exception e)
421  {
422  DebugConsole.ThrowError($"{e.Message} {e.StackTrace.CleanupStackTrace()}");
423  messageBox.Close();
424  }
425  if (!moveNext)
426  {
427  messageBox.Close();
428  }
429  }
430  }
431 
432  private IEnumerable<CoroutineStatus> CreateLocalCopy(GUITextBlock currentStepText, Steamworks.Ugc.Item workshopItem, GUIFrame parentFrame)
433  {
434  ContentPackage? workshopCopy =
435  ContentPackageManager.WorkshopPackages.FirstOrDefault(p => PackageMatchesItem(p, workshopItem));
436  if (workshopCopy is null)
437  {
438  if (!SteamManager.Workshop.CanBeInstalled(workshopItem))
439  {
440  SteamManager.Workshop.NukeDownload(workshopItem);
441  }
442  SteamManager.Workshop.DownloadModThenEnqueueInstall(workshopItem);
443  TaskPool.Add($"Install {workshopItem.Title}",
444  SteamManager.Workshop.WaitForInstall(workshopItem),
445  (t) =>
446  {
447  ContentPackageManager.WorkshopPackages.Refresh();
448  });
449  while (!ContentPackageManager.WorkshopPackages.Any(p => PackageMatchesItem(p, workshopItem)))
450  {
451  currentStepText.Text = SteamManager.Workshop.CanBeInstalled(workshopItem)
452  ? TextManager.Get("PublishPopupInstall")
453  : TextManager.GetWithVariable("PublishPopupDownload", "[percentage]", Percentage(workshopItem.DownloadAmount));
454  yield return new WaitForSeconds(0.5f);
455  }
456 
457  workshopCopy =
458  ContentPackageManager.WorkshopPackages.First(p => PackageMatchesItem(p, workshopItem));
459  }
460 
461  bool localCopyMade = false;
462  TaskPool.AddWithResult($"Create local copy {workshopItem.Title}",
463  SteamManager.Workshop.CreateLocalCopy(workshopCopy),
464  (t) =>
465  {
466  ContentPackageManager.LocalPackages.Refresh();
467  localCopyMade = true;
468  });
469  while (!localCopyMade)
470  {
471  currentStepText.Text = TextManager.Get("PublishPopupCreateLocal");
472  yield return new WaitForSeconds(0.5f);
473  }
474  PopulatePublishTab(workshopItem, parentFrame);
475 
476  yield return CoroutineStatus.Success;
477  }
478 
479  private IEnumerable<CoroutineStatus> PublishItem(
480  GUITextBlock currentStepText, GUIMessageBox messageBox,
481  string modVersion, Steamworks.Ugc.Editor editor, ContentPackage localPackage)
482  {
483  if (!SteamManager.IsInitialized)
484  {
485  yield return CoroutineStatus.Failure;
486  }
487 
488  bool stagingReady = false;
489  Exception? stagingException = null;
490  TaskPool.Add("CreatePublishStagingCopy",
491  SteamManager.Workshop.CreatePublishStagingCopy(editor.Title ?? localPackage.Name, modVersion, localPackage),
492  (t) =>
493  {
494  stagingReady = true;
495  stagingException = t.Exception?.GetInnermost();
496  });
497  TrySetText("PublishPopupStaging");
498 
499  while (!stagingReady) { yield return new WaitForSeconds(0.5f); }
500 
501  if (stagingException != null)
502  {
503  throw new Exception($"Failed to create staging copy: {stagingException.Message} {stagingException.StackTrace.CleanupStackTrace()}");
504  }
505 
506  editor = editor
507  .WithContent(SteamManager.Workshop.PublishStagingDir)
508  .ForAppId(SteamManager.AppID);
509 
510  messageBox.Buttons[0].Enabled = false;
511  Steamworks.Ugc.PublishResult? result = null;
512  Exception? resultException = null;
513  TaskPool.Add($"Publishing {localPackage.Name} ({localPackage.UgcId})",
514  editor.SubmitAsync(),
515  t =>
516  {
517  if (t.TryGetResult(out Steamworks.Ugc.PublishResult publishResult))
518  {
519  result = publishResult;
520  }
521  resultException = t.Exception?.GetInnermost();
522  });
523  TrySetText("PublishPopupSubmit");
524  while (!result.HasValue && resultException is null) { yield return new WaitForSeconds(0.5f); }
525 
526  if (result is { Success: true })
527  {
528  var resultId = result.Value.FileId;
529  bool packageMatchesResult(ContentPackage p)
530  => p.TryExtractSteamWorkshopId(out var workshopId) && workshopId.Value == resultId;
531  Steamworks.Ugc.Item resultItem = new Steamworks.Ugc.Item(resultId);
532  Task downloadTask = SteamManager.Workshop.ForceRedownload(resultItem);
533  while (!resultItem.IsInstalled && !downloadTask.IsCompleted)
534  {
535  currentStepText.Text = TextManager.GetWithVariable("PublishPopupDownload", "[percentage]", Percentage(resultItem.DownloadAmount));
536  yield return new WaitForSeconds(0.5f);
537  }
538 
539  //there seems to sometimes be a brief delay between the download task and the item being installed, wait a bit before deeming the install as failed
540  DateTime waitInstallUntil = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
541  while (!resultItem.IsInstalled || resultItem.IsDownloading)
542  {
543  if (DateTime.Now > waitInstallUntil)
544  {
545  throw new Exception($"Failed to install item: download task ended with status {downloadTask.Status}," +
546  $" item installed: {resultItem.IsInstalled}, " +
547  $" item downloading: {resultItem.IsDownloading}, " +
548  $"exception was {downloadTask.Exception?.GetInnermost()?.ToString().CleanupStackTrace() ?? "[NULL]"}");
549  }
550  yield return new WaitForSeconds(0.5f);
551  }
552 
553  ContentPackage? pkgToNuke
554  = ContentPackageManager.WorkshopPackages.FirstOrDefault(packageMatchesResult);
555  if (pkgToNuke != null)
556  {
557  Directory.Delete(pkgToNuke.Dir, recursive: true);
558  ContentPackageManager.WorkshopPackages.Refresh();
559  }
560 
561  bool installed = false;
562  TaskPool.Add(
563  "InstallNewlyPublished",
564  SteamManager.Workshop.WaitForInstall(resultItem),
565  (t) =>
566  {
567  installed = true;
568  });
569  while (!installed)
570  {
571  TrySetText("PublishPopupInstall");
572  yield return new WaitForSeconds(0.5f);
573  }
574 
575  ContentPackageManager.WorkshopPackages.Refresh();
576  ContentPackageManager.EnabledPackages.RefreshUpdatedMods();
577 
578  var localModProject = new ModProject(localPackage)
579  {
580  UgcId = Option<ContentPackageId>.Some(new SteamWorkshopId(resultId)),
581  ModVersion = modVersion
582  };
583  localModProject.DiscardHashAndInstallTime();
584  localModProject.Save(localPackage.Path);
585  ContentPackageManager.ReloadContentPackage(localPackage);
586  DeselectPublishedItem();
587 
588  if (result.Value.NeedsWorkshopAgreement)
589  {
590  SteamManager.OverlayCustomUrl(resultItem.Url);
591  }
592  new GUIMessageBox(string.Empty, TextManager.GetWithVariable("workshopitempublished", "[itemname]", localPackage.Name));
593  }
594  else if (resultException != null)
595  {
596  throw new Exception($"Failed to publish item: {resultException.Message} {resultException.StackTrace.CleanupStackTrace()}");
597  }
598  else
599  {
600  new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("workshopitempublishfailed", "[itemname]", localPackage.Name));
601  }
602 
603  SteamManager.Workshop.DeletePublishStagingCopy();
604  messageBox.Close();
605 
606  void TrySetText(string textTag)
607  {
608  if (currentStepText?.Text != null)
609  {
610  currentStepText.Text = TextManager.Get(textTag);
611  }
612  }
613  }
614  }
615 }
static GUITextBox ScrollableTextBox(GUILayoutGroup parent, float heightScale, string text)
Definition: UiUtil.cs:25
static void Spacer(GUILayoutGroup parent, float height=0.03f)
Definition: UiUtil.cs:15
static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font, float heightScale=1.0f)
Definition: UiUtil.cs:20
static RectTransform NewItemRectT(GUILayoutGroup parent, float heightScale=1.0f)