3 using System.Collections.Generic;
6 using System.Threading.Tasks;
10 using Microsoft.Xna.Framework;
19 public static class LegacySteamUgcTransition
21 private const string readmeName =
"LOCALMODS_README.txt";
23 private enum ModsListChildType
29 public static void Prepare()
31 TaskPool.Add(
"UgcTransition.Prepare", DetermineItemsToTransition(), t =>
33 if (!t.TryGetResult(out (OldSubs, OldItemAssemblies, OldMods) result)) {
return; }
34 var (subs, itemAssemblies, mods) = result;
35 if (!subs.FilePaths.Any() && !itemAssemblies.FilePaths.Any() && !mods.Mods.Any()) {
return; }
37 var msgBox =
new GUIMessageBox(TextManager.Get(
"Ugc.TransferTitle"),
"", relativeSize: (0.5f, 0.8f),
38 buttons:
new LocalizedString[] { TextManager.Get(
"Ugc.TransferButton") });
42 style:
"GUICancelButton")
44 OnClicked = (button, o) =>
52 text: TextManager.Get(
"Ugc.TransferDesc"), wrap:
true, textAlignment: Alignment.CenterLeft);
58 Dictionary<string, GUITickBox> pathTickboxMap =
new Dictionary<string, GUITickBox>();
66 UserData = ModsListChildType.Header
68 if (str is
RawLString { Value:
"" }) {
return; }
72 label: str, font: GUIStyle.SubHeadingFont)
77 if (!clicked) {
return true; }
78 bool toggleTickbox =
false;
79 foreach (var child
in modsList.Content.Children)
81 if (child == itemFrame) { toggleTickbox =
true; }
82 else if (child.UserData is ModsListChildType.Header) { toggleTickbox =
false; }
83 else if (toggleTickbox)
86 if (tb is
null) {
continue; }
95 onUpdate: (f, component) =>
98 bool shouldBeSelected = true;
99 bool toggleTickbox = false;
100 foreach (var child in modsList.Content.Children)
102 if (child == itemFrame) { toggleTickbox = true; }
103 else if (child.UserData is ModsListChildType.Header) { toggleTickbox = false; }
104 else if (toggleTickbox)
106 var tb = child.GetAnyChild<GUITickBox>();
107 if (tb is null) { continue; }
111 shouldBeSelected = false;
116 tickBox.Selected = shouldBeSelected;
120 void addTickbox(
string dir,
string name,
bool ticked)
125 CanBeFocused =
false,
126 UserData = ModsListChildType.Entry
132 pathTickboxMap.Add(dir, tickbox);
135 bool firstHeader =
true;
139 if (firstHeader) { firstHeader =
false;
return; }
143 if (subs.FilePaths.Any())
146 addHeader(TextManager.Get(
"WorkshopLabelSubmarines"));
147 foreach (var sub
in subs.FilePaths)
149 var subName = Path.GetFileNameWithoutExtension(sub);
150 addTickbox(sub, subName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(subName)));
154 if (itemAssemblies.FilePaths.Any())
157 addHeader(TextManager.Get(
"ItemAssemblies"));
158 foreach (var itemAssembly
in itemAssemblies.FilePaths)
160 var assemblyName = Path.GetFileNameWithoutExtension(itemAssembly);
161 addTickbox(itemAssembly, assemblyName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(assemblyName)));
168 addHeader(TextManager.Get(
"SubscribedMods"));
169 foreach (var mod
in mods.Mods)
171 addTickbox(mod.Dir, mod.Name,
172 ticked: !(mod.Item is { } item && ContentPackageManager.LocalPackages.Any(p =>
173 p.UgcId.TryUnwrap(out var ugcId)
175 && workshopId.
Value == item.Id)));
185 buttons: closable ?
new[] { TextManager.Get(
"Close") } : Array.Empty<
LocalizedString>());
192 msgBox.Buttons[0].OnClicked = (b, o) =>
194 TaskPool.Add(
"TransferMods", TransferMods(pathTickboxMap), t2 =>
196 if (t2.Exception !=
null)
198 DebugConsole.ThrowError(
"There was an error transferring mods", t2.Exception.GetInnermost());
200 ContentPackageManager.LocalPackages.Refresh();
201 if (t2.TryGetResult(out
string[]? modsToEnable))
203 var newRegular = ContentPackageManager.EnabledPackages.Regular.ToList();
204 newRegular.AddRange(ContentPackageManager.LocalPackages.Regular
205 .Where(r => modsToEnable.Contains(r.Dir.CleanUpPathCrossPlatform(correctFilenameCase:
false))));
206 newRegular = newRegular.Distinct().ToList();
207 ContentPackageManager.EnabledPackages.SetRegular(newRegular);
209 createSubMsgBox(TextManager.Get(
"Ugc.TransferComplete"), closable:
true);
212 createSubMsgBox(TextManager.Get(
"Ugc.Transferring"), closable:
false);
218 private struct OldSubs
220 public readonly IReadOnlyList<string> FilePaths;
222 public OldSubs(IReadOnlyList<string> filePaths)
224 FilePaths = filePaths;
228 private struct OldItemAssemblies
230 public readonly IReadOnlyList<string> FilePaths;
232 public OldItemAssemblies(IReadOnlyList<string> filePaths)
234 FilePaths = filePaths;
238 private struct OldMods
240 public readonly IReadOnlyList<(
string Dir,
string Name, Steamworks.Ugc.Item?
Item, DateTime InstallTime)> Mods;
242 public OldMods(IReadOnlyList<(
string Dir,
string Name, Steamworks.Ugc.Item?
Item, DateTime InstallTime)> mods)
248 private const string oldSubsPath =
"Submarines";
249 private const string oldModsPath =
"Mods";
250 private const string oldItemAssembliesPath =
"ItemAssemblies";
252 private static async Task<(OldSubs Subs, OldItemAssemblies ItemAssemblies, OldMods Mods)> DetermineItemsToTransition()
254 string[] subs = Array.Empty<
string>();
255 string[] itemAssemblies = Array.Empty<
string>();
256 List<(
string Dir,
string Name, Steamworks.Ugc.Item?
Item, DateTime InstallTime)> mods
257 =
new List<(
string Dir,
string Name, Steamworks.Ugc.Item?
Item, DateTime InstallTime)>();
258 if (FolderShouldBeTransitioned(oldModsPath))
260 string[] getFiles(
string path,
string pattern)
261 => Directory.Exists(path)
262 ? Directory.GetFiles(path, pattern, System.IO.SearchOption.TopDirectoryOnly)
263 : Array.Empty<
string>();
265 subs = getFiles(oldSubsPath,
"*.sub");
266 itemAssemblies = getFiles(oldItemAssembliesPath,
"*.xml");
268 string[] allOldMods = Directory.GetDirectories(oldModsPath,
"*");
270 var publishedItems = await SteamManager.Workshop.GetPublishedItems();
271 foreach (var modDir
in allOldMods)
274 if (fileList?.Root is
null) {
continue; }
276 var oldId = fileList.Root.GetAttributeUInt64(
"steamworkshopid", 0);
277 var updateTime = File.GetLastWriteTime(modDir).ToUniversalTime();
278 var oldName = fileList.Root.GetAttributeString(
"name",
"");
280 var item = oldId != 0 ? publishedItems.FirstOrNull(it => it.Id == oldId) :
null;
281 if (oldId == 0 || item.HasValue)
283 mods.Add((modDir, oldName, item, updateTime));
290 return (
new OldSubs(subs),
new OldItemAssemblies(itemAssemblies),
new OldMods(mods));
293 private static bool FolderShouldBeTransitioned(
string folderName)
295 return Directory.Exists(folderName)
296 && !File.Exists(Path.Combine(folderName, readmeName));
299 private static async Task<string[]> TransferMods(Dictionary<string, GUITickBox> pathTickboxMap)
302 WriteReadme(oldModsPath);
303 var modsToEnable = (await Task.WhenAll(pathTickboxMap.Select(TransferMod))).OfType<string>().ToArray();
307 private static Task<string?> TransferMod(KeyValuePair<string, GUITickBox> kvp)
308 => TransferMod(kvp.Key, kvp.Value);
310 private static async Task<string?> TransferMod(
string path,
GUITickBox tickbox)
312 if (!tickbox.
Selected) {
return null; }
313 string dirName = Path.GetFileNameWithoutExtension(path);
319 if (!Directory.Exists(destPath)) {
break; }
323 bool isSub = path.StartsWith(oldSubsPath, StringComparison.OrdinalIgnoreCase);
324 bool isItemAssembly = path.StartsWith(oldItemAssembliesPath, StringComparison.OrdinalIgnoreCase);
325 if (isSub || isItemAssembly)
339 if (doc?.Root !=
null)
354 Directory.CreateDirectory(destPath);
355 File.Copy(path, Path.Combine(destPath, $
"{dirName}.{(isSub ? "sub
" : "xml
")}"));
358 return destPath.CleanUpPathCrossPlatform(correctFilenameCase:
false);
363 await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath, SteamManager.Workshop.ShouldCorrectPaths.Yes);
369 private static void WriteReadme(
string folderName)
371 if (!Directory.Exists(folderName)) {
return; }
372 File.WriteAllText(path: Path.Combine(folderName, readmeName),
373 contents:
"This folder is no longer used by Barotrauma;\n" +
374 "your mods and submarines should have been transferred\n" +
375 "to LocalMods. If they are not being found, delete this\n" +
376 "readme and relaunch the game.", encoding: Encoding.UTF8);
const string FileListFileName
const string DefaultModVersion
const string LocalModsDir
GUIComponent that can be used to render custom content on the UI
List< GUIButton > Buttons
static File FromPath(string path, Type type)
Prefer FromPath<T> when possible, this just exists for cases where the type can only be decided at ru...
void Save(string path, bool catchUnauthorizedAccessExceptions=true)
static Type DetermineSubFileType(SubmarineType type)
static XDocument OpenFile(string file)