2021-04-25 22:59:36 -05:00
|
|
|
using OwlCore.AbstractUI.Models;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2022-03-01 14:14:11 -06:00
|
|
|
using System.Linq;
|
2021-04-25 22:59:36 -05:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using ZuneModCore.Mods;
|
|
|
|
|
|
|
|
|
|
namespace ZuneModCore
|
|
|
|
|
{
|
|
|
|
|
public abstract class Mod
|
|
|
|
|
{
|
|
|
|
|
public abstract string Id { get; }
|
|
|
|
|
|
|
|
|
|
public abstract string Title { get; }
|
|
|
|
|
|
|
|
|
|
public abstract string Description { get; }
|
|
|
|
|
|
2021-04-26 18:05:19 -05:00
|
|
|
public abstract string Author { get; }
|
|
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
public delegate void StatusChangedHandler(Mod sender, bool status);
|
|
|
|
|
public event StatusChangedHandler? StatusChanged;
|
|
|
|
|
|
2021-11-10 21:21:25 -06:00
|
|
|
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
|
2021-08-05 20:45:26 -05:00
|
|
|
|
2021-04-25 22:59:36 -05:00
|
|
|
public virtual Task Init() => Task.CompletedTask;
|
|
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
protected abstract Task<string?> ApplyCore();
|
2021-04-25 22:59:36 -05:00
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
protected abstract Task<string?> ResetCore();
|
|
|
|
|
|
|
|
|
|
protected virtual void FireStatusChanged(bool status) => StatusChanged?.Invoke(this, status);
|
2021-04-25 22:59:36 -05:00
|
|
|
|
2021-11-10 21:21:25 -06:00
|
|
|
private AbstractUICollection? _OptionsUI;
|
|
|
|
|
public AbstractUICollection? OptionsUI
|
2021-08-05 20:45:26 -05:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_OptionsUI == null)
|
|
|
|
|
_OptionsUI = GetDefaultOptionsUI();
|
|
|
|
|
return _OptionsUI;
|
|
|
|
|
}
|
|
|
|
|
set => _OptionsUI = value;
|
|
|
|
|
}
|
2021-04-25 22:59:36 -05:00
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a directory that mods can use to store mod-specific information,
|
|
|
|
|
/// such as backups for resetting.
|
|
|
|
|
/// </summary>
|
2021-04-26 04:28:43 -05:00
|
|
|
public string StorageDirectory
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2022-03-01 14:14:11 -06:00
|
|
|
string dir = Path.Combine(ModManager.CoreStorageDir, Id);
|
2021-04-26 04:28:43 -05:00
|
|
|
// Create the directory just in case the consumer assumes the folder exists already
|
|
|
|
|
Directory.CreateDirectory(dir);
|
|
|
|
|
return dir;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-25 22:59:36 -05:00
|
|
|
|
2022-03-01 14:14:11 -06:00
|
|
|
public abstract IReadOnlyList<ModDependency>? DependentMods { get; }
|
|
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Applies this mod, without any of its dependencies.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The error message if not successful.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public async Task<string?> Apply()
|
|
|
|
|
{
|
|
|
|
|
string? error = await ApplyCore();
|
|
|
|
|
if (error == null)
|
|
|
|
|
{
|
|
|
|
|
ModManager.MarkApplied(Id);
|
|
|
|
|
FireStatusChanged(true);
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-01 14:14:11 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes any missing depedencies.
|
|
|
|
|
/// </summary>
|
2022-03-03 14:26:05 -06:00
|
|
|
public Task InitDependencies() => ForAllDependenciesAsync(async mod =>
|
2022-03-01 14:14:11 -06:00
|
|
|
{
|
2022-03-03 14:26:05 -06:00
|
|
|
bool isDepApplied = mod.CheckApplied();
|
2022-03-01 14:14:11 -06:00
|
|
|
if (!isDepApplied)
|
|
|
|
|
await mod.Init();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Applies any missing depedencies.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Make sure to call <see cref="InitDependencies"/> first.
|
|
|
|
|
/// </remarks>
|
2022-03-03 14:26:05 -06:00
|
|
|
public Task ApplyDependencies() => ForAllDependenciesAsync(async mod =>
|
2022-03-01 14:14:11 -06:00
|
|
|
{
|
2022-03-03 14:26:05 -06:00
|
|
|
bool isDepApplied = mod.CheckApplied();
|
2022-03-01 14:14:11 -06:00
|
|
|
if (!isDepApplied)
|
|
|
|
|
await mod.Apply();
|
|
|
|
|
});
|
|
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Applies this mod and all its dependencies.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The error message if not successful.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public async Task<string?> ApplyWithDependencies()
|
|
|
|
|
{
|
|
|
|
|
await ApplyDependencies();
|
|
|
|
|
return await Apply();
|
|
|
|
|
}
|
2022-03-01 14:14:11 -06:00
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Resets this mod, without resetting any dependencies.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<string?> Reset()
|
|
|
|
|
{
|
|
|
|
|
string? error = await ResetCore();
|
|
|
|
|
if (error == null)
|
|
|
|
|
{
|
|
|
|
|
ModManager.MarkReset(Id);
|
|
|
|
|
FireStatusChanged(false);
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CheckApplied() => ModManager.CheckStatus(Id);
|
|
|
|
|
|
|
|
|
|
public bool CheckDependenciesApplied() => TrueForAllDependencies(mod => mod.CheckApplied());
|
|
|
|
|
|
|
|
|
|
private void ForAllDependencies(Action<Mod> action)
|
|
|
|
|
{
|
|
|
|
|
// Check if there are any dependencies
|
|
|
|
|
if (DependentMods == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var dep in DependentMods)
|
|
|
|
|
{
|
|
|
|
|
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
|
|
|
|
if (depMod != null)
|
|
|
|
|
action(depMod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task ForAllDependenciesAsync(Func<Mod, Task> asyncAction)
|
2022-03-01 14:14:11 -06:00
|
|
|
{
|
|
|
|
|
// Check if there are any dependencies
|
|
|
|
|
if (DependentMods == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var dep in DependentMods)
|
|
|
|
|
{
|
|
|
|
|
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
|
|
|
|
if (depMod != null)
|
|
|
|
|
await asyncAction(depMod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 14:26:05 -06:00
|
|
|
private bool TrueForAllDependencies(Func<Mod, bool> action)
|
2022-03-01 14:14:11 -06:00
|
|
|
{
|
|
|
|
|
// Check if there are any dependencies
|
|
|
|
|
if (DependentMods == null)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
foreach (var dep in DependentMods)
|
|
|
|
|
{
|
|
|
|
|
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
|
|
|
|
if (depMod != null)
|
2022-03-03 14:26:05 -06:00
|
|
|
if (!action(depMod))
|
2022-03-01 14:14:11 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString() => Title;
|
2021-04-25 22:59:36 -05:00
|
|
|
}
|
|
|
|
|
}
|