InstalledGameVersionCatalog

This commit is contained in:
2024-12-31 17:55:30 +05:00
parent 2c780afea8
commit e5deb3f3d4
24 changed files with 477 additions and 341 deletions

View File

@@ -3,14 +3,13 @@
namespace Mlaumcherb.Client.Avalonia.классы;
public class VersionCatalogProps
public class GameVersionCatalogProps
{
[JsonRequired] public required string name { get; init; }
[JsonRequired] public required string url { get; init; }
public override string ToString() => name;
public async Task<GameVersionCatalog> GetVersionCatalogAsync()
{
return await NetworkHelper.DownloadStringAndDeserialize<GameVersionCatalog>(url);

View File

@@ -1,83 +0,0 @@
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.холопы;
namespace Mlaumcherb.Client.Avalonia.классы;
public class GameVersionProps : IComparable<GameVersionProps>, IEquatable<GameVersionProps>
{
public string Name { get; }
public IOPath LocalDescriptorPath { get; }
public RemoteVersionDescriptorProps? RemoteProps { get; }
private bool _isDownloaded;
public bool IsDownloaded
{
get => _isDownloaded;
set
{
if(_isDownloaded != value)
{
_isDownloaded = value;
StatusChanged?.Invoke(value);
}
}
}
public event Action<bool>? StatusChanged;
private GameVersionProps(string name, RemoteVersionDescriptorProps? remoteProps)
{
Name = name;
RemoteProps = remoteProps;
LocalDescriptorPath = PathHelper.GetVersionDescriptorPath(name);
IsDownloaded = File.Exists(PathHelper.GetVersionJarFilePath(name));
}
public GameVersionProps(string name)
: this(name, null)
{ }
public GameVersionProps(RemoteVersionDescriptorProps remoteProps)
: this(remoteProps.id, remoteProps)
{ }
public void DeleteFiles()
{
IsDownloaded = false;
LauncherApp.Logger.LogInfo(Name, "Deleting files...");
var verdir = PathHelper.GetVersionDir(Name);
if(Directory.Exists(verdir))
Directory.Delete(verdir);
LauncherApp.Logger.LogInfo(Name, "Files deleted");
}
public override string ToString() => Name;
public override int GetHashCode() => Name.GetHashCode();
public int CompareTo(GameVersionProps? other)
{
if (ReferenceEquals(this, other)) return 0;
if (other is null) return 1;
if (Version.TryParse(Name, out var version1) && Version.TryParse(other.Name, out var version2))
{
return version1.CompareTo(version2);
}
return String.Compare(Name, other.Name, StringComparison.InvariantCulture);
}
public bool Equals(GameVersionProps? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
return Name == other.Name;
}
public override bool Equals(object? obj)
{
if (obj is GameVersionProps other) return Equals(other);
if (ReferenceEquals(this, obj)) return true;
return false;
}
}

View File

@@ -0,0 +1,134 @@
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.сеть;
using Mlaumcherb.Client.Avalonia.холопы;
using Newtonsoft.Json.Linq;
namespace Mlaumcherb.Client.Avalonia.классы;
public class InstalledGameVersionProps : IComparable<InstalledGameVersionProps>, IEquatable<InstalledGameVersionProps>
{
[JsonRequired] public string Id { get; }
public GameVersionCatalogProps? CatalogProps { get; set; }
public RemoteVersionDescriptorProps? DescriptorProps { get; set; }
private bool _isInstalled;
[JsonIgnore]
public bool IsInstalled
{
get => _isInstalled;
set
{
_isInstalled = value;
StatusChanged?.Invoke(value);
}
}
[JsonIgnore] public IOPath DescriptorPath { get; }
public event Action<bool>? StatusChanged;
public InstalledGameVersionProps(string id, GameVersionCatalogProps? catalogProps, RemoteVersionDescriptorProps? descriptorProps)
{
this.Id = id;
this.CatalogProps = catalogProps;
this.DescriptorProps = descriptorProps;
DescriptorPath = PathHelper.GetVersionDescriptorPath(id);
IsInstalled = File.Exists(PathHelper.GetVersionJarFilePath(id));
}
public async Task<GameVersion> LoadDescriptor(bool checkForUpdates)
{
// check if DescriptorProps had been changed on on remote version catalog server
if (checkForUpdates && CatalogProps is not null && DescriptorProps is not null)
{
var remoteCatalog = await NetworkHelper.DownloadStringAndDeserialize<GameVersionCatalog>(CatalogProps.url);
DescriptorProps = remoteCatalog.versions.Find(remoteProps => remoteProps.id == Id);
if(DescriptorProps is null)
LauncherApp.Logger.LogWarn(nameof(InstalledGameVersionProps),
$"Game version '{Id}' was deleted from remote catalog '{CatalogProps.name}'."
+ "It is kept locally, but can't be updated anymore.");
}
// Descriptor is downloaded here
if (HashHelper.CheckFileSHA1(DescriptorPath, DescriptorProps?.sha1, checkForUpdates))
{
if (DescriptorProps is null)
throw new NullReferenceException(
$"can't download game version descriptor '{Id}', because RemoteDescriptor is null");
// download descriptor
await NetworkHelper.DownloadFile(DescriptorProps.url, DescriptorPath);
}
string descriptorText = File.ReadAllText(DescriptorPath);
JObject descriptorRaw = JObject.Parse(descriptorText);
// Descriptors can inherit from other descriptors.
// For example, 1.12.2-forge-14.23.5.2860 inherits from 1.12.2
if (descriptorRaw.TryGetValue("inheritsFrom", out var v))
{
string parentDescriptorId = v.Value<string>()
?? throw new Exception("inheritsFrom is null");
LauncherApp.Logger.LogInfo(Id, $"merging descriptor '{parentDescriptorId}' with '{Id}'");
IOPath parentDescriptorPath = PathHelper.GetVersionDescriptorPath(parentDescriptorId);
if (!File.Exists(parentDescriptorPath))
throw new Exception($"Версия '{Id} требует установить версию '{parentDescriptorId}'");
string parentDescriptorText = File.ReadAllText(parentDescriptorPath);
JObject parentDescriptorRaw = JObject.Parse(parentDescriptorText);
parentDescriptorRaw.Merge(descriptorRaw,
new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat });
descriptorRaw = parentDescriptorRaw;
// removing dependency
descriptorRaw.Remove("inheritsFrom");
File.WriteAllText(DescriptorPath, descriptorRaw.ToString());
}
var descriptor = descriptorRaw.ToObject<GameVersionDescriptor>()
?? throw new Exception($"can't parse descriptor file '{DescriptorPath}'");
return new GameVersion(this, descriptor);
}
public void Delete()
{
if(!IsInstalled)
return;
LauncherApp.Logger.LogInfo(Id, "Deleting files...");
var verdir = PathHelper.GetVersionDir(Id);
if(Directory.Exists(verdir))
Directory.Delete(verdir);
LauncherApp.Logger.LogInfo(Id, "Files deleted");
IsInstalled = false;
}
public override string ToString() => Id;
public override int GetHashCode() => Id.GetHashCode();
public int CompareTo(InstalledGameVersionProps? other)
{
if (ReferenceEquals(this, other))
return 0;
if (other is null)
return 1;
return String.Compare(Id, other.Id, StringComparison.InvariantCulture);
}
public bool Equals(InstalledGameVersionProps? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public override bool Equals(object? obj)
{
if (obj is InstalledGameVersionProps other) return Equals(other);
if (ReferenceEquals(this, obj)) return true;
return false;
}
}

View File

@@ -0,0 +1,77 @@
using System.Linq;
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.холопы;
namespace Mlaumcherb.Client.Avalonia.классы;
public class InstalledVersionCatalog
{
/// version_id, props
[JsonRequired] public Dictionary<string, InstalledGameVersionProps> versions { get; set; } = new();
private IComparer<InstalledGameVersionProps> reverseComparer = new ReverseComparer<InstalledGameVersionProps>();
public static InstalledVersionCatalog Load()
{
InstalledVersionCatalog catalog;
var installedCatalogPath = PathHelper.GetInstalledVersionCatalogPath();
if (!File.Exists(installedCatalogPath))
{
LauncherApp.Logger.LogDebug(nameof(InstalledVersionCatalog), "creating new catalog");
catalog = new InstalledVersionCatalog();
}
else
{
LauncherApp.Logger.LogDebug(nameof(InstalledVersionCatalog), "loading catalog from file");
catalog = JsonConvert.DeserializeObject<InstalledVersionCatalog>(
File.ReadAllText(installedCatalogPath))
?? throw new NullReferenceException($"can't deserialize {installedCatalogPath}");
}
catalog.CheckInstalledVersions();
catalog.Save();
return catalog;
}
public void Save()
{
var installedCatalogPath = PathHelper.GetInstalledVersionCatalogPath();
LauncherApp.Logger.LogDebug(nameof(InstalledVersionCatalog), "saving catalog to file");
var text = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(installedCatalogPath, text);
LauncherApp.Logger.LogDebug(nameof(InstalledVersionCatalog), "catalog has been saved");
}
/// <summary>
/// Check if any versions were installed or deleted manually.
/// </summary>
public void CheckInstalledVersions()
{
var versionsUpdated = new List<InstalledGameVersionProps>();
Directory.Create(PathHelper.GetVersionsDir());
foreach (var subdir in Directory.GetDirectories(PathHelper.GetVersionsDir()))
{
string id = subdir.LastName().ToString();
if (!File.Exists(PathHelper.GetVersionDescriptorPath(id)))
throw new Exception(
$"Can't find version descriptor file in directory '{subdir}'. Rename it as directory name.");
if (versions.TryGetValue(id, out var foundProps))
{
versionsUpdated.Add(foundProps);
}
else
{
versionsUpdated.Add(new InstalledGameVersionProps(id, null, null));
}
}
// reverse sort
versionsUpdated.Sort(reverseComparer);
// create dictionary from list
versions = new Dictionary<string, InstalledGameVersionProps>(
versionsUpdated.Select(props =>
new KeyValuePair<string, InstalledGameVersionProps>(props.Id, props)
));
}
}

View File

@@ -6,7 +6,7 @@ public class ModpackFilesManifest
{
public class FileProps
{
[JsonRequired] public string sha1 { get; set; }
[JsonRequired] public string sha1 { get; set; } = "";
public bool allow_edit { get; set; } = false;
}

View File

@@ -11,7 +11,7 @@ public class MyModpackV1
// 1
[JsonRequired] public int format_version { get; set; }
[JsonRequired] public string name { get; set; }
[JsonRequired] public string name { get; set; } = "";
// zip archive with all files
[JsonRequired] public Artifact zip { get; set; } = null!;
// ModpackFilesManifest