#nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Barotrauma.Extensions; using Barotrauma.Networking; using Microsoft.Xna.Framework; namespace Barotrauma.Steam { public static class BulkDownloader { private static void CloseAllMessageBoxes() { GUIMessageBox.MessageBoxes.ForEachMod(b => { if (b is GUIMessageBox m) { m.Close(); } else { GUIMessageBox.MessageBoxes.Remove(b); } }); } public static void PrepareUpdates() { CloseAllMessageBoxes(); GUIMessageBox msgBox = new GUIMessageBox(headerText: "", text: TextManager.Get("DeterminingRequiredModUpdates"), buttons: Array.Empty()); TaskPool.Add( "BulkDownloader.PrepareUpdates > GetItemsThatNeedUpdating", GetItemsThatNeedUpdating(), t => { msgBox.Close(); if (!t.TryGetResult(out IReadOnlyList? items)) { return; } InitiateDownloads(items); }); } internal static void SubscribeToServerMods(IEnumerable missingIds, ConnectCommand rejoinCommand) { CloseAllMessageBoxes(); GUIMessageBox msgBox = new GUIMessageBox(headerText: "", text: TextManager.Get("PreparingWorkshopDownloads"), buttons: Array.Empty()); TaskPool.Add( "BulkDownloader.SubscribeToServerMods > GetItems", Task.WhenAll(missingIds.Select(SteamManager.Workshop.GetItem)), t => { msgBox.Close(); if (!t.TryGetResult(out Steamworks.Ugc.Item?[]? itemsNullable)) { return; } var items = itemsNullable .Where(it => it.HasValue) .Select(it => it ?? default) .ToArray(); items.ForEach(it => it.Subscribe()); InitiateDownloads(items, onComplete: () => { ContentPackageManager.UpdateContentPackageList(); GameMain.Instance.ConnectCommand = Option.Some(rejoinCommand); }); }); } public static async Task> GetItemsThatNeedUpdating() { var determiningTasks = ContentPackageManager.WorkshopPackages.Select(async p => (p, await p.IsUpToDate())); (ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks); return (await Task.WhenAll(outOfDatePackages.Where(p => !p.IsUpToDate) .Select(p => p.Package.UgcId) .NotNone() .OfType() .Select(async id => await SteamManager.Workshop.GetItem(id.Value)))) .NotNone().ToArray(); } public static void InitiateDownloads(IReadOnlyList itemsToDownload, Action? onComplete = null) { var msgBox = new GUIMessageBox(TextManager.Get("WorkshopItemDownloading"), "", relativeSize: (0.5f, 0.6f), buttons: new LocalizedString[] { TextManager.Get("Cancel") }); msgBox.Buttons[0].OnClicked = msgBox.Close; var modsList = new GUIListBox(new RectTransform((1.0f, 0.8f), msgBox.Content.RectTransform)) { HoverCursor = CursorState.Default }; foreach (var item in itemsToDownload) { var itemFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), modsList.Content.RectTransform), style: null) { CanBeFocused = false }; var itemTitle = new GUITextBlock(new RectTransform(Vector2.One, itemFrame.RectTransform), text: item.Title ?? ""); var itemDownloadProgress = new GUIProgressBar(new RectTransform((0.5f, 0.75f), itemFrame.RectTransform, Anchor.CenterRight), 0.0f) { Color = GUIStyle.Green }; var textShadow = new GUITextBlock(new RectTransform(Vector2.One, itemDownloadProgress.RectTransform) { AbsoluteOffset = new Point(GUI.IntScale(3)) }, "", textColor: Color.Black, textAlignment: Alignment.Center); var text = new GUITextBlock(new RectTransform(Vector2.One, itemDownloadProgress.RectTransform), "", textAlignment: Alignment.Center); var itemDownloadProgressUpdater = new GUICustomComponent( new RectTransform(Vector2.Zero, msgBox.Content.RectTransform), onUpdate: (f, component) => { float progress = 0.0f; if (item.IsDownloading) { progress = item.DownloadAmount; text.Text = textShadow.Text = TextManager.GetWithVariable( "PublishPopupDownload", "[percentage]", ((int)MathF.Round(item.DownloadAmount * 100)).ToString()); } else if (itemDownloadProgress.BarSize > 0.0f) { if (!item.IsInstalled && !SteamManager.Workshop.CanBeInstalled(item.Id)) { itemDownloadProgress.Color = GUIStyle.Red; text.Text = textShadow.Text = TextManager.Get("workshopiteminstallfailed"); } else { text.Text = textShadow.Text = TextManager.Get(item.IsInstalled ? "workshopiteminstalled" : "PublishPopupInstall"); } progress = 1.0f; } itemDownloadProgress.BarSize = Math.Max(itemDownloadProgress.BarSize, MathHelper.Lerp(itemDownloadProgress.BarSize, progress, 0.1f)); }); } TaskPool.Add("DownloadItems", DownloadItems(itemsToDownload, msgBox), _ => { if (GUIMessageBox.MessageBoxes.Contains(msgBox)) { onComplete?.Invoke(); } msgBox.Close(); ContentPackageManager.WorkshopPackages.Refresh(); ContentPackageManager.EnabledPackages.RefreshUpdatedMods(); if (SettingsMenu.Instance?.WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu) { mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true); } GameMain.MainMenuScreen.ResetModUpdateButton(); }); } private static async Task DownloadItems(IReadOnlyList itemsToDownload, GUIMessageBox msgBox) { foreach (var item in itemsToDownload) { DebugConsole.Log($"Reinstalling {item.Title}..."); await SteamManager.Workshop.Reinstall(item); DebugConsole.Log($"Finished installing {item.Title}..."); if (!GUIMessageBox.MessageBoxes.Contains(msgBox)) { DebugConsole.Log($"Download prompt closed, interrupting {nameof(DownloadItems)}."); break; } } DebugConsole.Log($"{nameof(DownloadItems)} finished."); } } }