forked from Reloaded-Project/Reloaded-II
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added: Library code to create 'mod-packs' for downloading multiple mods.
- Loading branch information
Showing
11 changed files
with
467 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
source/Reloaded.Mod.Loader.Tests/Update/Pack/PackagePackerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using Reloaded.Mod.Loader.Update.Packs; | ||
using Routes = Reloaded.Mod.Loader.Update.Packs.Routes; | ||
|
||
namespace Reloaded.Mod.Loader.Tests.Update.Pack; | ||
|
||
public class PackagePackerTests | ||
{ | ||
public string ImageFilePath = Path.Combine(Assets.PackAssetsFolder, "heroes-preview.jxl"); | ||
public const string ModId = "test.mod"; | ||
|
||
[Fact] | ||
public void Pack_Unpack_WithoutImages() | ||
{ | ||
// Arrange | ||
var builder = BuildBaselinePack(); | ||
|
||
// Act | ||
var result = builder.Build(out var pack); | ||
result.Position = 0; | ||
var reader = new ReloadedPackReader(result); | ||
var packCopy = reader.GetPack(); | ||
|
||
// Assert | ||
Assert.Equal(pack, packCopy); | ||
} | ||
|
||
[Fact] | ||
public void Pack_Unpack_WithMainImage() | ||
{ | ||
// Arrange | ||
var builder = BuildBaselinePack(); | ||
var imageBytes = File.ReadAllBytes(ImageFilePath); | ||
using var fs = new FileStream(ImageFilePath, FileMode.Open); | ||
builder.AddImage(fs, Path.GetExtension(ImageFilePath)!, "Sample Image"); | ||
|
||
// Act | ||
var result = builder.Build(out var pack); | ||
result.Position = 0; | ||
var reader = new ReloadedPackReader(result); | ||
|
||
// Assert | ||
var newImage = reader.GetImage(reader.Pack.ImageFiles[0].Path); | ||
Assert.Equal(imageBytes, newImage); | ||
} | ||
|
||
[Fact] | ||
public void Pack_Unpack_WithMod() | ||
{ | ||
// Arrange | ||
var builder = BuildBaselinePack(); | ||
AddSampleMod(builder); | ||
|
||
// Act | ||
var result = builder.Build(out var pack); | ||
result.Position = 0; | ||
var reader = new ReloadedPackReader(result); | ||
var packCopy = reader.GetPack(); | ||
|
||
// Assert | ||
Assert.Equal(pack, packCopy); | ||
} | ||
|
||
[Fact] | ||
public void Pack_Unpack_WithModImage() | ||
{ | ||
// Arrange | ||
var builder = BuildBaselinePack(); | ||
var modBuilder = AddSampleMod(builder); | ||
var imageBytes = File.ReadAllBytes(ImageFilePath); | ||
using var fs = new FileStream(ImageFilePath, FileMode.Open); | ||
modBuilder.AddImage(fs, Path.GetExtension(ImageFilePath)!, "Sample Image"); | ||
|
||
// Act | ||
var result = builder.Build(out var pack); | ||
result.Position = 0; | ||
var reader = new ReloadedPackReader(result); | ||
|
||
// Assert | ||
var newImage = reader.GetImage(reader.Pack.Items[0].ImageFiles[0].Path); | ||
Assert.Equal(imageBytes, newImage); | ||
} | ||
|
||
private ReloadedPackBuilder BuildBaselinePack() | ||
{ | ||
var builder = new ReloadedPackBuilder(); | ||
builder.SetName("Sample Package for Testing"); | ||
builder.SetReadme("## Sample Readme"); | ||
return builder; | ||
} | ||
|
||
private ReloadedPackItemBuilder AddSampleMod(ReloadedPackBuilder builder) | ||
{ | ||
var modBuilder = builder.AddModItem(ModId); | ||
modBuilder.SetName("Sample Mod"); | ||
modBuilder.SetReadme("Sample Readme"); | ||
return modBuilder; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
namespace Reloaded.Mod.Loader.Update.Packs; | ||
|
||
/// <summary> | ||
/// Represents a pack that contains a collection of mods to be installed. | ||
/// </summary> | ||
[Equals(DoNotAddEqualityOperators = true)] | ||
public class ReloadedPack : IConfig | ||
{ | ||
/// <summary> | ||
/// Name of the pack. | ||
/// </summary> | ||
public string Name { get; set; } = String.Empty; | ||
|
||
/// <summary> | ||
/// Readme for the pack, in markdown format. | ||
/// </summary> | ||
public string Readme { get; set; } = String.Empty; | ||
|
||
/// <summary> | ||
/// List of preview image files belonging to the pack. | ||
/// May be PNG, JPEG & JXL (JPEG XL). | ||
/// </summary> | ||
public List<ReloadedPackImage> ImageFiles { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// Items associated with this pack. | ||
/// </summary> | ||
public List<ReloadedPackItem> Items { get; set; } = new(); | ||
} |
97 changes: 97 additions & 0 deletions
97
source/Reloaded.Mod.Loader.Update/Packs/ReloadedPackBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
namespace Reloaded.Mod.Loader.Update.Packs; | ||
|
||
/// <summary> | ||
/// Class that can be used to help build Reloaded packs. | ||
/// Uses a fluent API. | ||
/// </summary> | ||
public class ReloadedPackBuilder | ||
{ | ||
private string _name; | ||
private string _readme; | ||
private List<(Stream stream, string name, string cap)> _images = new(); | ||
private int _imageIndex = 0; | ||
private List<ReloadedPackItemBuilder> _itemBuilders = new(); | ||
|
||
/// <summary> | ||
/// Sets the name for this Reloaded pack. | ||
/// </summary> | ||
public ReloadedPackBuilder SetName(string name) | ||
{ | ||
_name = name; | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the markdown readme for this Reloaded pack. | ||
/// </summary> | ||
public ReloadedPackBuilder SetReadme(string readme) | ||
{ | ||
_readme = readme; | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Adds an image to the pack. | ||
/// </summary> | ||
/// <param name="imageData">Stream containing the image data.</param> | ||
/// <param name="extension">Extension of the image, with the dot.</param> | ||
/// <param name="caption">Caption for the image.</param> | ||
public ReloadedPackBuilder AddImage(Stream imageData, string extension, string? caption) | ||
{ | ||
_images.Add((imageData, $"Main_{_imageIndex++}{extension}", caption)); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Adds a mod to this package, returning a builder that allows customization of this mod. | ||
/// </summary> | ||
/// <param name="modId">ID of the mod.</param> | ||
/// <returns>Builder for the individual pack item.</returns> | ||
public ReloadedPackItemBuilder AddModItem(string modId) | ||
{ | ||
var itemBuilder = new ReloadedPackItemBuilder(modId); | ||
_itemBuilders.Add(itemBuilder); | ||
return itemBuilder; | ||
} | ||
|
||
/// <summary> | ||
/// Creates the Reloaded package. | ||
/// </summary> | ||
/// <returns>Stream containing the final ZIP archive.</returns> | ||
public MemoryStream Build(out ReloadedPack package) | ||
{ | ||
// Note: zips by standard should use forward slash. | ||
var memStream = new MemoryStream(); | ||
using var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true); | ||
|
||
// Create Main Package | ||
package = new ReloadedPack(); | ||
package.Name = _name; | ||
package.Readme = _readme; | ||
foreach (var image in _images) | ||
{ | ||
package.ImageFiles.Add(new ReloadedPackImage() | ||
{ | ||
Path = image.name, | ||
Caption = image.cap, | ||
}); | ||
|
||
// We use no compression assuming images are already compressed well. | ||
var entry = archive.CreateEntry(Routes.GetImagePath(image.name), CompressionLevel.NoCompression); | ||
using var entryStream = entry.Open(); | ||
image.stream.CopyTo(entryStream); | ||
} | ||
|
||
// Create Mod Entries | ||
foreach (var builder in _itemBuilders) | ||
builder.Build(package, archive); | ||
|
||
// But compress the config | ||
var configEntry = archive.CreateEntry(Routes.Config, CompressionLevel.Optimal); | ||
using var configEntryStream = configEntry.Open(); | ||
var writer = new Utf8JsonWriter(configEntryStream); | ||
JsonSerializer.Serialize(writer, package); | ||
|
||
return memStream; | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
source/Reloaded.Mod.Loader.Update/Packs/ReloadedPackImage.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
namespace Reloaded.Mod.Loader.Update.Packs; | ||
|
||
/// <summary> | ||
/// Represents a singular image stored inside the Reloaded package. | ||
/// </summary> | ||
[Equals(DoNotAddEqualityOperators = true)] | ||
public struct ReloadedPackImage | ||
{ | ||
// TODO: [NET7] Restore constructor with guarantee of non-null username. Right now we can't because it breaks built-in System.Text.Json. This is fixed in newer versions. | ||
|
||
/// <summary> | ||
/// Path of the image inside the archive. | ||
/// </summary> | ||
public string Path { get; set; } | ||
|
||
/// <summary> | ||
/// Caption of the image. | ||
/// </summary> | ||
public string Caption { get; set; } | ||
} |
36 changes: 36 additions & 0 deletions
36
source/Reloaded.Mod.Loader.Update/Packs/ReloadedPackItem.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace Reloaded.Mod.Loader.Update.Packs; | ||
|
||
/// <summary> | ||
/// Represents an individual item contained in a Reloaded pack. | ||
/// </summary> | ||
[Equals(DoNotAddEqualityOperators = true)] | ||
public class ReloadedPackItem | ||
{ | ||
/// <summary> | ||
/// Name of the mod represented by this item. | ||
/// [Shown in UI] | ||
/// </summary> | ||
public string Name { get; set; } = String.Empty; | ||
|
||
/// <summary> | ||
/// ID of the mod contained. | ||
/// [Shown in UI] | ||
/// </summary> | ||
public string ModId { get; set; } = String.Empty; | ||
|
||
/// <summary> | ||
/// Readme for this mod, in markdown format. | ||
/// </summary> | ||
public string Readme { get; set; } = String.Empty; | ||
|
||
/// <summary> | ||
/// List of preview image files belonging to this item. | ||
/// May be PNG, JPEG & JXL (JPEG XL). | ||
/// </summary> | ||
public List<ReloadedPackImage> ImageFiles { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// Copied from <see cref="ModConfig.PluginData"/>. Contains info on how to download the mod. | ||
/// </summary> | ||
public Dictionary<string, object> PluginData { get; set; } = new(); | ||
} |
Oops, something went wrong.