9b35f6b23f
* Update bug-reports.yml * Fix modifyChatMessage hook * Add LuaCsSetup.Lua back for compatibility * Fix Game.AssignOnExecute having command arguments be passed as varargs instead of a table * Actually use the PackageId const everywhere we need to refer to our content package * Load languages files even if the package is disabled * Fix Hook.Remove not being implemented properly * - Changed event aliases to be case insensitive. * - Fixed assembly logging style. - Fixed double logging during execution. * Fix garbage network data being read by the game when reading LuaCs network messages * PackageId -> PackageName * Added caching toggle to PluginManagementService * Fix LuaCs initializing too late for singleplayer campaigns and rework the C# prompt to only show when enabling mods/joining server * Oops, fix NRE crash * Fix hide username in logs config not doing anything * Fix Cs prompt showing up more than one between rounds * Fix server host being prompted twice with the C# popup * Ignore our workshop packages from the game's dependency thing since it doesn't really make sense * Load console commands after executing and possible fix for the not console command permitted * Added fallback friendly name resolution for ModConfig assembly contents. * Register Voronoi2 stuff * Added configinfo null check to SettingBase.cs * Add safety check so this stops crashing when we look at it the wrong way * Fixed "Folder" attribute files not being found. * Keep the LuaCsConfig class laying around for compatibility, not sure anywhere in our code base (and shouldn't be) * Added fallback compilation for UseInternalsAwareAssembly if the publicized script compilation fails. * Added legacy overload of AddCommand for mod compat. * Added LoggerService to Lua env. Made ILoggerService compliant with LuaCsLogger API. * Changed csharp script compilation algorithm to be best effort. * Added "RunUnrestricted" mode for lua scripts that need to run outside of sandbox. * - Fixed networking sync vars failing to sync initially. - Fixed lua failing to differentiate overloads ISettingBase. * Add alias for human.CPRSuccess and human.CPRFailed * - Fixed up the settings menu. - Made SettingEntry throw an error if "Value" attribute is not found in XML. - Fixed saved values for settings sometimes not reloading after disabling and re-enabling a package. * Fix LuaCs net messages received during connection initialization to be read incorrectly, happened because we would reset the BitPosition in our harmony patch which would cause the message to be read incorrectly later * Allow reloadlua to force the state to running * New icon for settings and make the top left text more user friendly * Fix client.packages hook sending normal packages * Fixed OnUpdate() not passing in deltaTime instead of totalTime. * Missing diffs frombb21a09244* Added networking tests for configs. * Added missing diffs forf61f852a25. * Some tweaks to the text * Remove missing Value error, it should just use the default value if it's not specified * Fix UseInternalAccessName * Always purge cashes for plugin content on unloading. * Fix texture not multiple of 4 * v1.12.7.0 (Spring Update 2026 Hotfix 1) --------- Co-authored-by: Joonas Rikkonen <poe.regalis@gmail.com> Co-authored-by: Evil Factory <36804725+evilfactory@users.noreply.github.com> Co-authored-by: MapleWheels <njainanan@hotmail.com>
613 lines
25 KiB
C#
613 lines
25 KiB
C#
#nullable enable
|
|
using Barotrauma.Extensions;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using Barotrauma.IO;
|
|
using Barotrauma.LuaCs.Events;
|
|
using Barotrauma.Steam;
|
|
using Microsoft.Xna.Framework;
|
|
using OneOf.Types;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
public static partial class ContentPackageManager
|
|
{
|
|
public const string CopyIndicatorFileName = ".copying";
|
|
public const string VanillaFileList = "Content/ContentPackages/Vanilla.xml";
|
|
|
|
public const string CorePackageElementName = "corepackage";
|
|
public const string RegularPackagesElementName = "regularpackages";
|
|
public const string RegularPackagesSubElementName = "package";
|
|
|
|
public static bool ModsEnabled => GameMain.VanillaContent == null || EnabledPackages.All.Any(p => p.HasMultiplayerSyncedContent && p != GameMain.VanillaContent);
|
|
|
|
public static class EnabledPackages
|
|
{
|
|
public static CorePackage? Core { get; private set; } = null;
|
|
|
|
private static readonly List<RegularPackage> regular = new List<RegularPackage>();
|
|
public static IReadOnlyList<RegularPackage> Regular => regular;
|
|
|
|
/// <summary>
|
|
/// Combined hash of the currently enabled packages. Can be used to check if the enabled mods or their load order has changed.
|
|
/// </summary>
|
|
public static Md5Hash MergedHash { get; private set; } = Md5Hash.Blank;
|
|
|
|
public static IEnumerable<ContentPackage> All =>
|
|
Core != null
|
|
? (Core as ContentPackage).ToEnumerable().CollectionConcat(Regular)
|
|
: Enumerable.Empty<ContentPackage>();
|
|
|
|
public static class BackupPackages
|
|
{
|
|
public static CorePackage? Core;
|
|
public static ImmutableArray<RegularPackage>? Regular;
|
|
}
|
|
|
|
public static void SetCore(CorePackage newCore)
|
|
{
|
|
SetCoreEnumerable(newCore).Consume();
|
|
}
|
|
|
|
public static IEnumerable<LoadProgress> SetCoreEnumerable(CorePackage newCore)
|
|
{
|
|
var oldCore = Core;
|
|
if (newCore == oldCore) { yield break; }
|
|
if (newCore.FatalLoadErrors.Any()) { yield break; }
|
|
Core?.UnloadContent();
|
|
Core = newCore;
|
|
foreach (var p in newCore.LoadContentEnumerable()) { yield return p; }
|
|
SortContent();
|
|
yield return LoadProgress.Progress(1.0f);
|
|
}
|
|
|
|
public static void ReloadCore()
|
|
{
|
|
if (Core == null) { return; }
|
|
ReloadPackage(Core);
|
|
}
|
|
|
|
public static void ReloadPackage(ContentPackage p)
|
|
{
|
|
p.UnloadContent();
|
|
p.LoadContent();
|
|
SortContent();
|
|
}
|
|
|
|
public static void EnableRegular(RegularPackage p)
|
|
{
|
|
if (regular.Contains(p)) { return; }
|
|
|
|
var newRegular = regular.ToList();
|
|
newRegular.Add(p);
|
|
SetRegular(newRegular);
|
|
}
|
|
|
|
public static void SetRegular(IReadOnlyList<RegularPackage> newRegular)
|
|
{
|
|
SetRegularEnumerable(newRegular).Consume();
|
|
}
|
|
|
|
public static IEnumerable<LoadProgress> SetRegularEnumerable(IReadOnlyList<RegularPackage> inNewRegular)
|
|
{
|
|
if (ReferenceEquals(inNewRegular, regular)) { yield break; }
|
|
if (inNewRegular.SequenceEqual(regular)) { yield break; }
|
|
ThrowIfDuplicates(inNewRegular);
|
|
var newRegular = inNewRegular
|
|
// Refuse to enable packages with load errors
|
|
// so people are forced away from broken mods
|
|
.Where(r => !r.FatalLoadErrors.Any())
|
|
.ToList();
|
|
IEnumerable<RegularPackage> toUnload = regular.Where(r => !newRegular.Contains(r));
|
|
RegularPackage[] toLoad = newRegular.Where(r => !regular.Contains(r)).ToArray();
|
|
toUnload.ForEach(r => r.UnloadContent());
|
|
|
|
Range<float> loadingRange = new Range<float>(0.0f, 1.0f);
|
|
|
|
for (int i = 0; i < toLoad.Length; i++)
|
|
{
|
|
var package = toLoad[i];
|
|
loadingRange = new Range<float>(i / (float)toLoad.Length, (i + 1) / (float)toLoad.Length);
|
|
foreach (var progress in package.LoadContentEnumerable())
|
|
{
|
|
if (progress.Result.IsFailure)
|
|
{
|
|
//If an exception was thrown while loading this package, refuse to add it to the list of enabled packages
|
|
newRegular.Remove(package);
|
|
break;
|
|
}
|
|
yield return progress.Transform(loadingRange);
|
|
}
|
|
}
|
|
regular.Clear(); regular.AddRange(newRegular);
|
|
SortContent();
|
|
yield return LoadProgress.Progress(1.0f);
|
|
}
|
|
|
|
public static void ThrowIfDuplicates(IEnumerable<ContentPackage> pkgs)
|
|
{
|
|
var contentPackages = pkgs as IList<ContentPackage> ?? pkgs.ToArray();
|
|
foreach (ContentPackage cp in contentPackages)
|
|
{
|
|
if (contentPackages.AtLeast(2, cp2 => cp == cp2))
|
|
{
|
|
throw new InvalidOperationException($"There are duplicates in the list of selected content packages (\"{cp.Name}\", hash: {cp.Hash?.ShortRepresentation ?? "none"})");
|
|
}
|
|
}
|
|
}
|
|
|
|
private class TypeComparer<T> : IEqualityComparer<T>
|
|
{
|
|
public bool Equals([AllowNull] T x, [AllowNull] T y)
|
|
{
|
|
if (x is null || y is null)
|
|
{
|
|
return x is null == y is null;
|
|
}
|
|
return x.GetType() == y.GetType();
|
|
}
|
|
|
|
public int GetHashCode([DisallowNull] T obj)
|
|
{
|
|
return obj.GetType().GetHashCode();
|
|
}
|
|
}
|
|
|
|
private static void SortContent()
|
|
{
|
|
ThrowIfDuplicates(All);
|
|
All
|
|
.SelectMany(r => r.Files)
|
|
.Distinct(new TypeComparer<ContentFile>())
|
|
.ForEach(f => f.Sort());
|
|
MergedHash = Md5Hash.MergeHashes(All.Select(cp => cp.Hash));
|
|
TextManager.IncrementLanguageVersion();
|
|
}
|
|
|
|
public static int IndexOf(ContentPackage contentPackage)
|
|
{
|
|
if (contentPackage is CorePackage core)
|
|
{
|
|
if (core == Core) { return 0; }
|
|
return -1;
|
|
}
|
|
else if (contentPackage is RegularPackage reg)
|
|
{
|
|
return Regular.IndexOf(reg) + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static void DisableMods(IReadOnlyCollection<ContentPackage> mods)
|
|
{
|
|
if (Core != null && mods.Contains(Core))
|
|
{
|
|
var newCore = ContentPackageManager.CorePackages.FirstOrDefault(p => !mods.Contains(p));
|
|
if (newCore != null)
|
|
{
|
|
SetCore(newCore);
|
|
}
|
|
}
|
|
SetRegular(Regular.Where(p => !mods.Contains(p)).ToArray());
|
|
}
|
|
|
|
public static void DisableRemovedMods()
|
|
{
|
|
if (Core != null && !ContentPackageManager.CorePackages.Contains(Core))
|
|
{
|
|
SetCore(ContentPackageManager.CorePackages.First());
|
|
}
|
|
SetRegular(Regular.Where(p => ContentPackageManager.RegularPackages.Contains(p)).ToArray());
|
|
}
|
|
|
|
public static void RefreshUpdatedMods()
|
|
{
|
|
if (Core != null && !ContentPackageManager.CorePackages.Contains(Core))
|
|
{
|
|
SetCore(ContentPackageManager.WorkshopPackages.Core.FirstOrDefault(p => p.UgcId == Core.UgcId) ??
|
|
ContentPackageManager.CorePackages.First());
|
|
}
|
|
|
|
List<RegularPackage> newRegular = new List<RegularPackage>();
|
|
foreach (var p in Regular)
|
|
{
|
|
if (ContentPackageManager.RegularPackages.Contains(p))
|
|
{
|
|
newRegular.Add(p);
|
|
}
|
|
else if (ContentPackageManager.WorkshopPackages.Regular.FirstOrDefault(p2
|
|
=> p2.UgcId == p.UgcId) is { } newP)
|
|
{
|
|
newRegular.Add(newP);
|
|
}
|
|
}
|
|
SetRegular(newRegular);
|
|
}
|
|
|
|
public static void BackUp()
|
|
{
|
|
if (BackupPackages.Core != null || BackupPackages.Regular != null)
|
|
{
|
|
throw new InvalidOperationException("Tried to back up enabled packages multiple times");
|
|
}
|
|
|
|
BackupPackages.Core = Core;
|
|
BackupPackages.Regular = Regular.ToImmutableArray();
|
|
}
|
|
|
|
public static void Restore()
|
|
{
|
|
if (BackupPackages.Core == null || BackupPackages.Regular == null)
|
|
{
|
|
DebugConsole.AddWarning("Tried to restore enabled packages multiple times/without performing a backup");
|
|
return;
|
|
}
|
|
|
|
SetCore(BackupPackages.Core);
|
|
SetRegular(BackupPackages.Regular);
|
|
|
|
BackupPackages.Core = null;
|
|
BackupPackages.Regular = null;
|
|
}
|
|
}
|
|
|
|
public sealed partial class PackageSource : ICollection<ContentPackage>
|
|
{
|
|
private readonly Predicate<string>? skipPredicate;
|
|
private readonly Action<string, Exception>? onLoadFail;
|
|
|
|
public PackageSource(string dir, Predicate<string>? skipPredicate, Action<string, Exception>? onLoadFail)
|
|
{
|
|
this.skipPredicate = skipPredicate;
|
|
this.onLoadFail = onLoadFail;
|
|
directory = dir;
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
public void SwapPackage(ContentPackage oldPackage, ContentPackage newPackage)
|
|
{
|
|
bool contains = false;
|
|
if (oldPackage is CorePackage oldCore && corePackages.Contains(oldCore))
|
|
{
|
|
corePackages.Remove(oldCore);
|
|
contains = true;
|
|
}
|
|
else if (oldPackage is RegularPackage oldRegular && regularPackages.Contains(oldRegular))
|
|
{
|
|
regularPackages.Remove(oldRegular);
|
|
contains = true;
|
|
}
|
|
|
|
if (contains)
|
|
{
|
|
if (newPackage is CorePackage newCore)
|
|
{
|
|
corePackages.Add(newCore);
|
|
}
|
|
else if (newPackage is RegularPackage newRegular)
|
|
{
|
|
regularPackages.Add(newRegular);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
//remove packages that have been deleted from the directory
|
|
corePackages.RemoveWhere(p => !File.Exists(p.Path));
|
|
regularPackages.RemoveWhere(p => !File.Exists(p.Path));
|
|
|
|
//load packages that have been added to the directory
|
|
var subDirs = Directory.GetDirectories(directory);
|
|
foreach (string subDir in subDirs)
|
|
{
|
|
var fileListPath = Path.Combine(subDir, ContentPackage.FileListFileName).CleanUpPathCrossPlatform();
|
|
if (this.Any(p => p.Path.Equals(fileListPath, StringComparison.OrdinalIgnoreCase))) { continue; }
|
|
|
|
if (!File.Exists(fileListPath)) { continue; }
|
|
if (skipPredicate?.Invoke(fileListPath) is true) { continue; }
|
|
|
|
var result = ContentPackage.TryLoad(fileListPath);
|
|
if (!result.TryUnwrapSuccess(out var newPackage))
|
|
{
|
|
onLoadFail?.Invoke(
|
|
fileListPath,
|
|
result.TryUnwrapFailure(out var exception) ? exception : throw new UnreachableCodeException());
|
|
continue;
|
|
}
|
|
|
|
switch (newPackage)
|
|
{
|
|
case CorePackage corePackage:
|
|
corePackages.Add(corePackage);
|
|
break;
|
|
case RegularPackage regularPackage:
|
|
regularPackages.Add(regularPackage);
|
|
break;
|
|
}
|
|
|
|
Debug.WriteLine($"Loaded \"{newPackage.Name}\"");
|
|
}
|
|
}
|
|
|
|
private readonly string directory;
|
|
private readonly HashSet<RegularPackage> regularPackages = new HashSet<RegularPackage>();
|
|
public IEnumerable<RegularPackage> Regular => regularPackages;
|
|
|
|
private readonly HashSet<CorePackage> corePackages = new HashSet<CorePackage>();
|
|
public IEnumerable<CorePackage> Core => corePackages;
|
|
|
|
public IEnumerator<ContentPackage> GetEnumerator()
|
|
{
|
|
foreach (var core in Core) { yield return core; }
|
|
foreach (var regular in Regular) { yield return regular; }
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
void ICollection<ContentPackage>.Add(ContentPackage item) => throw new InvalidOperationException();
|
|
|
|
void ICollection<ContentPackage>.Clear() => throw new InvalidOperationException();
|
|
|
|
public bool Contains(ContentPackage item)
|
|
=> item switch
|
|
{
|
|
CorePackage core => corePackages.Contains(core),
|
|
RegularPackage regular => this.regularPackages.Contains(regular),
|
|
_ => throw new ArgumentException($"Expected regular or core package, got {item.GetType().Name}")
|
|
};
|
|
|
|
void ICollection<ContentPackage>.CopyTo(ContentPackage[] array, int arrayIndex)
|
|
{
|
|
foreach (var package in corePackages)
|
|
{
|
|
array[arrayIndex] = package;
|
|
arrayIndex++;
|
|
}
|
|
|
|
foreach (var package in regularPackages)
|
|
{
|
|
array[arrayIndex] = package;
|
|
arrayIndex++;
|
|
}
|
|
}
|
|
|
|
bool ICollection<ContentPackage>.Remove(ContentPackage item) => throw new InvalidOperationException();
|
|
|
|
public int Count => corePackages.Count + regularPackages.Count;
|
|
public bool IsReadOnly => true;
|
|
}
|
|
|
|
public static readonly PackageSource LocalPackages
|
|
= new PackageSource(
|
|
ContentPackage.LocalModsDir,
|
|
skipPredicate: null,
|
|
onLoadFail: null);
|
|
public static readonly PackageSource WorkshopPackages = new PackageSource(
|
|
ContentPackage.WorkshopModsDir,
|
|
skipPredicate: SteamManager.Workshop.IsInstallingToPath,
|
|
onLoadFail: (fileListPath, exception) =>
|
|
{
|
|
// Delete Workshop mods that fail to load to
|
|
// force a reinstall on next launch if necessary
|
|
Directory.TryDelete(Path.GetDirectoryName(fileListPath)!);
|
|
});
|
|
|
|
public static CorePackage? VanillaCorePackage { get; private set; } = null;
|
|
|
|
public static IEnumerable<CorePackage> CorePackages
|
|
=> (VanillaCorePackage is null
|
|
? Enumerable.Empty<CorePackage>()
|
|
: VanillaCorePackage.ToEnumerable())
|
|
.CollectionConcat(LocalPackages.Core.CollectionConcat(WorkshopPackages.Core));
|
|
|
|
public static IEnumerable<RegularPackage> RegularPackages
|
|
=> LocalPackages.Regular.CollectionConcat(WorkshopPackages.Regular);
|
|
|
|
public static IEnumerable<ContentPackage> AllPackages
|
|
=> VanillaCorePackage.ToEnumerable().CollectionConcat(LocalPackages).CollectionConcat(WorkshopPackages)
|
|
.OfType<ContentPackage>();
|
|
|
|
public static void UpdateContentPackageList()
|
|
{
|
|
LocalPackages.Refresh();
|
|
WorkshopPackages.Refresh();
|
|
EnabledPackages.DisableRemovedMods();
|
|
}
|
|
|
|
public static Result<ContentPackage, Exception> ReloadContentPackage(ContentPackage p)
|
|
{
|
|
var result = ContentPackage.TryLoad(p.Path);
|
|
|
|
if (result.TryUnwrapSuccess(out var newPackage))
|
|
{
|
|
switch (newPackage)
|
|
{
|
|
case CorePackage core:
|
|
{
|
|
if (EnabledPackages.Core == p) { EnabledPackages.SetCore(core); }
|
|
|
|
break;
|
|
}
|
|
case RegularPackage regular:
|
|
{
|
|
int index = EnabledPackages.Regular.IndexOf(p);
|
|
if (index >= 0)
|
|
{
|
|
var newRegular = EnabledPackages.Regular.ToArray();
|
|
newRegular[index] = regular;
|
|
EnabledPackages.SetRegular(newRegular);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalPackages.SwapPackage(p, newPackage);
|
|
WorkshopPackages.SwapPackage(p, newPackage);
|
|
}
|
|
EnabledPackages.DisableRemovedMods();
|
|
return result;
|
|
}
|
|
|
|
public readonly record struct LoadProgress(Result<float, LoadProgress.Error> Result)
|
|
{
|
|
public readonly record struct Error(
|
|
Either<ImmutableArray<string>, Exception> ErrorsOrException)
|
|
{
|
|
public Error(IEnumerable<string> errorMessages) : this(ErrorsOrException: errorMessages.ToImmutableArray()) { }
|
|
public Error(Exception exception) : this(ErrorsOrException: exception) { }
|
|
}
|
|
|
|
public static LoadProgress Failure(Exception exception)
|
|
=> new LoadProgress(
|
|
Result<float, Error>.Failure(new Error(exception)));
|
|
|
|
public static LoadProgress Failure(IEnumerable<string> errorMessages)
|
|
=> new LoadProgress(
|
|
Result<float, Error>.Failure(new Error(errorMessages)));
|
|
|
|
public static LoadProgress Progress(float value)
|
|
=> new LoadProgress(
|
|
Result<float, Error>.Success(value));
|
|
|
|
public LoadProgress Transform(Range<float> range)
|
|
=> Result.TryUnwrapSuccess(out var value)
|
|
? new LoadProgress(
|
|
Result<float, Error>.Success(
|
|
MathHelper.Lerp(range.Start, range.End, value)))
|
|
: this;
|
|
}
|
|
|
|
public static void LoadVanillaFileList()
|
|
{
|
|
VanillaCorePackage = new CorePackage(XDocument.Load(VanillaFileList), VanillaFileList);
|
|
foreach (ContentPackage.LoadError error in VanillaCorePackage.FatalLoadErrors)
|
|
{
|
|
DebugConsole.ThrowError(error.ToString());
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<LoadProgress> Init()
|
|
{
|
|
Range<float> loadingRange = new Range<float>(0.0f, 1.0f);
|
|
|
|
SteamManager.Workshop.DeleteFailedCopies();
|
|
UpdateContentPackageList();
|
|
|
|
if (VanillaCorePackage is null) { LoadVanillaFileList(); }
|
|
|
|
SteamManager.Workshop.DeleteUnsubscribedMods();
|
|
|
|
CorePackage enabledCorePackage = VanillaCorePackage!;
|
|
List<RegularPackage> enabledRegularPackages = new List<RegularPackage>();
|
|
|
|
#if CLIENT
|
|
TaskPool.AddWithResult("EnqueueWorkshopUpdates", EnqueueWorkshopUpdates(), t => { });
|
|
#else
|
|
#warning TODO: implement Workshop updates for servers at some point
|
|
#endif
|
|
|
|
var contentPackagesElement = XMLExtensions.TryLoadXml(GameSettings.PlayerConfigPath)?.Root
|
|
?.GetChildElement("ContentPackages");
|
|
if (contentPackagesElement != null)
|
|
{
|
|
T? findPackage<T>(IEnumerable<T> packages, XElement? elem) where T : ContentPackage
|
|
{
|
|
if (elem is null) { return null; }
|
|
string name = elem.GetAttributeString("name", "");
|
|
string path = elem.GetAttributeStringUnrestricted("path", "").CleanUpPathCrossPlatform(correctFilenameCase: false);
|
|
return
|
|
packages.FirstOrDefault(p => p.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
|
|
?? packages.FirstOrDefault(p => p.NameMatches(name));
|
|
}
|
|
|
|
var corePackageElement = contentPackagesElement.GetChildElement(CorePackageElementName);
|
|
if (corePackageElement == null)
|
|
{
|
|
DebugConsole.AddWarning($"No core package selected. Switching to the \"{enabledCorePackage.Name}\" package.");
|
|
}
|
|
else
|
|
{
|
|
var configEnabledCorePackage = findPackage(CorePackages, corePackageElement);
|
|
if (configEnabledCorePackage == null)
|
|
{
|
|
string packageStr = corePackageElement.GetAttributeString("name", null) ?? corePackageElement.GetAttributeStringUnrestricted("path", "UNKNOWN");
|
|
DebugConsole.ThrowError($"Could not find the selected core package \"{packageStr}\". Switching to the \"{enabledCorePackage.Name}\" package.");
|
|
}
|
|
else
|
|
{
|
|
enabledCorePackage = configEnabledCorePackage;
|
|
}
|
|
}
|
|
|
|
var regularPackagesElement = contentPackagesElement.GetChildElement(RegularPackagesElementName);
|
|
if (regularPackagesElement != null)
|
|
{
|
|
XElement[] regularPackageElements = regularPackagesElement.GetChildElements(RegularPackagesSubElementName).ToArray();
|
|
for (int i = 0; i < regularPackageElements.Length; i++)
|
|
{
|
|
var regularPackage = findPackage(RegularPackages, regularPackageElements[i]);
|
|
if (regularPackage != null) { enabledRegularPackages.Add(regularPackage); }
|
|
}
|
|
}
|
|
}
|
|
|
|
int pkgCount = 1 + enabledRegularPackages.Count; //core + regular
|
|
|
|
loadingRange = new Range<float>(0.01f, 0.01f + (0.99f / pkgCount));
|
|
foreach (var p in EnabledPackages.SetCoreEnumerable(enabledCorePackage))
|
|
{
|
|
yield return p.Transform(loadingRange);
|
|
}
|
|
|
|
loadingRange = new Range<float>(0.01f + (0.99f / pkgCount), 1.0f);
|
|
foreach (var p in EnabledPackages.SetRegularEnumerable(enabledRegularPackages))
|
|
{
|
|
yield return p.Transform(loadingRange);
|
|
}
|
|
|
|
yield return LoadProgress.Progress(1.0f);
|
|
}
|
|
|
|
public static void CheckMissingDependencies()
|
|
{
|
|
foreach (var enabledPackage in EnabledPackages.All)
|
|
{
|
|
enabledPackage.ClearMissingDependencies();
|
|
enabledPackage.TryFetchUgcChildren((Steamworks.Data.PublishedFileId[]? children) =>
|
|
{
|
|
if (children == null) { return; }
|
|
var missingChildren = children
|
|
.Where(childUgcItemId =>
|
|
EnabledPackages.All.None(package =>
|
|
package.UgcId.TryUnwrap(out var ugcId) && ugcId is SteamWorkshopId workshopId && workshopId.Value == childUgcItemId.Value));
|
|
foreach (var missingChild in missingChildren)
|
|
{
|
|
if (missingChild.ToString() == "2559634234" ||
|
|
missingChild.ToString() == "2795927223")
|
|
{
|
|
continue;
|
|
}
|
|
enabledPackage.AddMissingDependency(missingChild);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public static void LogEnabledRegularPackageErrors()
|
|
{
|
|
foreach (var p in EnabledPackages.Regular)
|
|
{
|
|
p.LogErrors();
|
|
}
|
|
}
|
|
}
|
|
}
|