83 Commits

Author SHA1 Message Date
2edbcf72c7 fixes for client cli publish script 2025-11-09 14:18:50 +01:00
19b9a64721 fix missing version 2025-11-09 14:17:31 +01:00
ec6830e636 also add the new file !!!! 2025-11-09 14:16:06 +01:00
9a5c70581b build also cli 2025-11-09 14:15:53 +01:00
a6175b3874 ui(client) also publish win-arm64, osx-x64, osx-arm64 2025-11-09 14:04:24 +01:00
27ce41e0d1 ui(client): disable flatpak build 2025-11-09 13:59:30 +01:00
574a8d5ec2 final fix 2025-11-09 13:58:43 +01:00
d570d29c92 update nuget packages 2025-11-09 13:43:20 +01:00
562ce27341 ui(client): optimize installation key caching & show invalid key hint 2025-11-09 08:40:18 +01:00
463c2bbf2b ui(client): use Margin instead of RowSpacing and ColumnSpacing
-> fixes visible spacing of invisible rows
2025-11-09 08:11:27 +01:00
145a5771a1 ignore .idea 2025-11-08 22:51:12 +01:00
3adfcda472 update 2025-11-08 22:50:45 +01:00
078d5c0661 version improvement 2025-11-08 22:49:46 +01:00
06db3289c0 fix home share permission 2025-11-08 18:40:51 +01:00
a573f4fb89 fix release build 2025-11-08 18:36:45 +01:00
091d820332 fix build 2025-11-08 18:29:04 +01:00
1d44f93a92 enable network & home directory access for flatpak 2025-11-08 18:17:13 +01:00
3de764b833 fixes 2025-11-08 17:31:17 +01:00
3e6b70bc4c fixes 2025-11-08 16:36:40 +01:00
065d915d46 improvements & deployment 2025-11-08 15:46:56 +01:00
11b76ee73b add license 2025-11-08 11:33:29 +00:00
1c070891f1 ui(client): formatting & code improvements 2025-11-07 15:22:28 +01:00
4baf571713 Merge branch 'feat/migrate-client-to-avalonia' into 'master'
Feat/migrate client to avalonia

See merge request litw-refined/minecraft-modpack-updater!2
2025-11-07 13:58:36 +00:00
Pascal
b899d2ee7d ui(client): improvements 2025-11-07 10:45:55 +01:00
Pascal
05b94a3189 migrate to Pilz.Features 2025-11-06 12:20:03 +01:00
Pascal
6a6fd7efe6 ui(client): translation 2025-11-06 11:42:08 +01:00
Pascal
a89145071d ui(client): migrate to AvaloniaUI 2025-11-06 07:42:44 +01:00
554304c801 version bump, again 2025-08-23 09:28:24 +02:00
785c4ce41d version bump 2025-08-23 08:41:02 +02:00
bd7ce5bbe7 use full version string for {version} 2025-08-23 08:40:09 +02:00
8ef522bc6c 1.9.3 2025-08-20 08:32:27 +02:00
582752c987 seperated cli & some work for options 2025-08-20 08:17:33 +02:00
b434ddf480 preparation for dynamic options 2025-07-31 14:51:30 +02:00
8300f571a3 update nuget packages 2025-07-31 14:43:27 +02:00
e86fe2ee17 another fix 2025-07-04 22:27:37 +02:00
531c2a60bf fix option shit 2025-07-04 22:13:47 +02:00
b1ac3cd73c uff, enough for now 2025-06-30 18:55:30 +02:00
6674872848 more release shit 2025-06-30 17:31:42 +02:00
03f27214d6 update client app symbol 2025-06-30 14:44:07 +02:00
d8c798fe09 remove linux update url 2025-06-30 11:49:24 +02:00
70786fc169 version bump 2025-06-30 11:45:38 +02:00
aed70d4f3f update publish profile 2025-06-30 05:53:38 +02:00
45ef76e647 ui: hide installation key if no extras key can be used 2025-06-30 05:50:41 +02:00
433613e43d ui: migrate client back to WinForms
-> let's use wine for comatibility again
-> easier to maintain
-> easier to build dynamic options with
2025-06-29 20:03:02 +02:00
6e8b4f0a9d manager: tree view & action set infos editor panel 2025-06-29 14:19:58 +02:00
87cb9b8b37 update nuget packages 2025-06-29 13:40:36 +02:00
Pascal
77a93b585a manager: add proper support for options 2025-06-27 11:24:53 +02:00
Pascal
d0e3d2fa61 finalize model for option sets 2025-06-27 09:29:57 +02:00
Pascal
f5596ab0ba manager: allow parent path annotations
- Allows unlimited amount of `..\` or `../` at the start of DestPath string.
- Each of then will go back one folder.
- Similar to how Linux works or Visual Studio project styles.
- This allows us to install/update lwjgl3ify in PrismLauncher.
2025-06-27 09:10:18 +02:00
Pascal
09c1ac8b08 ui: simplify workspaces menu 2025-06-27 08:34:03 +02:00
Pascal
4e69acaf04 ui: row context menu fine tuning 2025-06-27 08:24:20 +02:00
Pascal
55db801c4c update nuget packages 2025-06-27 08:22:08 +02:00
Pascal
cbbb546f30 ui: fixed adding new row beeing empty 2025-06-27 08:09:26 +02:00
04848d4622 repair manager project 2025-06-26 18:53:00 +02:00
d85a41c158 update url 2025-06-19 12:54:11 +02:00
798961ea22 update publish profiles 2025-06-19 12:25:33 +02:00
bf0037cf79 hide console on windows only 2025-06-19 12:15:31 +02:00
c52c4059a0 version bump 2025-06-19 11:41:52 +02:00
03fefa33fe update AppUpdate to support linux updates 2025-06-19 11:41:04 +02:00
30b1832cd0 ui: migrate client to gtk 2025-06-17 09:40:10 +02:00
0aa6ed98c6 add install options (WIP) 2025-06-16 15:34:15 +02:00
c1616fecf0 fix modlist plugin not loading 2025-06-16 15:33:37 +02:00
a5eb9fad43 fix some null reference exceptions 2025-04-23 15:40:01 +02:00
df21d8180d version bump 2025-04-23 14:46:04 +02:00
f19974599f add repair mode 2025-04-23 14:45:22 +02:00
e14dedc924 support first placeholder & overwriting version 2025-04-23 13:38:51 +02:00
0285892f20 fixes 2025-04-21 20:55:55 +02:00
01fc5606cb add reftag commandline argument 2025-04-21 20:50:16 +02:00
70805083ba version bump 2025-04-21 20:48:19 +02:00
96cd9bcaac add support for {ref} placeholder 2025-04-21 20:43:09 +02:00
986257c0a4 v1.6.5 2025-04-21 18:40:19 +02:00
b11ad06287 add support for version placeholder in download links 2025-04-21 18:39:47 +02:00
d68dd09ad2 add option to generate markdown table 2025-04-03 06:19:47 +02:00
1b3070493c order by name 2025-01-28 20:46:47 +01:00
1c9c50778a add the missing files from the last commit (wtf?) 2025-01-28 20:43:27 +01:00
33a209a01a add generate modlist feature 2025-01-28 20:38:27 +01:00
2ce73ad032 list only relevant modrinth versions 2025-01-26 09:19:07 +01:00
ecb7ae0d1a fail silently if url can not be resolved 2024-12-22 18:48:30 +01:00
40380d088f version bump 2024-12-07 18:11:48 +01:00
50cbf081e1 fix endless update 2024-12-07 18:11:04 +01:00
3b79335e85 more logging 2024-12-05 07:22:01 +01:00
3e1ab72162 update deps & enable console logging 2024-12-05 07:06:06 +01:00
a83d109f24 fix first install includes ALL updates 2024-12-05 06:56:45 +01:00
97 changed files with 2399 additions and 3500 deletions

2
.gitignore vendored
View File

@@ -349,3 +349,5 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
*.json
.idea/

16
Directory.Build.props Normal file
View File

@@ -0,0 +1,16 @@
<Project>
<PropertyGroup>
<PackageProjectUrl>https://git.pilzinsel64.de/litw-refined/minecraft-modpack-updater</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<GenerateSerializationAssemblies>False</GenerateSerializationAssemblies>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
</PropertyGroup>
</Project>

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Pilzinsel64
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
<PackageReference Include="Pilz.IO" Version="2.1.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ModpackUpdater.Apps.Client.Gui.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace ModpackUpdater.Apps.Client.Gui;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainForm();
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -1,16 +1,16 @@
using Newtonsoft.Json;
using Pilz.Configuration;
namespace ModpackUpdater.Apps.Client;
namespace ModpackUpdater.Apps.Client.Gui;
public class AppConfig : IChildSettings, ISettingsIdentifier
public class AppConfig : ISettingsNode, ISettingsIdentifier
{
public static string Identifier => "pilz.appconfig";
public string LastMinecraftProfilePath { get; set; }
public string? LastMinecraftProfilePath { get; set; }
[JsonIgnore, Obsolete]
public string ConfigFilePath { get; private set; }
public string? ConfigFilePath { get; private set; }
[JsonProperty("ConfigFilePath"), Obsolete]
private string ConfigFilePathLegacy
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="480" height="480" viewBox="0 0 480 480" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip_path_1">
<rect width="480" height="480" />
</clipPath>
</defs>
<g clip-path="url(#clip_path_1)">
<path d="M360 40L180 40L140 0C140 0 40 0 40 0C17.91 0 0 17.91 0 40C0 40 0 120 0 120L400 120C400 120 400 80 400 80C400 57.91 382.09 40 360 40C360 40 360 40 360 40Z" fill="#FFA000" transform="translate(20 100)" />
<g transform="translate(53 -7)">
<path d="M160.042 63.3334L0 7.91667L13.9167 213.75L160.042 300.833L278.333 205.833L292.25 0L160.042 63.3334Z" fill="#5D4037" fill-rule="evenodd" transform="translate(20.875 63.333)" />
<path d="M0 0L13.9167 205.834L160.042 292.917L160.042 55.4167L0 0Z" fill="#8D6E63" fill-rule="evenodd" transform="translate(20.875 71.25)" />
<path d="M13.9167 112.084L13.9167 79.1667L27.8333 72.2554L27.8333 95L41.75 87.0833L41.75 118.75L62.625 109.028L62.625 55.4167L76.5417 48.6954L76.5417 87.0833L90.4583 80.5679L90.4583 63.3333L104.375 55.4167L104.375 35.3637L118.292 28.6979L118.292 47.5L129.425 41.1667L132.208 0L0 63.3333L0 118.75L13.9167 112.084Z" fill="#43A047" fill-rule="evenodd" transform="translate(180.917 63.333)" />
<path d="M0 47.5L139.167 0L292.25 39.5833L160.042 102.917L0 47.5Z" fill="#B2FF59" fill-rule="evenodd" transform="translate(20.875 23.75)" />
<path d="M0 0L2.78333 40.6442L13.9167 44.3888L13.9167 34.9363L27.8333 39.5833L27.8333 65.0513L48.7083 72.2792L48.7083 34.6196L62.625 39.5833L62.625 77.1004L76.5417 81.9217L76.5417 58.9713L90.4583 63.7925L90.4583 86.743L104.375 91.5563L104.375 82.1671L118.292 87.0834L118.292 74.6067L132.208 79.1667L132.208 101.199L160.042 110.833L160.042 55.4167L0 0Z" fill="#66BB6A" fill-rule="evenodd" transform="translate(20.875 71.25)" />
</g>
<path d="M360 0C360 0 40 0 40 0C17.91 0 0 15.1595 0 33.8571C0 33.8571 0 203.143 0 203.143C0 221.84 17.91 237 40 237C40 237 360 237 360 237C382.09 237 400 221.84 400 203.143C400 203.143 400 33.8571 400 33.8571C400 15.1595 382.09 0 360 0C360 0 360 0 360 0Z" fill="#FFCA28" transform="translate(20 183)" />
<path d="M70 80L0 0L140 0L70 80Z" fill="#1565C0" transform="translate(150 380)" />
<path d="M0 0L60 0L60 111.25L0 111.25L0 0Z" fill="#1565C0" transform="translate(190 280)" />
<path d="M30 15C30 23.28 23.29 30 15 30C6.71002 30 0 23.28 0 15C0 6.72 6.71002 0 15 0C23.29 0 30 6.72 30 15C30 15 30 15 30 15Z" fill="#4A148C" transform="translate(430 60)" />
<path d="M125 100C72.62 100 30 57.38 30 5C30 5 30 0 30 0L0 0L0 10C0 10 0.25 10 0.25 10C2.84 74.93 55.08 127.16 120 129.75C120 129.75 120 130 120 130L130 130L130 100L125 100C125 100 125 100 125 100Z" fill="#9C27B0" transform="translate(330 60)" />
<path d="M75 50C50.19 50 30 29.81 30 5C30 5 30 0 30 0L0 0L0 10C0 10 0.25 10 0.25 10C2.73004 47.36 32.63 77.27 70 79.75C70 79.75 70 80 70 80L80 80L80 50L75 50C75 50 75 50 75 50Z" fill="#7B1FA2" transform="translate(380 60)" />
<path d="M175 150C95.05 150 30 84.95 30 5C30 5 30 0 30 0L0 0L0 10C0 10 0.25 10 0.25 10C2.88998 102.5 77.51 177.11 170 179.75C170 179.75 170 180 170 180L180 180L180 150L175 150C175 150 175 150 175 150Z" fill="#BA68C8" transform="translate(280 60)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -8,7 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace ModpackUpdater.My.Resources {
namespace ModpackUpdater.Apps.Client.Gui {
using System;
@@ -39,7 +39,7 @@ namespace ModpackUpdater.My.Resources {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.FiledialogFilters", typeof(FiledialogFilters).Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.FiledialogFilters", typeof(FiledialogFilters).Assembly);
resourceMan = temp;
}
return resourceMan;

View File

@@ -0,0 +1,162 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ModpackUpdater.Apps.Client.Gui.LangRes {
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class GeneralLangRes {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal GeneralLangRes() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
public static string AnUpdateIsAvailable {
get {
return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture);
}
}
public static string CheckForUpdates {
get {
return ResourceManager.GetString("CheckForUpdates", resourceCulture);
}
}
public static string CheckingForUpdates {
get {
return ResourceManager.GetString("CheckingForUpdates", resourceCulture);
}
}
public static string ConfigIncompleteOrNotLoaded {
get {
return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture);
}
}
public static string DownloadProgramUpdate {
get {
return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture);
}
}
public static string ErrorOnUpdateCheckOrUpdating {
get {
return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture);
}
}
public static string EverythingIsRightAndUpToDate {
get {
return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture);
}
}
public static string Install {
get {
return ResourceManager.GetString("Install", resourceCulture);
}
}
public static string InstallationKey {
get {
return ResourceManager.GetString("InstallationKey", resourceCulture);
}
}
public static string Installing {
get {
return ResourceManager.GetString("Installing", resourceCulture);
}
}
public static string MinecraftProfile {
get {
return ResourceManager.GetString("MinecraftProfile", resourceCulture);
}
}
public static string MinecraftProfileFolderSeemsInvalid {
get {
return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture);
}
}
public static string ModpackConfigUrl {
get {
return ResourceManager.GetString("ModpackConfigUrl", resourceCulture);
}
}
public static string Repair {
get {
return ResourceManager.GetString("Repair", resourceCulture);
}
}
public static string Select {
get {
return ResourceManager.GetString("Select", resourceCulture);
}
}
public static string SelectMinecraftProfileFolder {
get {
return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture);
}
}
public static string Status {
get {
return ResourceManager.GetString("Status", resourceCulture);
}
}
public static string UpdateServerInMaintenance {
get {
return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture);
}
}
public static string InstallationKeyNotValid {
get {
return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture);
}
}
}
}

View File

@@ -117,37 +117,61 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="MsgBox_UpdateAvailable" xml:space="preserve">
<value>A new version of this program is available. If you confirm, the update will be installed automatically. It takes just a few seconds. Continue?</value>
</data>
<data name="MsgBox_UpdateAvailable_Title" xml:space="preserve">
<value>New program version available</value>
</data>
<data name="StatusTest_EverythingOk" xml:space="preserve">
<value>Everything is right and up-to-date.</value>
</data>
<data name="StatusText_CheckingForUpdates" xml:space="preserve">
<value>Checking for Updates...</value>
</data>
<data name="StatusText_ConfigIncompleteOrNotLoaded" xml:space="preserve">
<value>Config incomplete or not loaded!</value>
</data>
<data name="StatusText_ErrorWhileUpdateCheckOrUpdate" xml:space="preserve">
<value>Error on update check or while updating!</value>
</data>
<data name="StatusText_Installing" xml:space="preserve">
<value>Installing...</value>
</data>
<data name="StatusText_InstallingAppUpdate" xml:space="preserve">
<value>Downloading program update...</value>
</data>
<data name="StatusText_Maintenance" xml:space="preserve">
<value>The update servers are in maintenance.</value>
</data>
<data name="StatusText_MinecraftProfileWarning" xml:space="preserve">
<value>Minecraft profile folder seems to be not valid.</value>
</data>
<data name="StatusText_UpdateAvailable" xml:space="preserve">
<data name="AnUpdateIsAvailable" xml:space="preserve">
<value>An update is available!</value>
</data>
<data name="CheckForUpdates" xml:space="preserve">
<value>Check for updates</value>
</data>
<data name="CheckingForUpdates" xml:space="preserve">
<value>Checking for Updates...</value>
</data>
<data name="ConfigIncompleteOrNotLoaded" xml:space="preserve">
<value>Config incomplete or not loaded!</value>
</data>
<data name="DownloadProgramUpdate" xml:space="preserve">
<value>Downloading program update...</value>
</data>
<data name="ErrorOnUpdateCheckOrUpdating" xml:space="preserve">
<value>Error on update check or while updating!</value>
</data>
<data name="EverythingIsRightAndUpToDate" xml:space="preserve">
<value>Everything is right and up-to-date.</value>
</data>
<data name="Install" xml:space="preserve">
<value>Install</value>
</data>
<data name="InstallationKey" xml:space="preserve">
<value>Installation key</value>
</data>
<data name="Installing" xml:space="preserve">
<value>Installing...</value>
</data>
<data name="MinecraftProfile" xml:space="preserve">
<value>Minecraft profile</value>
</data>
<data name="MinecraftProfileFolderSeemsInvalid" xml:space="preserve">
<value>Minecraft profile folder seems to be not valid.</value>
</data>
<data name="ModpackConfigUrl" xml:space="preserve">
<value>Modpack config url</value>
</data>
<data name="Repair" xml:space="preserve">
<value>Repair</value>
</data>
<data name="Select" xml:space="preserve">
<value>Select</value>
</data>
<data name="SelectMinecraftProfileFolder" xml:space="preserve">
<value>Select the minecraft profile folder (usually named .minecraft)</value>
</data>
<data name="Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="UpdateServerInMaintenance" xml:space="preserve">
<value>The update servers are in maintenance.</value>
</data>
<data name="InstallationKeyNotValid" xml:space="preserve">
<value>Installation key seems to be invalid</value>
</data>
</root>

View File

@@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ModpackUpdater.Apps.Client.Gui.LangRes {
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class MsgBoxLangRes {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal MsgBoxLangRes() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.MsgBoxLangRes", typeof(MsgBoxLangRes).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
public static string UpdateAvailable {
get {
return ResourceManager.GetString("UpdateAvailable", resourceCulture);
}
}
public static string UpdateAvailable_Title {
get {
return ResourceManager.GetString("UpdateAvailable_Title", resourceCulture);
}
}
public static string ErrorWhileUpdate_Title {
get {
return ResourceManager.GetString("ErrorWhileUpdate_Title", resourceCulture);
}
}
public static string ErrorWhileUpdate {
get {
return ResourceManager.GetString("ErrorWhileUpdate", resourceCulture);
}
}
}
}

View File

@@ -117,4 +117,17 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="UpdateAvailable" xml:space="preserve">
<value>A new version of this program is available! Install now?
If you confirm, the update will be installed automatically within a few seconds.</value>
</data>
<data name="UpdateAvailable_Title" xml:space="preserve">
<value>New program version available</value>
</data>
<data name="ErrorWhileUpdate_Title" xml:space="preserve">
<value>Error while updating</value>
</data>
<data name="ErrorWhileUpdate" xml:space="preserve">
<value>An error happened while updating the program. Error message:\n{0}</value>
</data>
</root>

View File

@@ -0,0 +1,121 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lang="clr-namespace:ModpackUpdater.Apps.Client.Gui.LangRes"
xmlns:pilz="https://git.pilzinsel64.de/pilz-framework/pilz"
mc:Ignorable="d"
x:Class="ModpackUpdater.Apps.Client.Gui.MainForm"
Width="520"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
CanMaximize="false"
Title="Minecraft Modpack Updater"
Icon="/Assets/app.ico"
>
<Grid
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
x:Name="MainGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="250"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Labels -->
<Label Grid.Row="0" Grid.Column="0" Margin="3" VerticalAlignment="Center" Content="{x:Static lang:GeneralLangRes.MinecraftProfile}" />
<Label Grid.Row="1" Grid.Column="0" Margin="3" VerticalAlignment="Center" Content="{x:Static lang:GeneralLangRes.ModpackConfigUrl}" />
<Label Grid.Row="2" Grid.Column="0" Margin="3" VerticalAlignment="Center" IsVisible="false" x:Name="LabelInstallKey" Content="{x:Static lang:GeneralLangRes.InstallationKey}" />
<Label Grid.Row="3" Grid.Column="0" Margin="3" VerticalAlignment="Center" Content="{x:Static lang:GeneralLangRes.Status}" />
<Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="3" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Spacing="6">
<Image Width="16" Height="16" x:Name="ImageStatus" />
<TextBlock x:Name="TextStatus" />
</StackPanel>
</Label>
<!-- TextBoxes: Profile -->
<TextBox
x:Name="TextBoxMinecraftProfileFolder"
Grid.Row="0"
Grid.Column="1"
Margin="3"
VerticalAlignment="Center"
Watermark="C:\..."
TextChanged="TextBoxMinecraftProfileFolder_TextChanged"
/>
<!-- TextBoxes: ModpackConfig -->
<TextBox
x:Name="TextBoxModpackConfig"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="3"
VerticalAlignment="Center"
Watermark="https://..."
TextChanged="TextBoxModpackConfig_TextChanged"
/>
<!-- TextBoxes: InstallKey -->
<TextBox
x:Name="TextBoxInstallKey"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="3"
VerticalAlignment="Center"
Watermark="XXXXX-YYYYY-ZZZZZ-AAAAA-BBBBB"
TextChanged="TextBoxInstallKey_TextChanged"
IsVisible="false"
/>
<!-- Button: SearchProfileFolder -->
<pilz:ImageButton
x:Name="ButtonSearchProfileFolder"
Grid.Row="0"
Grid.Column="2"
Margin="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Text="{x:Static lang:GeneralLangRes.Select}"
Click="ButtonSearchProfileFolder_Click"
/>
<!-- Button: CheckForUpdates -->
<pilz:ImageButton
x:Name="ButtonCheckForUpdates"
Grid.Row="4"
Grid.Column="1"
Margin="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Text="{x:Static lang:GeneralLangRes.CheckForUpdates}"
Click="ButtonCheckForUpdates_Click"
/>
<!-- Button: Install -->
<pilz:ImageSplitButton
x:Name="ButtonInstall"
Grid.Row="4"
Grid.Column="2"
Margin="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Text="{x:Static lang:GeneralLangRes.Install}"
Click="ButtonInstall_Click"
>
<SplitButton.Flyout>
<MenuFlyout>
<MenuItem x:Name="MenuItemRepair" Header="{x:Static lang:GeneralLangRes.Repair}" Click="MenuItemRepair_Click" />
</MenuFlyout>
</SplitButton.Flyout>
</pilz:ImageSplitButton>
</Grid>
</Window>

View File

@@ -0,0 +1,381 @@
using System.Diagnostics;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using ModpackUpdater.Apps.Client.Gui.LangRes;
using ModpackUpdater.Manager;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using Pilz;
using Pilz.Extensions;
using Pilz.Runtime;
using Pilz.SymbolPacks.Sets;
using Pilz.UI.AvaloniaUI.Symbols;
using Pilz.UI.Symbols;
using Pilz.Updating.Client;
namespace ModpackUpdater.Apps.Client.Gui;
public partial class MainForm : Window
{
private readonly UpdateCheckOptions updateOptions = new();
private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new();
private ModpackFeatures? features;
private UpdateCheckResult? lastUpdateCheckResult;
private bool currentUpdating;
private bool loadingData;
private int curOptionsRow = 3;
public MainForm()
{
InitializeComponent();
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
Closing += MainForm_Closing;
Loaded += MainForm_Loaded;
ButtonSearchProfileFolder.ImageSource = Symbols.Fluent.GetImageSource(SymbolsFluent.opened_folder);
ButtonCheckForUpdates.ImageSource = Symbols.Fluent.GetImageSource(SymbolsFluent.update);
ButtonInstall.ImageSource = Symbols.Fluent.GetImageSource(SymbolsFluent.software_installer);
MenuItemRepair.Icon = Symbols.Fluent.GetImage(SymbolsFluent.wrench, SymbolSize.Small);
ClearStatus();
LoadProfileToUi();
}
#region Features
private void SetStatus(string statusText, IImage? image)
{
TextStatus.Text = statusText;
ImageStatus.Source = image;
}
private void ClearStatus()
{
TextStatus.Text = "-";
ImageStatus.Source = null;
}
private void LoadProfileToUi()
{
loadingData = true;
TextBoxMinecraftProfileFolder.Text = modpackInfo.LocaLPath ?? AppConfig.Instance.LastMinecraftProfilePath ?? TextBoxMinecraftProfileFolder.Text;
TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text;
TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text;
Dispatcher.UIThread.Post(() => loadingData = false, DispatcherPriority.Background);
}
private void LoadOptionsToUi()
{
//foreach (var set in )
//{
// // ...
//}
}
private async void CheckStatusAndUpdate(bool loadProfileToUi)
{
if (CheckStatus(loadProfileToUi))
await ExecuteUpdate(false, false);
}
private bool CheckStatus(bool loadProfileToUi)
{
try
{
modpackInfo = ModpackInfo.TryLoad(TextBoxMinecraftProfileFolder.Text?.Trim());
}
catch
{
// Ignore
}
if (loadProfileToUi)
LoadProfileToUi();
try
{
updateConfig = ModpackConfig.LoadFromUrl(TextBoxModpackConfig.Text);
}
catch
{
// Ignore
}
if (modpackInfo != null)
{
features = new(updateConfig);
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
if (!features.IsInvalid() && !string.IsNullOrWhiteSpace(TextBoxInstallKey.Text) && !AllowExtras())
{
SetStatus(GeneralLangRes.InstallationKeyNotValid, Symbols.Fluent.GetImageSource(SymbolsFluent.warning_shield));
return false;
}
}
LabelInstallKey.IsVisible = TextBoxInstallKey.IsVisible = !string.IsNullOrWhiteSpace(updateConfig.UnleashApiUrl);
if (modpackInfo == null || string.IsNullOrWhiteSpace(TextBoxMinecraftProfileFolder.Text) /*|| modpackInfo.Valid*/)
{
SetStatus(GeneralLangRes.MinecraftProfileFolderSeemsInvalid, Symbols.Fluent.GetImageSource(SymbolsFluent.warning_shield));
ButtonCheckForUpdates.IsEnabled = false;
ButtonInstall.IsEnabled = false;
return false;
}
else if (string.IsNullOrWhiteSpace(TextBoxModpackConfig.Text))
{
SetStatus(GeneralLangRes.ConfigIncompleteOrNotLoaded, Symbols.Fluent.GetImageSource(SymbolsFluent.warning_shield));
ButtonCheckForUpdates.IsEnabled = false;
ButtonInstall.IsEnabled = false;
return false;
}
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
{
SetStatus(GeneralLangRes.UpdateServerInMaintenance, Symbols.Fluent.GetImageSource(SymbolsFluent.services));
ButtonCheckForUpdates.IsEnabled = false;
ButtonInstall.IsEnabled = false;
return false;
}
LoadOptionsToUi();
ButtonCheckForUpdates.IsEnabled = true;
ButtonInstall.IsEnabled = true;
return true;
}
private async Task ExecuteUpdate(bool doInstall, bool repair)
{
// Ensure set extras key
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
var updater = new ModpackInstaller(updateConfig, modpackInfo);
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
void error()
{
SetStatus(GeneralLangRes.ErrorOnUpdateCheckOrUpdating, Symbols.Fluent.GetImageSource(SymbolsFluent.close));
currentUpdating = false;
}
void installing()
{
SetStatus(GeneralLangRes.Installing, Symbols.Fluent.GetImageSource(SymbolsFluent.software_installer));
currentUpdating = true;
}
void updatesAvailable()
{
SetStatus(GeneralLangRes.AnUpdateIsAvailable, Symbols.Fluent.GetImageSource(SymbolsFluent.software_installer));
}
void everythingOk()
{
SetStatus(GeneralLangRes.EverythingIsRightAndUpToDate, Symbols.Fluent.GetImageSource(SymbolsFluent.done));
currentUpdating = false;
}
// Check only if not pressed "install", not really needed otherwise.
if (lastUpdateCheckResult is null || !doInstall || repair)
{
SetStatus(GeneralLangRes.CheckingForUpdates, Symbols.Fluent.GetImageSource(SymbolsFluent.update));
// Check for extras once again
updateOptions.IncludeExtras = AllowExtras();
// Force re-install on repair
updateOptions.IgnoreInstalledVersion = repair;
try
{
lastUpdateCheckResult = await updater.Check(updateOptions);
}
catch (Exception)
{
error();
if (Debugger.IsAttached)
throw;
}
}
// Error while update check
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
{
error();
return;
}
// Load options
// lastUpdateCheckResult.OptionsAvailable...
// lastUpdateCheckResult.OptionsEnabled...
// No updates available
if (!lastUpdateCheckResult.HasUpdates)
{
everythingOk();
return;
}
// Updates available (but don't install)
if (!doInstall)
{
updatesAvailable();
return;
}
// Install updates
installing();
currentUpdating = true;
try
{
// Install
if (await updater.Install(lastUpdateCheckResult) == false)
{
error();
return;
}
// Success
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
everythingOk();
}
catch (Exception)
{
// Error
error();
if (Debugger.IsAttached)
throw;
}
}
private bool AllowExtras()
{
return features != null && features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
}
private void Updated_CheckingProgresssUpdated(int toCheck, int processed)
{
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", Symbols.Fluent.GetImageSource(SymbolsFluent.update));
}
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
{
var actionCount = result.Actions.Count;
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", Symbols.Fluent.GetImageSource(SymbolsFluent.software_installer));
}
#endregion
#region Gui
private void MainForm_Closing(object? sender, WindowClosingEventArgs e)
{
AppConfig.Instance.LastMinecraftProfilePath = TextBoxMinecraftProfileFolder.Text?.Trim();
}
private async Task UpdateApp()
{
if (Debugger.IsAttached)
return;
var myAppPath = EnvironmentEx.ProcessPath!;
var updater = new UpdateClient(Program.UpdateUrl, Assembly.GetEntryAssembly()!.GetAppVersion(), AppChannel.Stable)
{
Distro = RuntimeInformationsEx.GetRuntimeIdentifier(),
};
if (await updater.CheckForUpdate() is {} packageToInstall
&& await MessageBoxManager.GetMessageBoxStandard(MsgBoxLangRes.UpdateAvailable_Title, MsgBoxLangRes.UpdateAvailable, ButtonEnum.YesNo, MsBox.Avalonia.Enums.Icon.Info).ShowWindowDialogAsync(this) == ButtonResult.Yes)
{
SetStatus(GeneralLangRes.DownloadProgramUpdate, Symbols.Fluent.GetImageSource(SymbolsFluent.software_installer));
IsEnabled = false;
if (await updater.DownloadPackageAsync(packageToInstall)
&& await updater.InstallPackageAsync(packageToInstall, myAppPath))
{
IsVisible = false;
await Process.Start(myAppPath).WaitForExitAsync();
Environment.Exit(0);
return;
}
IsEnabled = true;
}
}
private async void MainForm_Loaded(object? sender, RoutedEventArgs e)
{
#if !DISABLE_UPDATE
try
{
await UpdateApp();
}
catch (Exception ex)
{
await MessageBoxManager.GetMessageBoxStandard(MsgBoxLangRes.UpdateAvailable_Title, string.Format(MsgBoxLangRes.UpdateAvailable, ex.Message), ButtonEnum.YesNo, MsBox.Avalonia.Enums.Icon.Info).ShowAsync();
IsEnabled = true;
}
#endif
CheckStatusAndUpdate(true);
}
private void TextBoxMinecraftProfileFolder_TextChanged(object? o, TextChangedEventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(true);
}
private void TextBoxModpackConfig_TextChanged(object? o, RoutedEventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void TextBoxInstallKey_TextChanged(object? o, RoutedEventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private async void ButtonSearchProfileFolder_Click(object? sender, RoutedEventArgs e)
{
var filePaths = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = GeneralLangRes.SelectMinecraftProfileFolder,
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)),
AllowMultiple = false,
});
if (filePaths.Count >= 1)
TextBoxMinecraftProfileFolder.Text = filePaths[0].Path.AbsolutePath;
}
private async void ButtonCheckForUpdates_Click(object? sender, RoutedEventArgs e)
{
ClearStatus();
await ExecuteUpdate(false, false);
}
private async void ButtonInstall_Click(object? sender, RoutedEventArgs e)
{
if (currentUpdating)
return;
ClearStatus();
await ExecuteUpdate(true, false);
}
private async void MenuItemRepair_Click(object? sender, RoutedEventArgs e)
{
if (currentUpdating)
return;
ClearStatus();
await ExecuteUpdate(true, true);
}
#endregion
}

View File

@@ -0,0 +1,100 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ApplicationIcon>Assets\app.ico</ApplicationIcon>
<AssemblyName>MinecraftModpackUpdater</AssemblyName>
<ImplicitUsings>true</ImplicitUsings>
<Nullable>enable</Nullable>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<TrimmerRootAssembly Include="Newtonsoft.Json" />
<TrimmerRootAssembly Include="Pilz.SymbolPacks.Fluent" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Version.cs" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<Compile Update="FiledialogFilters.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>FiledialogFilters.resx</DependentUpon>
</Compile>
<Compile Update="LangRes\GeneralLangRes.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>GeneralLangRes.resx</DependentUpon>
</Compile>
<Compile Update="LangRes\MsgBoxLangRes.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>MsgBoxLangRes.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="FiledialogFilters.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>FiledialogFilters.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="LangRes\GeneralLangRes.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>GeneralLangRes.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="LangRes\MsgBoxLangRes.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>MsgBoxLangRes.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia.BuildServices" Version="11.3.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="HarfBuzzSharp" Version="8.3.1.2" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.2" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="8.3.1.2" />
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Pilz" Version="2.6.1" />
<PackageReference Include="Pilz.Configuration" Version="3.2.7" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
<PackageReference Include="Pilz.IO" Version="2.1.0" />
<PackageReference Include="Pilz.SymbolPacks.Fluent" Version="1.0.12" />
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.1.3" />
<PackageReference Include="Pilz.UI.AvaloniaUI.Symbols" Version="1.0.0" />
<PackageReference Include="Avalonia" Version="11.3.8" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
<PackageReference Include="Avalonia.Svg" Version="11.3.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.8">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="Pilz.Updating" Version="4.3.5" />
<PackageReference Include="Pilz.Updating.Client" Version="4.4.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModpackUpdater.Apps.AppUpdates\ModpackUpdater.Apps.AppUpdates.csproj" />
<ProjectReference Include="..\ModpackUpdater.Manager\ModpackUpdater.Manager.csproj" />
<ProjectReference Include="..\ModpackUpdater\ModpackUpdater.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,72 @@
using Avalonia;
using Castle.Core.Logging;
using Newtonsoft.Json;
using Pilz;
using Pilz.Configuration;
namespace ModpackUpdater.Apps.Client.Gui;
public static class Program
{
public const string UpdateUrl = "https://git.pilzinsel64.de/litw-refined/minecraft-modpack-updater/-/snippets/3/raw/main/updates-new.json";
private static readonly SettingsManager settingsManager;
public static string ProcessPath = EnvironmentEx.ProcessPath!;
public static ISettings Settings => settingsManager.Instance;
public static ILogger Log { get; } = new ConsoleLogger();
static Program()
{
settingsManager = new(GetSettingsPath(2), true);
MigrateLegacySettings(GetSettingsPath(null));
}
[STAThread]
internal static void Main(string[] args)
{
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
private static string GetSettingsPath(int? settingsVersion = 3)
{
const string appDataDirectoryName = "MinecraftModpackUpdater";
var fileNamePostfix = settingsVersion == null ? string.Empty : $"V{settingsVersion}";
var settingsFileName = $"Settings{fileNamePostfix}.json";
var settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), appDataDirectoryName);
Directory.CreateDirectory(settingsPath);
settingsPath = Path.Combine(settingsPath, settingsFileName);
return settingsPath;
}
private static void MigrateLegacySettings(string settingsPath)
{
// Try load legacy config file
if (!File.Exists(settingsPath) || JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText(settingsPath)) is not AppConfig legacyConfig)
return;
// Migrate
var newConfig = Settings.Get<AppConfig>();
newConfig.LastMinecraftProfilePath = legacyConfig.LastMinecraftProfilePath;
if (ModpackInfo.TryLoad(legacyConfig.LastMinecraftProfilePath) is ModpackInfo info)
#pragma warning disable CS0612 // Typ oder Element ist veraltet
info.ConfigUrl = legacyConfig.ConfigFilePath;
// Ensure save settings
settingsManager.Save();
// Delete legacy config file
File.Delete(settingsPath);
}
}

View File

@@ -6,12 +6,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\publish\general\</PublishDir>
<PublishDir>..\publish\ui\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0-windows</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>

View File

@@ -0,0 +1,92 @@
# PUPNET DEPLOY: 1.9.1
# Use: 'pupnet --help conf' for information.
# APP PREAMBLE
AppBaseName = MinecraftModpackUpdater
AppFriendlyName = Minecraft Modpack Updater
AppId = de.pilzinsel64.minecraft-modpack-updater
AppVersionRelease = 1.0.0[1]
AppShortSummary = Install and Update Minecraft Modpacks easliy.
AppDescription = """
Minecraft Modpack Updater is a simple tool to install and update a modpack to a selected minecraft profile by a selected modpack configuration file. It downloads a config file via https and checks the version there and what files has been changed and download the updateded files via a given link from the config.
"""
AppLicenseId = MIT
AppLicenseFile = ../LICENSE
AppChangeFile =
# PUBLISHER
PublisherName = Pilzinsel64
PublisherId = de.pilzinsel64
PublisherCopyright = Copyright (C) Pilzinsel64 2025
PublisherLinkName = Pilzinsel64 Homepage
PublisherLinkUrl = https://pilzinsel64.de
PublisherEmail =
# DESKTOP INTEGRATION
DesktopNoDisplay = false
DesktopTerminal = false
DesktopFile =
StartCommand =
PrimeCategory =
MetaFile =
IconFiles = """
Assets/app.ico
Assets/app.svg
"""
# DOTNET PUBLISH
DotnetProjectPath = ModpackUpdater.Apps.Client.Gui.csproj
DotnetPublishArgs = -p:Version=${APP_VERSION} --self-contained true -p:DebugType=None -p:DebugSymbols=false -p:PublishSingleFile=true -p:PublishTrimmed=false
DotnetPostPublish =
DotnetPostPublishOnWindows =
# PACKAGE OUTPUT
PackageName = minecraft-modpack-updater
OutputDirectory = ../publish/client-ui
# APPIMAGE OPTIONS
AppImageArgs =
AppImageRuntimePath =
AppImageVersionOutput = false
# FLATPAK OPTIONS
FlatpakPlatformRuntime = org.freedesktop.Platform
FlatpakPlatformSdk = org.freedesktop.Sdk
FlatpakPlatformVersion = 25.08
FlatpakFinishArgs = """
--socket=wayland
--socket=fallback-x11
--filesystem=home
--share=network
"""
FlatpakBuilderArgs =
# RPM OPTIONS
RpmAutoReq = false
RpmAutoProv = true
RpmRequires = """
krb5-libs
libicu
openssl-libs
"""
# DEBIAN OPTIONS
DebianRecommends = """
libc6
libgcc1
libgssapi-krb5-2
libicu70
libssl3
libstdc++6
zlib1g
"""
# WINDOWS SETUP OPTIONS
SetupGroupName =
SetupAdminInstall = false
SetupCommandPrompt =
SetupMinWindowsVersion = 10
SetupSignTool =
SetupSuffixOutput =
SetupVersionOutput = false
SetupUninstallScript =

View File

@@ -0,0 +1,12 @@
pupnet -y -v "$1[1]" -r linux-x64 -k appimage
#pupnet -y -v "$1[1]" -r linux-x64 -k flatpak -p DefineConstants=DISABLE_UPDATE
#pupnet -y -v "$1[1]"-r linux-x64 -k deb -p DefineConstants=DISABLE_UPDATE
#pupnet -y -v "$1[1]"-r linux-x64 -k rpm -p DefineConstants=DISABLE_UPDATE
pupnet -y -v "$1[1]" -r linux-arm64 -k appimage
#pupnet -y -v "$1[1]" -r linux-arm64 -k flatpak -p DefineConstants=DISABLE_UPDATE
#pupnet -y -v "$1[1]"-r linux-arm64 -k deb -p DefineConstants=DISABLE_UPDATE
#pupnet -y -v "$1[1]"-r linux-arm64 -k rpm -p DefineConstants=DISABLE_UPDATE
pupnet -y -v "$1[1]" -r win-x64 -k zip
pupnet -y -v "$1[1]" -r win-arm64 -k zip
pupnet -y -v "$1[1]" -r osx-x64 -k zip
pupnet -y -v "$1[1]" -r osx-arm64 -k zip

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="TelerikWinFormsThemeName" value="Windows11CompactDark" />
</appSettings>
</configuration>

View File

@@ -1,79 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Pilz.Extensions;
using System.Reflection;
namespace ModpackUpdater.Apps.Client;
public class AppUpdater
{
private class UpdateInfo
{
[JsonConverter(typeof(VersionConverter))]
public Version Version { get; set; }
public string DownloadUrl { get; set; }
}
private const string UPDATE_URL = "https://git.pilzinsel64.de/gaming/minecraft/minecraft-modpack-updater/-/snippets/3/raw/main/updates.json";
private readonly HttpClient httpClient = new();
private UpdateInfo info;
public async Task<bool> Check()
{
var hasUpdate = false;
try
{
var appVersion = Assembly.GetExecutingAssembly().GetAppVersion().Version;
var result = await httpClient.GetStringAsync(UPDATE_URL);
info = JsonConvert.DeserializeObject<UpdateInfo>(result);
if (info is not null && info.Version > appVersion)
hasUpdate = true;
}
catch
{
}
return hasUpdate;
}
public async Task Install()
{
var client = new HttpClient();
var tempFileName = Path.GetTempFileName();
var appFileName = Pilz.Win32.NativeTools.GetExecutablePath();
var oldFileName = appFileName + ".old";
// Delete old file
try
{
File.Delete(oldFileName);
}
catch
{
}
// Download the new file
using (var tempFileStream = new FileStream(tempFileName, FileMode.Create, FileAccess.ReadWrite))
{
Stream downloadStream = null;
try
{
downloadStream = await client.GetStreamAsync(info.DownloadUrl);
await downloadStream.CopyToAsync(tempFileStream);
}
catch
{
}
finally
{
downloadStream?.Dispose();
}
}
// Replace current application file with new file
File.Move(appFileName, oldFileName, true);
File.Move(tempFileName, appFileName);
}
}

View File

@@ -1,334 +0,0 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace ModpackUpdater.Apps.Client
{
[Microsoft.VisualBasic.CompilerServices.DesignerGenerated()]
public partial class Form1 : Telerik.WinControls.UI.RadForm
{
// Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
[DebuggerNonUserCode()]
protected override void Dispose(bool disposing)
{
try
{
if (disposing && components is not null)
components.Dispose();
}
finally
{
base.Dispose(disposing);
}
}
// Wird vom Windows Form-Designer benötigt.
private System.ComponentModel.IContainer components = new System.ComponentModel.Container();
// Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
// Das Bearbeiten ist mit dem Windows Form-Designer möglich.
// Das Bearbeiten mit dem Code-Editor ist nicht möglich.
[DebuggerStepThrough()]
private void InitializeComponent()
{
var resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
RadLabel1 = new Telerik.WinControls.UI.RadLabel();
RadLabel2 = new Telerik.WinControls.UI.RadLabel();
RadLabel3 = new Telerik.WinControls.UI.RadLabel();
RadLabel_Status = new Telerik.WinControls.UI.RadLabel();
RadTextBox_MinecraftProfileFolder = new Telerik.WinControls.UI.RadTextBox();
RadTextBox_ModpackConfig = new Telerik.WinControls.UI.RadTextBox();
RadButton_Install = new Telerik.WinControls.UI.RadButton();
RadButton_CheckForUpdates = new Telerik.WinControls.UI.RadButton();
RadButton_PasteModpackConfig = new Telerik.WinControls.UI.RadButton();
RadButton_SearchMinecraftProfileFolder = new Telerik.WinControls.UI.RadButton();
tableLayoutPanel1 = new TableLayoutPanel();
radLabel4 = new Telerik.WinControls.UI.RadLabel();
radTextBox_InstallKey = new Telerik.WinControls.UI.RadTextBox();
radButton_PasteInstallKey = new Telerik.WinControls.UI.RadButton();
radButton_RefreshConfig = new Telerik.WinControls.UI.RadButton();
((System.ComponentModel.ISupportInitialize)RadLabel1).BeginInit();
((System.ComponentModel.ISupportInitialize)RadLabel2).BeginInit();
((System.ComponentModel.ISupportInitialize)RadLabel3).BeginInit();
((System.ComponentModel.ISupportInitialize)RadLabel_Status).BeginInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_MinecraftProfileFolder).BeginInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_ModpackConfig).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_Install).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_CheckForUpdates).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_PasteModpackConfig).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_SearchMinecraftProfileFolder).BeginInit();
tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radLabel4).BeginInit();
((System.ComponentModel.ISupportInitialize)radTextBox_InstallKey).BeginInit();
((System.ComponentModel.ISupportInitialize)radButton_PasteInstallKey).BeginInit();
((System.ComponentModel.ISupportInitialize)radButton_RefreshConfig).BeginInit();
((System.ComponentModel.ISupportInitialize)this).BeginInit();
SuspendLayout();
//
// RadLabel1
//
RadLabel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
RadLabel1.AutoSize = false;
RadLabel1.Location = new Point(3, 3);
RadLabel1.Name = "RadLabel1";
RadLabel1.Size = new Size(144, 24);
RadLabel1.TabIndex = 0;
RadLabel1.Text = "Minecraft profile folder:";
//
// RadLabel2
//
RadLabel2.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
RadLabel2.AutoSize = false;
RadLabel2.Location = new Point(3, 63);
RadLabel2.Name = "RadLabel2";
RadLabel2.Size = new Size(144, 24);
RadLabel2.TabIndex = 1;
RadLabel2.Text = "Modpack config:";
//
// RadLabel3
//
RadLabel3.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
RadLabel3.AutoSize = false;
RadLabel3.Location = new Point(3, 183);
RadLabel3.Name = "RadLabel3";
RadLabel3.Size = new Size(144, 24);
RadLabel3.TabIndex = 2;
RadLabel3.Text = "Status:";
//
// RadLabel_Status
//
RadLabel_Status.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
RadLabel_Status.AutoSize = false;
tableLayoutPanel1.SetColumnSpan(RadLabel_Status, 6);
RadLabel_Status.Location = new Point(153, 183);
RadLabel_Status.Name = "RadLabel_Status";
RadLabel_Status.Size = new Size(266, 24);
RadLabel_Status.TabIndex = 3;
RadLabel_Status.Text = "-";
RadLabel_Status.TextImageRelation = TextImageRelation.ImageBeforeText;
//
// RadTextBox_MinecraftProfileFolder
//
RadTextBox_MinecraftProfileFolder.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadTextBox_MinecraftProfileFolder, 6);
RadTextBox_MinecraftProfileFolder.Location = new Point(153, 3);
RadTextBox_MinecraftProfileFolder.Name = "RadTextBox_MinecraftProfileFolder";
RadTextBox_MinecraftProfileFolder.NullText = "No file loaded!";
RadTextBox_MinecraftProfileFolder.ReadOnly = true;
RadTextBox_MinecraftProfileFolder.Size = new Size(266, 24);
RadTextBox_MinecraftProfileFolder.TabIndex = 4;
//
// RadTextBox_ModpackConfig
//
RadTextBox_ModpackConfig.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadTextBox_ModpackConfig, 6);
RadTextBox_ModpackConfig.Location = new Point(153, 63);
RadTextBox_ModpackConfig.Name = "RadTextBox_ModpackConfig";
RadTextBox_ModpackConfig.NullText = "No config url provided.";
RadTextBox_ModpackConfig.ReadOnly = true;
RadTextBox_ModpackConfig.Size = new Size(266, 24);
RadTextBox_ModpackConfig.TabIndex = 5;
//
// RadButton_Install
//
RadButton_Install.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_Install, 2);
RadButton_Install.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_Install.Location = new Point(325, 213);
RadButton_Install.Name = "RadButton_Install";
RadButton_Install.Size = new Size(94, 24);
RadButton_Install.TabIndex = 10;
RadButton_Install.Text = "Install";
RadButton_Install.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_Install.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_Install.Click += ButtonX_StartUpdate_Click;
//
// RadButton_CheckForUpdates
//
RadButton_CheckForUpdates.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_CheckForUpdates, 3);
RadButton_CheckForUpdates.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_CheckForUpdates.Location = new Point(175, 213);
RadButton_CheckForUpdates.Name = "RadButton_CheckForUpdates";
RadButton_CheckForUpdates.Size = new Size(144, 24);
RadButton_CheckForUpdates.TabIndex = 0;
RadButton_CheckForUpdates.Text = "Check for Updates";
RadButton_CheckForUpdates.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_CheckForUpdates.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_CheckForUpdates.Click += ButtonX_CheckForUpdates_Click;
//
// RadButton_PasteModpackConfig
//
RadButton_PasteModpackConfig.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_PasteModpackConfig, 2);
RadButton_PasteModpackConfig.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_PasteModpackConfig.Location = new Point(325, 93);
RadButton_PasteModpackConfig.Name = "RadButton_PasteModpackConfig";
RadButton_PasteModpackConfig.Size = new Size(94, 24);
RadButton_PasteModpackConfig.TabIndex = 7;
RadButton_PasteModpackConfig.Text = "Paste";
RadButton_PasteModpackConfig.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_PasteModpackConfig.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_PasteModpackConfig.Click += RadButton_PasteModpackConfig_Click;
//
// RadButton_SearchMinecraftProfileFolder
//
RadButton_SearchMinecraftProfileFolder.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_SearchMinecraftProfileFolder, 2);
RadButton_SearchMinecraftProfileFolder.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_SearchMinecraftProfileFolder.Location = new Point(325, 33);
RadButton_SearchMinecraftProfileFolder.Name = "RadButton_SearchMinecraftProfileFolder";
RadButton_SearchMinecraftProfileFolder.Size = new Size(94, 24);
RadButton_SearchMinecraftProfileFolder.TabIndex = 6;
RadButton_SearchMinecraftProfileFolder.Text = "Search";
RadButton_SearchMinecraftProfileFolder.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_SearchMinecraftProfileFolder.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_SearchMinecraftProfileFolder.Click += ButtonX_SearchMinecraftProfile_Click;
//
// tableLayoutPanel1
//
tableLayoutPanel1.AutoSize = true;
tableLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
tableLayoutPanel1.ColumnCount = 7;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 150F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.Controls.Add(RadButton_CheckForUpdates, 2, 7);
tableLayoutPanel1.Controls.Add(RadLabel1, 0, 0);
tableLayoutPanel1.Controls.Add(RadLabel2, 0, 2);
tableLayoutPanel1.Controls.Add(RadTextBox_MinecraftProfileFolder, 1, 0);
tableLayoutPanel1.Controls.Add(RadTextBox_ModpackConfig, 1, 2);
tableLayoutPanel1.Controls.Add(RadLabel_Status, 1, 6);
tableLayoutPanel1.Controls.Add(RadLabel3, 0, 6);
tableLayoutPanel1.Controls.Add(RadButton_SearchMinecraftProfileFolder, 5, 1);
tableLayoutPanel1.Controls.Add(RadButton_Install, 5, 7);
tableLayoutPanel1.Controls.Add(radLabel4, 0, 4);
tableLayoutPanel1.Controls.Add(radTextBox_InstallKey, 1, 4);
tableLayoutPanel1.Controls.Add(radButton_PasteInstallKey, 5, 5);
tableLayoutPanel1.Controls.Add(RadButton_PasteModpackConfig, 5, 3);
tableLayoutPanel1.Controls.Add(radButton_RefreshConfig, 4, 3);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(0, 0);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 8;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.Size = new Size(422, 249);
tableLayoutPanel1.TabIndex = 7;
//
// radLabel4
//
radLabel4.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
radLabel4.AutoSize = false;
radLabel4.Location = new Point(3, 123);
radLabel4.Name = "radLabel4";
radLabel4.Size = new Size(144, 24);
radLabel4.TabIndex = 12;
radLabel4.Text = "Installation key:";
//
// radTextBox_InstallKey
//
radTextBox_InstallKey.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(radTextBox_InstallKey, 6);
radTextBox_InstallKey.Location = new Point(153, 123);
radTextBox_InstallKey.Name = "radTextBox_InstallKey";
radTextBox_InstallKey.NullText = "No key provided. Only for private servers.";
radTextBox_InstallKey.ReadOnly = true;
radTextBox_InstallKey.Size = new Size(266, 24);
radTextBox_InstallKey.TabIndex = 13;
//
// radButton_PasteInstallKey
//
radButton_PasteInstallKey.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(radButton_PasteInstallKey, 2);
radButton_PasteInstallKey.ImageAlignment = ContentAlignment.MiddleRight;
radButton_PasteInstallKey.Location = new Point(325, 153);
radButton_PasteInstallKey.Name = "radButton_PasteInstallKey";
radButton_PasteInstallKey.Size = new Size(94, 24);
radButton_PasteInstallKey.TabIndex = 14;
radButton_PasteInstallKey.Text = "Paste";
radButton_PasteInstallKey.TextAlignment = ContentAlignment.MiddleLeft;
radButton_PasteInstallKey.TextImageRelation = TextImageRelation.ImageBeforeText;
radButton_PasteInstallKey.Click += RadButton_PasteInstallKey_Click;
//
// radButton_RefreshConfig
//
radButton_RefreshConfig.Anchor = AnchorStyles.Top | AnchorStyles.Right;
radButton_RefreshConfig.DisplayStyle = Telerik.WinControls.DisplayStyle.Image;
radButton_RefreshConfig.ImageAlignment = ContentAlignment.MiddleCenter;
radButton_RefreshConfig.Location = new Point(295, 93);
radButton_RefreshConfig.Name = "radButton_RefreshConfig";
radButton_RefreshConfig.Size = new Size(24, 24);
radButton_RefreshConfig.TabIndex = 11;
radButton_RefreshConfig.Text = "Reload";
radButton_RefreshConfig.Click += RadButton_RefreshConfig_Click;
//
// Form1
//
AutoScaleBaseSize = new Size(7, 15);
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
ClientSize = new Size(422, 249);
Controls.Add(tableLayoutPanel1);
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimumSize = new Size(430, 0);
Name = "Form1";
StartPosition = FormStartPosition.CenterScreen;
Text = "Minecraft Modpack Updater";
FormClosing += Form1_FormClosing;
Load += Form1_Load;
Shown += Form1_Shown;
((System.ComponentModel.ISupportInitialize)RadLabel1).EndInit();
((System.ComponentModel.ISupportInitialize)RadLabel2).EndInit();
((System.ComponentModel.ISupportInitialize)RadLabel3).EndInit();
((System.ComponentModel.ISupportInitialize)RadLabel_Status).EndInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_MinecraftProfileFolder).EndInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_ModpackConfig).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_Install).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_CheckForUpdates).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_PasteModpackConfig).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_SearchMinecraftProfileFolder).EndInit();
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)radLabel4).EndInit();
((System.ComponentModel.ISupportInitialize)radTextBox_InstallKey).EndInit();
((System.ComponentModel.ISupportInitialize)radButton_PasteInstallKey).EndInit();
((System.ComponentModel.ISupportInitialize)radButton_RefreshConfig).EndInit();
((System.ComponentModel.ISupportInitialize)this).EndInit();
ResumeLayout(false);
PerformLayout();
}
internal Telerik.WinControls.UI.RadLabel RadLabel1;
internal Telerik.WinControls.UI.RadLabel RadLabel2;
internal Telerik.WinControls.UI.RadLabel RadLabel3;
internal Telerik.WinControls.UI.RadLabel RadLabel_Status;
internal Telerik.WinControls.UI.RadTextBox RadTextBox_MinecraftProfileFolder;
internal Telerik.WinControls.UI.RadTextBox RadTextBox_ModpackConfig;
internal Telerik.WinControls.UI.RadButton RadButton_Install;
internal Telerik.WinControls.UI.RadButton RadButton_CheckForUpdates;
internal Telerik.WinControls.UI.RadButton RadButton_SearchMinecraftProfileFolder;
internal Telerik.WinControls.UI.RadButton RadButton_PasteModpackConfig;
private TableLayoutPanel tableLayoutPanel1;
private Telerik.WinControls.UI.RadButton radButton_RefreshConfig;
internal Telerik.WinControls.UI.RadLabel radLabel4;
internal Telerik.WinControls.UI.RadTextBox radTextBox_InstallKey;
internal Telerik.WinControls.UI.RadButton radButton_PasteInstallKey;
}
}

View File

@@ -1,310 +0,0 @@
using ModpackUpdater.Manager;
using ModpackUpdater.My.Resources;
using Pilz.Extensions;
using Pilz.UI.Symbols;
using System.Diagnostics;
using System.Reflection;
using Telerik.WinControls;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Client;
public partial class Form1
{
private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new();
private ModpackFeatures features;
private bool currentUpdating = false;
private UpdateCheckResult lastUpdateCheckResult = null;
private readonly UpdateCheckOptionsAdv updateOptions;
public Form1(UpdateCheckOptionsAdv updateOptions) : this()
{
this.updateOptions = updateOptions;
if (!string.IsNullOrWhiteSpace(updateOptions.ProfileFolder))
LoadMinecraftProfile(updateOptions.ProfileFolder);
else if (!string.IsNullOrWhiteSpace(AppConfig.Instance.LastMinecraftProfilePath))
LoadMinecraftProfile(AppConfig.Instance.LastMinecraftProfilePath);
}
public Form1()
{
InitializeComponent();
Text = $"{Text} (v{Assembly.GetExecutingAssembly().GetAppVersion()})";
RadButton_Install.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small);
RadButton_CheckForUpdates.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small);
radButton_RefreshConfig.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.refresh, SymbolSize.Small);
RadButton_SearchMinecraftProfileFolder.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.opened_folder, SymbolSize.Small);
radButton_PasteInstallKey.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.paste, SymbolSize.Small);
RadButton_PasteModpackConfig.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.paste, SymbolSize.Small);
}
private void LoadMinecraftProfile(string folderPath)
{
RadTextBox_MinecraftProfileFolder.Text = folderPath;
AppConfig.Instance.LastMinecraftProfilePath = folderPath;
CheckStatusAndUpdate(loadedProfile: true);
}
private void LoadUpdateConfigFile(string filePath)
{
RadTextBox_ModpackConfig.Text = filePath;
CheckStatusAndUpdate();
}
private void LoadInstallKey(string installKey)
{
radTextBox_InstallKey.Text = installKey;
CheckStatusAndUpdate();
}
private void SetStatus(string statusText, RadSvgImage image)
{
RadLabel_Status.Text = statusText;
RadLabel_Status.SvgImage = image;
}
private void ClearStatus()
{
RadLabel_Status.Text = "-";
RadLabel_Status.SvgImage = null;
}
private void CheckStatusAndUpdate(bool loadedProfile = false)
{
if (CheckStatus(loadedProfile))
RadButton_CheckForUpdates.PerformClick();
}
private bool CheckStatus(bool loadedProfile)
{
try
{
modpackInfo = ModpackInfo.TryLoad(RadTextBox_MinecraftProfileFolder.Text.Trim());
if (loadedProfile)
{
RadTextBox_ModpackConfig.Text = modpackInfo.ConfigUrl;
radTextBox_InstallKey.Text = modpackInfo.ExtrasKey;
}
else
modpackInfo.ExtrasKey = radTextBox_InstallKey.Text;
}
catch
{
}
try
{
updateConfig = ModpackConfig.LoadFromUrl(RadTextBox_ModpackConfig.Text);
}
catch (Exception)
{
}
if (modpackInfo != null)
features = new(updateConfig);
if (modpackInfo == null || string.IsNullOrWhiteSpace(RadTextBox_MinecraftProfileFolder.Text) /*|| modpackInfo.Valid*/)
{
SetStatus(LangRes.StatusText_MinecraftProfileWarning, AppGlobals.Symbols.GetSvgImage(AppSymbols.general_warning_sign, SymbolSize.Small));
RadButton_PasteModpackConfig.Enabled = false;
radButton_PasteInstallKey.Enabled = false;
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
else if (updateConfig == null || string.IsNullOrWhiteSpace(RadTextBox_ModpackConfig.Text))
{
SetStatus(LangRes.StatusText_ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetSvgImage(AppSymbols.general_warning_sign, SymbolSize.Small));
RadButton_PasteModpackConfig.Enabled = true;
radButton_PasteInstallKey.Enabled = false;
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
{
SetStatus(LangRes.StatusText_Maintenance, AppGlobals.Symbols.GetSvgImage(AppSymbols.services, SymbolSize.Small));
RadButton_PasteModpackConfig.Enabled = true;
radButton_PasteInstallKey.Enabled = true;
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
RadButton_PasteModpackConfig.Enabled = true;
radButton_PasteInstallKey.Enabled = true;
RadButton_CheckForUpdates.Enabled = true;
RadButton_Install.Enabled = true;
return true;
}
private async Task ExecuteUpdate(bool doInstall)
{
var updater = new ModpackInstaller(updateConfig, modpackInfo);
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
void error()
{
SetStatus(LangRes.StatusText_ErrorWhileUpdateCheckOrUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.close, SymbolSize.Small));
currentUpdating = false;
}
void installing()
{
SetStatus(LangRes.StatusText_Installing, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
currentUpdating = true;
}
void updatesAvailable()
{
SetStatus(LangRes.StatusText_UpdateAvailable, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
}
void everythingOk()
{
SetStatus(LangRes.StatusTest_EverythingOk, AppGlobals.Symbols.GetSvgImage(AppSymbols.done, SymbolSize.Small));
currentUpdating = false;
}
// Check only if not pressed "install", not really needed otherwise.
if (lastUpdateCheckResult is null || !doInstall)
{
SetStatus(LangRes.StatusText_CheckingForUpdates, AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small));
// Check for extras once again
updateOptions.IncludeExtras = features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
try
{
lastUpdateCheckResult = await updater.Check(updateOptions);
}
catch(Exception)
{
error();
if (Debugger.IsAttached)
throw;
}
finally
{
}
}
// Error while update check
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
{
error();
return;
}
// No updates available
if (!lastUpdateCheckResult.HasUpdates)
{
everythingOk();
return;
}
// Updates available (but don't install)
if (!doInstall)
{
updatesAvailable();
return;
}
// Install updates
installing();
currentUpdating = true;
try
{
// Install
if (await updater.Install(lastUpdateCheckResult) == false)
{
error();
return;
}
// Success
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
everythingOk();
}
catch (Exception)
{
// Error
error();
if (Debugger.IsAttached)
throw;
}
}
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
{
int actionCount = result.Actions.Count;
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
}
private void Updated_CheckingProgresssUpdated(int toCheck, int processed)
{
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small));
}
private void ButtonX_SearchMinecraftProfile_Click(object sender, EventArgs e)
{
var ofd = new RadOpenFolderDialog();
if (ofd.ShowDialog(this) == DialogResult.OK)
LoadMinecraftProfile(ofd.FileName);
}
private void RadButton_PasteModpackConfig_Click(object sender, EventArgs e)
{
LoadUpdateConfigFile(Clipboard.GetText());
}
private void RadButton_PasteInstallKey_Click(object sender, EventArgs e)
{
LoadInstallKey(Clipboard.GetText());
}
private void RadButton_RefreshConfig_Click(object sender, EventArgs e)
{
LoadUpdateConfigFile(RadTextBox_ModpackConfig.Text);
}
private async void ButtonX_CheckForUpdates_Click(object sender, EventArgs e)
{
ClearStatus();
await ExecuteUpdate(false);
}
private async void ButtonX_StartUpdate_Click(object sender, EventArgs e)
{
if (!currentUpdating)
{
ClearStatus();
await ExecuteUpdate(true);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
AppConfig.Instance.LastMinecraftProfilePath = RadTextBox_MinecraftProfileFolder.Text;
}
private void Form1_Load(object sender, EventArgs e)
{
if (Directory.Exists(AppConfig.Instance.LastMinecraftProfilePath))
LoadMinecraftProfile(AppConfig.Instance.LastMinecraftProfilePath);
}
private async void Form1_Shown(object sender, EventArgs e)
{
var updater = new AppUpdater();
if (!updateOptions.NoUpdate && await updater.Check() && RadMessageBox.Show(LangRes.MsgBox_UpdateAvailable, LangRes.MsgBox_UpdateAvailable_Title, MessageBoxButtons.YesNo, RadMessageIcon.Info) == DialogResult.Yes)
{
SetStatus(LangRes.StatusText_InstallingAppUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
Enabled = false;
await updater.Install();
Application.Restart();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,162 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ModpackUpdater.My.Resources {
using System;
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class LangRes {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal LangRes() {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.LangRes", typeof(LangRes).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die A new version of this program is available. If you confirm, the update will be installed automatically. It takes just a few seconds. Continue? ähnelt.
/// </summary>
internal static string MsgBox_UpdateAvailable {
get {
return ResourceManager.GetString("MsgBox_UpdateAvailable", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die New program version available ähnelt.
/// </summary>
internal static string MsgBox_UpdateAvailable_Title {
get {
return ResourceManager.GetString("MsgBox_UpdateAvailable_Title", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Everything is right and up-to-date. ähnelt.
/// </summary>
internal static string StatusTest_EverythingOk {
get {
return ResourceManager.GetString("StatusTest_EverythingOk", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Checking for Updates... ähnelt.
/// </summary>
internal static string StatusText_CheckingForUpdates {
get {
return ResourceManager.GetString("StatusText_CheckingForUpdates", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Config incomplete or not loaded! ähnelt.
/// </summary>
internal static string StatusText_ConfigIncompleteOrNotLoaded {
get {
return ResourceManager.GetString("StatusText_ConfigIncompleteOrNotLoaded", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Error on update check or while updating! ähnelt.
/// </summary>
internal static string StatusText_ErrorWhileUpdateCheckOrUpdate {
get {
return ResourceManager.GetString("StatusText_ErrorWhileUpdateCheckOrUpdate", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Installing... ähnelt.
/// </summary>
internal static string StatusText_Installing {
get {
return ResourceManager.GetString("StatusText_Installing", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Downloading program update... ähnelt.
/// </summary>
internal static string StatusText_InstallingAppUpdate {
get {
return ResourceManager.GetString("StatusText_InstallingAppUpdate", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die The update servers are in maintenance. ähnelt.
/// </summary>
internal static string StatusText_Maintenance {
get {
return ResourceManager.GetString("StatusText_Maintenance", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Minecraft profile folder seems to be not valid. ähnelt.
/// </summary>
internal static string StatusText_MinecraftProfileWarning {
get {
return ResourceManager.GetString("StatusText_MinecraftProfileWarning", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die An update is available! ähnelt.
/// </summary>
internal static string StatusText_UpdateAvailable {
get {
return ResourceManager.GetString("StatusText_UpdateAvailable", resourceCulture);
}
}
}
}

View File

@@ -2,54 +2,26 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>icons8_download_from_ftp.ico</ApplicationIcon>
<AssemblyName>Minecraft Modpack Updater</AssemblyName>
<ImplicitUsings>true</ImplicitUsings>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>MinecraftModpackUpdaterCli</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Compile Update="FiledialogFilters.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>FiledialogFilters.resx</DependentUpon>
</Compile>
<Compile Update="LangRes.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>LangRes.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="FiledialogFilters.resx">
<Generator>ResXFileCodeGenerator</Generator>
<CustomToolNamespace>ModpackUpdater.My.Resources</CustomToolNamespace>
<LastGenOutput>FiledialogFilters.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="LangRes.resx">
<Generator>ResXFileCodeGenerator</Generator>
<CustomToolNamespace>ModpackUpdater.My.Resources</CustomToolNamespace>
<LastGenOutput>LangRes.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Include="..\Version.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pilz.Configuration" Version="3.1.3" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Pilz" Version="2.6.1" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
<PackageReference Include="Pilz.IO" Version="2.1.0" />
<PackageReference Include="Pilz.UI" Version="2.3.10" />
<PackageReference Include="Pilz.UI.Telerik" Version="2.7.4" />
<PackageReference Include="Pilz.Win32" Version="2.1.0" />
<PackageReference Include="UI.for.WinForms.Common" Version="2024.3.806" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModpackUpdater.Apps\ModpackUpdater.Apps.csproj" />
<ProjectReference Include="..\ModpackUpdater.Apps.AppUpdates\ModpackUpdater.Apps.AppUpdates.csproj" />
<ProjectReference Include="..\ModpackUpdater.Manager\ModpackUpdater.Manager.csproj" />
<ProjectReference Include="..\ModpackUpdater\ModpackUpdater.csproj" />
</ItemGroup>

View File

@@ -10,7 +10,8 @@ internal class Options
public IReadOnlyList<string> Additionals => additionals;
public bool Help { get; private set; }
public bool Silent { get; private set; }
public bool NoUi { get; private set; }
public string? RefTag { get; private set; }
public string? Version { get; private set; }
public UpdateCheckOptionsAdv UpdateOptions { get; } = new();
public Options(string[] args)
@@ -19,15 +20,15 @@ internal class Options
{
{ "silent", "Do not output anything.", s => Silent = true },
{ "h|help", "Writes the help text as output.", h => Help = true },
{ "n|noui", "Install without user interface.", n => NoUi = true },
{ "p|profile=", "Sets the minecraft profile folder.", p => UpdateOptions.ProfileFolder = p },
{ "c|config=", "Sets the modpack update info url.", c => UpdateOptions.ModpackConfig = c },
{ "s|side=", "Sets the installation side.\nDefault side is Client.\nAvailable: Client, Server", s => UpdateOptions.Side = Enum.Parse<Side>(s)},
{ "u|uai", "Disallow an update directly after install. This only has effect if there is no existing installation.", uai => UpdateOptions.AllowUpdaterAfterInstall = false},
{ "noupdate", "Skip the update check.", noupdate => UpdateOptions.NoUpdate = true},
{ "m|maintenance", "Ignores the maintenance mode.", m => UpdateOptions.IgnoreMaintenance = true},
{ "i|nonpublic", "Include non public (currently hidden) updates.", i => UpdateOptions.IncludeNonPublic = true},
{ "k|key=", "An key for retriving extra files on updates.", k => UpdateOptions.ExtrasKey = k},
{ "r|reftag=", "Force uses a specific reference tag, if supported.", r => RefTag = r},
{ "v|version=", "Force uses a specific version, if supported.", v => Version = v},
};
additionals.AddRange(options.Parse(args));

View File

@@ -1,93 +1,29 @@
using ModpackUpdater.Manager;
using Newtonsoft.Json;
using Pilz;
using Pilz.Configuration;
using System.Runtime.InteropServices;
[assembly: AssemblyAppVersion("1.6.2.0")]
using Castle.Core.Logging;
using ModpackUpdater.Manager;
namespace ModpackUpdater.Apps.Client;
public static class Program
{
private static readonly SettingsManager settingsManager;
private static readonly ILogger log = new ConsoleLogger();
public static ISettings Settings => settingsManager.Instance;
[DllImport("kernel32.dll")]
static extern nint GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(nint hWnd, int nCmdShow);
static Program()
{
settingsManager = new(GetSettingsPath(2), true);
MigrateLegacySettings(GetSettingsPath(null));
}
public static ILogger Log => log;
internal static Options Options { get; private set; } = null!;
[STAThread]
internal static void Main(string[] args)
{
var options = new Options(args);
if (options.Help)
options.DrawHelp();
else if (options.NoUi)
InstallWithoutGui(options.UpdateOptions, options.Silent);
Options = new Options(args);
if (Options.Help)
Options.DrawHelp();
else
{
ShowWindow(GetConsoleWindow(), 0);
RunApp(options.UpdateOptions);
}
}
private static void RunApp(UpdateCheckOptionsAdv updateOptions)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
AppGlobals.Initialize();
Application.Run(new Form1(updateOptions));
}
private static string GetSettingsPath(int? settingsVersion = 3)
{
const string AppDataDirectoryName = "MinecraftModpackUpdater";
var fileNamePostfix = settingsVersion == null ? string.Empty : $"V{settingsVersion}";
var SettingsFileName = $"Settings{fileNamePostfix}.json";
var settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), AppDataDirectoryName);
Directory.CreateDirectory(settingsPath);
settingsPath = Path.Combine(settingsPath, SettingsFileName);
return settingsPath;
}
private static void MigrateLegacySettings(string settingsPath)
{
// Try load legacy config file
if (!File.Exists(settingsPath) || JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText(settingsPath)) is not AppConfig legacyConfig)
return;
// Migrate
var newConfig = Settings.Get<AppConfig>();
newConfig.LastMinecraftProfilePath = legacyConfig.LastMinecraftProfilePath;
if (ModpackInfo.TryLoad(legacyConfig.LastMinecraftProfilePath) is ModpackInfo info)
#pragma warning disable CS0612 // Typ oder Element ist veraltet
info.ConfigUrl = legacyConfig.ConfigFilePath;
// Ensure save settings
settingsManager.Save();
// Delete legacy config file
File.Delete(settingsPath);
InstallWithoutGui(Options.UpdateOptions, Options.Silent);
}
private static void InstallWithoutGui(UpdateCheckOptionsAdv updateOptions, bool silent)
{
var info = ModpackInfo.TryLoad(updateOptions.ProfileFolder);
var config = ModpackConfig.LoadFromUrl(CheckModpackConfigUrl(updateOptions.ModpackConfig, info));
var config = ModpackConfig.LoadFromUrl(CheckModpackConfigUrl(updateOptions.ModpackConfig!, info));
var features = new ModpackFeatures(config);
// Check features
@@ -97,20 +33,22 @@ public static class Program
updateOptions.IncludeExtras = features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(info));
// Check for update
var installer = new ModpackInstaller(config, info);
var installer = new ModpackInstaller(config, info)
{
OverwriteRefTag = Options.RefTag,
OverwriteVersion = Options.Version,
Log = Log,
};
var result = installer.Check(updateOptions).Result;
if (!silent && !updateOptions.NoUpdate && new AppUpdater().Check().Result)
Console.WriteLine("A new version is available!");
if (result.HasUpdates)
{
var success = installer.Install(result).Result;
if (!silent)
Console.WriteLine($"Installation {(success ?? false ? "completed successfully" : "failed")}!");
Log.Info($"Installation {(success ?? false ? "completed successfully" : "failed")}!");
}
else if (!silent)
Console.WriteLine("No updates available");
Log.Info("No updates available");
}
private static string CheckModpackConfigUrl(string configUrl, ModpackInfo info)

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\publish\cli\linux-arm64</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>linux-arm64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\publish\cli\linux-x64</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\publish\cli\windows\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -4,8 +4,7 @@ namespace ModpackUpdater.Apps.Client;
public class UpdateCheckOptionsAdv : UpdateCheckOptions
{
public string ProfileFolder { get; set; }
public string ModpackConfig { get; set; }
public bool NoUpdate { get; set; }
public string ExtrasKey { get; set; }
public string? ProfileFolder { get; set; }
public string? ModpackConfig { get; set; }
public string? ExtrasKey { get; set; }
}

View File

@@ -0,0 +1,90 @@
# PUPNET DEPLOY: 1.9.1
# Use: 'pupnet --help conf' for information.
# APP PREAMBLE
AppBaseName = MinecraftModpackUpdaterCli
AppFriendlyName = Minecraft Modpack Updater CLI
AppId = de.pilzinsel64.minecraft-modpack-updater-cli
AppVersionRelease = 1.0.0[1]
AppShortSummary = Install and Update Minecraft Modpacks easliy.
AppDescription = """
Minecraft Modpack Updater is a simple tool to install and update a modpack to a selected minecraft profile by a selected modpack configuration file. It downloads a config file via https and checks the version there and what files has been changed and download the updateded files via a given link from the config.
"""
AppLicenseId = MIT
AppLicenseFile = ../LICENSE
AppChangeFile =
# PUBLISHER
PublisherName = Pilzinsel64
PublisherId = de.pilzinsel64
PublisherCopyright = Copyright (C) Pilzinsel64 2025
PublisherLinkName = Pilzinsel64 Homepage
PublisherLinkUrl = https://pilzinsel64.de
PublisherEmail =
# DESKTOP INTEGRATION
DesktopNoDisplay = false
DesktopTerminal = false
DesktopFile =
StartCommand =
PrimeCategory =
MetaFile =
IconFiles = """
"""
# DOTNET PUBLISH
DotnetProjectPath = ModpackUpdater.Apps.Client.csproj
DotnetPublishArgs = -p:Version=${APP_VERSION} --self-contained true -p:DebugType=None -p:DebugSymbols=false -p:PublishSingleFile=true -p:PublishTrimmed=false
DotnetPostPublish =
DotnetPostPublishOnWindows =
# PACKAGE OUTPUT
PackageName = minecraft-modpack-updater-cli
OutputDirectory = ../publish/client-cli
# APPIMAGE OPTIONS
AppImageArgs =
AppImageRuntimePath =
AppImageVersionOutput = false
# FLATPAK OPTIONS
FlatpakPlatformRuntime = org.freedesktop.Platform
FlatpakPlatformSdk = org.freedesktop.Sdk
FlatpakPlatformVersion = 25.08
FlatpakFinishArgs = """
--socket=wayland
--socket=fallback-x11
--filesystem=home
--share=network
"""
FlatpakBuilderArgs =
# RPM OPTIONS
RpmAutoReq = false
RpmAutoProv = true
RpmRequires = """
krb5-libs
libicu
openssl-libs
"""
# DEBIAN OPTIONS
DebianRecommends = """
libc6
libgcc1
libgssapi-krb5-2
libicu70
libssl3
libstdc++6
zlib1g
"""
# WINDOWS SETUP OPTIONS
SetupGroupName =
SetupAdminInstall = false
SetupCommandPrompt =
SetupMinWindowsVersion = 10
SetupSignTool =
SetupSuffixOutput =
SetupVersionOutput = false
SetupUninstallScript =

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -0,0 +1,12 @@
pupnet -y -v "$1[1]" -r linux-x64 -k appimage
#pupnet -y -v "$1[1]" -r linux-x64 -k flatpak
#pupnet -y -v "$1[1]"-r linux-x64 -k deb
#pupnet -y -v "$1[1]"-r linux-x64 -k rpm
pupnet -y -v "$1[1]" -r linux-arm64 -k appimage
#pupnet -y -v "$1[1]" -r linux-arm64 -k flatpak
#pupnet -y -v "$1[1]"-r linux-arm64 -k deb
#pupnet -y -v "$1[1]"-r linux-arm64 -k rpm
pupnet -y -v "$1[1]" -r win-x64 -k zip
pupnet -y -v "$1[1]" -r win-arm64 -k zip
pupnet -y -v "$1[1]" -r osx-x64 -k zip
pupnet -y -v "$1[1]" -r osx-arm64 -k zip

View File

@@ -1,5 +1,5 @@
using ModpackUpdater.Apps.Manager.Api.Model;
using Pilz.Plugins.Advanced;
using Pilz.Features;
namespace ModpackUpdater.Apps.Manager.Api.Plugins.Features;

View File

@@ -1,5 +1,5 @@
using ModpackUpdater.Apps.Manager.Api.Model;
using Pilz.Plugins.Advanced;
using Pilz.Features;
namespace ModpackUpdater.Apps.Manager.Api.Plugins.Params;

View File

@@ -1,7 +1,7 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Manager.Features.CM;

View File

@@ -1,7 +1,7 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Manager.Features.CM;

View File

@@ -1,7 +1,7 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Pilz.UI.Symbols;
using Telerik.WinControls.UI;

View File

@@ -1,7 +1,7 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Manager.Features.CM;

View File

@@ -1,8 +1,9 @@
using Microsoft.Extensions.Primitives;
using ModpackUpdater.Apps.Manager.Api.Model;
using ModpackUpdater.Apps.Manager.Api.Model;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using ModpackUpdater.Manager;
using Pilz.UI.Extensions;
using OfficeOpenXml;
using Pilz.UI.WinForms.Extensions;
using System.Text;
using Telerik.WinControls.UI;
@@ -122,11 +123,18 @@ internal static class SharedFunctions
foreach (var action in actions)
{
if (action.SourceType != SourceType.DirectLink)
{
try
{
Task.Run(async () =>
{
action.SourceUrl = await factory.ResolveSourceUrl(action);
}).Wait();
}
catch (Exception)
{
// Fail silently
}
api.UpdateItem(action);
}
}
@@ -202,4 +210,100 @@ internal static class SharedFunctions
return log.ToString().TrimEnd();
}
public static string GenerateModlistAsMarkdown(InstallInfos installInfos)
{
var sb = new StringBuilder();
sb.Append("|" + ActionsListLangRes.Col_Name);
sb.Append("|" + ActionsListLangRes.Col_SrcTag);
sb.Append("|" + ActionsListLangRes.Col_Side);
sb.Append("|" + ActionsListLangRes.Col_SrcType);
sb.Append("|" + ActionsListLangRes.Col_SrcOwner);
sb.Append("|" + ActionsListLangRes.Col_SrcName);
sb.AppendLine("|");
sb.AppendLine("|---|---|---|---|---|---|");
// Rows
foreach (var action in installInfos.Actions.OrderBy(n => n.Name))
{
if (action.IsExtra || action.IsZip || string.IsNullOrWhiteSpace(action.Id) || !action.Id.StartsWith("mod:"))
continue;
if (string.IsNullOrWhiteSpace(action.Website))
sb.Append($"|{action.Name}");
else
sb.Append($"|[{action.Name}]({action.Website})");
if (string.IsNullOrWhiteSpace(action.SourceUrl))
sb.Append($"|{action.SourceTag}");
else
sb.Append($"|[{action.SourceTag}]({action.GetSourceUrl(installInfos.Version)})");
sb.Append($"|{action.Side.ToString()}");
sb.Append($"|{action.SourceType}");
sb.Append($"|{action.SourceOwner}");
sb.Append($"|{action.SourceName}");
sb.AppendLine("|");
}
return sb.ToString().TrimEnd();
}
public static ExcelPackage? GenerateModlistAsExcel(InstallInfos installInfos)
{
var pkg = new ExcelPackage();
var ws = pkg.Workbook.Worksheets.Add(string.Format(GeneralLangRes.Text_ModlistForVersion, installInfos.Version));
var cr = 1;
var cc = 1;
// Header
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_Name;
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_SrcTag;
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_Side;
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_SrcType;
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_SrcOwner;
ws.Cells[cr, cc++].Value = ActionsListLangRes.Col_SrcName;
cr += 1;
cc = 1;
// Rows
foreach (var action in installInfos.Actions.OrderBy(n => n.Name))
{
if (action.IsExtra || action.IsZip || string.IsNullOrWhiteSpace(action.Id) || !action.Id.StartsWith("mod:"))
continue;
var cellName = ws.Cells[cr, cc++];
cellName.Value = action.Name;
if (!string.IsNullOrWhiteSpace(action.Website))
cellName.SetHyperlink(new Uri(action.Website));
var cellTag = ws.Cells[cr, cc++];
cellTag.Value = string.IsNullOrWhiteSpace(action.SourceTag) ? "direct link" : action.SourceTag;
if (!string.IsNullOrWhiteSpace(action.SourceUrl))
cellTag.SetHyperlink(new Uri(action.GetSourceUrl(installInfos.Version)));
ws.Cells[cr, cc++].Value = action.Side.ToString();
ws.Cells[cr, cc++].Value = action.SourceType;
ws.Cells[cr, cc++].Value = action.SourceOwner;
ws.Cells[cr, cc++].Value = action.SourceName;
cr += 1;
cc = 1;
}
// Styling
cc = 1;
ws.Column(cc++).Width = 30;
ws.Column(cc++).Width = 20;
ws.Column(cc++).Width = 10;
ws.Column(cc++).Width = 20;
ws.Column(cc++).Width = 20;
ws.Column(cc++).Width = 30;
var tableDef = ws.Tables.Add(ws.Cells[1, 1, cr - 1, cc - 1], "Table");
tableDef.TableStyle = OfficeOpenXml.Table.TableStyles.Medium16;
tableDef.ShowHeader = true;
return pkg;
}
}

View File

@@ -1,7 +1,7 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Ui;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Manager.Features.Tools;

View File

@@ -1,6 +1,6 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Plugins.Advanced;
using Pilz.Features;
namespace ModpackUpdater.Apps.Manager.Features.Tools;

View File

@@ -1,6 +1,6 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Telerik.WinControls;
namespace ModpackUpdater.Apps.Manager.Features.Tools;

View File

@@ -0,0 +1,39 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Features;
using Telerik.WinControls;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Manager.Features.Tools;
internal class GenerateModlistAsExcelFeature : PluginFunction, IPluginFeatureProvider<GenerateModlistAsExcelFeature>
{
public static GenerateModlistAsExcelFeature Instance { get; } = new();
public GenerateModlistAsExcelFeature() : base(FeatureTypes.Tools, "origin.genmodlist.xlsx", FeatureNamesLangRes.GenerateModlistAsExcelFeature)
{
Icon = AppGlobals.Symbols.GetSvgImage(AppSymbols.list_view, Pilz.UI.Symbols.SymbolSize.Small);
}
protected override object? ExecuteFunction(PluginFunctionParameter? @params)
{
if (@params is not MainApiParameters p || p.Api.CurWorkspace?.InstallInfos is null || p.Api.CurWorkspace?.InstallInfos is null)
return null;
using var pkg = SharedFunctions.GenerateModlistAsExcel(p.Api.CurWorkspace.InstallInfos);
if (pkg is null)
return null;
using var sfd = new RadSaveFileDialog
{
Filter = "*.xlsx|*.xlsx|*|*"
};
if (sfd.ShowDialog(p.Api.MainWindow) == DialogResult.OK)
{
pkg.SaveAs(sfd.FileName);
RadMessageBox.Show(p.Api.MainWindow, MsgBoxLangRes.ModlistGenerated, MsgBoxLangRes.ModlistGenerated_Title, MessageBoxButtons.OK, RadMessageIcon.Info);
}
return null;
}
}

View File

@@ -0,0 +1,27 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Features;
using Telerik.WinControls;
namespace ModpackUpdater.Apps.Manager.Features.Tools;
internal class GenerateModlistAsMarkdownFeature : PluginFunction, IPluginFeatureProvider<GenerateModlistAsMarkdownFeature>
{
public static GenerateModlistAsMarkdownFeature Instance { get; } = new();
public GenerateModlistAsMarkdownFeature() : base(FeatureTypes.Tools, "origin.genmodlist.md", FeatureNamesLangRes.GenerateModlistAsMarkdownFeature)
{
Icon = AppGlobals.Symbols.GetSvgImage(AppSymbols.list_view, Pilz.UI.Symbols.SymbolSize.Small);
}
protected override object? ExecuteFunction(PluginFunctionParameter? @params)
{
if (@params is not MainApiParameters p || p.Api.CurWorkspace?.InstallInfos is null || p.Api.CurWorkspace?.InstallInfos is null)
return null;
Clipboard.SetText(SharedFunctions.GenerateModlistAsMarkdown(p.Api.CurWorkspace.InstallInfos));
RadMessageBox.Show(p.Api.MainWindow, MsgBoxLangRes.ModlistCopiedToClipboard, MsgBoxLangRes.ModlistCopiedToClipboard_Title, MessageBoxButtons.OK, RadMessageIcon.Info);
return null;
}
}

View File

@@ -1,6 +1,6 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Plugins.Advanced;
using Pilz.Features;
namespace ModpackUpdater.Apps.Manager.Features.Tools;

View File

@@ -1,6 +1,6 @@
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Plugins.Advanced;
using Pilz.Features;
using Pilz.UI.Symbols;
namespace ModpackUpdater.Apps.Manager.Features.Tools;

View File

@@ -1,7 +1,6 @@
using ModpackUpdater.Apps.Manager.Api.Model;
using NGitLab;
using NGitLab.Models;
using System.Text.Encodings.Web;
namespace ModpackUpdater.Apps.Manager.Features.Workspaces.GitLabRepo;

View File

@@ -1,5 +1,5 @@
using Pilz.UI;
using Pilz.UI.Telerik.Dialogs;
using Pilz.UI.WinForms.Telerik.Dialogs;
namespace ModpackUpdater.Apps.Manager.Features.Workspaces.GitLabRepo;

View File

@@ -1,10 +1,10 @@
using ModpackUpdater.Apps.Manager.Api.Model;
using ModpackUpdater.Apps.Manager.Api.Plugins.Features;
using ModpackUpdater.Apps.Manager.LangRes;
using Pilz.Plugins.Advanced;
using Pilz.UI.Extensions;
using Pilz.Features;
using Pilz.UI.Symbols;
using Pilz.UI.Telerik.Dialogs;
using Pilz.UI.WinForms.Extensions;
using Pilz.UI.WinForms.Telerik.Dialogs;
namespace ModpackUpdater.Apps.Manager.Features.Workspaces.GitLabRepo;

View File

@@ -105,6 +105,24 @@ namespace ModpackUpdater.Apps.Manager.LangRes {
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Generate modlist excel file ähnelt.
/// </summary>
internal static string GenerateModlistAsExcelFeature {
get {
return ResourceManager.GetString("GenerateModlistAsExcelFeature", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Generate modlist markdown table ähnelt.
/// </summary>
internal static string GenerateModlistAsMarkdownFeature {
get {
return ResourceManager.GetString("GenerateModlistAsMarkdownFeature", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die GitLab workspace ähnelt.
/// </summary>

View File

@@ -132,6 +132,12 @@
<data name="GenerateChangelogFeature" xml:space="preserve">
<value>Generate changelog</value>
</data>
<data name="GenerateModlistAsExcelFeature" xml:space="preserve">
<value>Generate modlist excel file</value>
</data>
<data name="GenerateModlistAsMarkdownFeature" xml:space="preserve">
<value>Generate modlist markdown table</value>
</data>
<data name="GitLabWorkspace" xml:space="preserve">
<value>GitLab workspace</value>
</data>

View File

@@ -77,5 +77,14 @@ namespace ModpackUpdater.Apps.Manager.LangRes {
return ResourceManager.GetString("Node_Update", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Modlist for v{0} ähnelt.
/// </summary>
internal static string Text_ModlistForVersion {
get {
return ResourceManager.GetString("Text_ModlistForVersion", resourceCulture);
}
}
}
}

View File

@@ -123,4 +123,7 @@
<data name="Node_Update" xml:space="preserve">
<value>Update: {0}</value>
</data>
<data name="Text_ModlistForVersion" xml:space="preserve">
<value>Modlist for v{0}</value>
</data>
</root>

View File

@@ -78,6 +78,42 @@ namespace ModpackUpdater.Apps.Manager.LangRes {
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die The modlist has been generated and copied to the clipboard. ähnelt.
/// </summary>
internal static string ModlistCopiedToClipboard {
get {
return ResourceManager.GetString("ModlistCopiedToClipboard", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Modlist generated successfully ähnelt.
/// </summary>
internal static string ModlistCopiedToClipboard_Title {
get {
return ResourceManager.GetString("ModlistCopiedToClipboard_Title", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die The modlist has been generated successfully and saved to the selected location. ähnelt.
/// </summary>
internal static string ModlistGenerated {
get {
return ResourceManager.GetString("ModlistGenerated", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Modlist generated successfully ähnelt.
/// </summary>
internal static string ModlistGenerated_Title {
get {
return ResourceManager.GetString("ModlistGenerated_Title", resourceCulture);
}
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Are you sure that you want to delete this update? ähnelt.
/// </summary>

View File

@@ -123,6 +123,18 @@
<data name="ChangelogCopiedToClipboard_Title" xml:space="preserve">
<value>Changelog generated successfully</value>
</data>
<data name="ModlistCopiedToClipboard" xml:space="preserve">
<value>The modlist has been generated and copied to the clipboard.</value>
</data>
<data name="ModlistCopiedToClipboard_Title" xml:space="preserve">
<value>Modlist generated successfully</value>
</data>
<data name="ModlistGenerated" xml:space="preserve">
<value>The modlist has been generated successfully and saved to the selected location.</value>
</data>
<data name="ModlistGenerated_Title" xml:space="preserve">
<value>Modlist generated successfully</value>
</data>
<data name="RemoveUpdate" xml:space="preserve">
<value>Are you sure that you want to delete this update?</value>
</data>

View File

@@ -11,15 +11,17 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NGitLab" Version="6.55.0" />
<PackageReference Include="Pilz.Configuration" Version="3.1.3" />
<PackageReference Include="Pilz.Plugins.Advanced" Version="2.10.1" />
<PackageReference Include="Pilz.Plugins.Advanced.UI" Version="1.7.0" />
<PackageReference Include="Pilz.Plugins.Advanced.UI.Telerik" Version="1.7.0" />
<PackageReference Include="Pilz.UI" Version="2.3.10" />
<PackageReference Include="Pilz.UI.Telerik" Version="2.7.4" />
<PackageReference Include="UI.for.WinForms.Common" Version="2024.3.806" />
<PackageReference Include="UI.for.WinForms.GridView" Version="2024.3.806" />
<PackageReference Include="EPPlus" Version="8.2.1" />
<PackageReference Include="NGitLab" Version="11.0.0" />
<PackageReference Include="Pilz.Configuration" Version="3.2.7" />
<PackageReference Include="Pilz.UI" Version="3.1.1" />
<PackageReference Include="Pilz.UI.WinForms" Version="2.7.0" />
<PackageReference Include="Pilz.UI.WinForms.Telerik" Version="2.14.3" />
<PackageReference Include="Pilz.UI.WinForms.Telerik.Features" Version="1.9.0" />
<PackageReference Include="Pilz.UI.WinForms.Telerik.Symbols" Version="1.2.1" />
<PackageReference Include="UI.for.WinForms.Common" Version="2025.3.812" />
<PackageReference Include="UI.for.WinForms.GridView" Version="2025.3.812" />
<PackageReference Include="UI.for.WinForms.Themes" Version="2025.3.812" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,8 +1,6 @@
using Pilz;
using OfficeOpenXml;
using Pilz.Configuration;
using Pilz.Plugins.Advanced;
[assembly: AssemblyAppVersion("1.0.0.0")]
using Pilz.Features;
namespace ModpackUpdater.Apps.Manager;
@@ -14,6 +12,7 @@ public static class Program
static Program()
{
ExcelPackage.License.SetNonCommercialPersonal("Pilzinsel64");
settingsManager = new(GetSettingsPath(), true);
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>H:\Applications\Minecraft Modpack Manager</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0-windows</TargetFramework>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -29,46 +29,53 @@ partial class MainForm
/// </summary>
private void InitializeComponent()
{
var tableViewDefinition1 = new Telerik.WinControls.UI.TableViewDefinition();
var tableViewDefinition2 = new Telerik.WinControls.UI.TableViewDefinition();
var resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
radSplitContainer1 = new Telerik.WinControls.UI.RadSplitContainer();
splitPanel1 = new Telerik.WinControls.UI.SplitPanel();
tableLayoutPanel2 = new TableLayoutPanel();
radListControl_Updates = new Telerik.WinControls.UI.RadListControl();
radTreeView_Sets = new Telerik.WinControls.UI.RadTreeView();
splitPanel2 = new Telerik.WinControls.UI.SplitPanel();
tableLayoutPanel1 = new TableLayoutPanel();
tableLayoutPanel_ActionSet = new TableLayoutPanel();
radGridView_Actions = new Telerik.WinControls.UI.RadGridView();
tableLayoutPanel_ActionSetInfo = new TableLayoutPanel();
radLabel1 = new Telerik.WinControls.UI.RadLabel();
radTextBoxControl_Version = new Telerik.WinControls.UI.RadTextBoxControl();
radCheckBox_IsPublic = new Telerik.WinControls.UI.RadCheckBox();
radMenuItem_Workspace = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_WorkspacePreferences = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_SaveWorkspace = new Telerik.WinControls.UI.RadMenuItem();
radMenuSeparatorItem1 = new Telerik.WinControls.UI.RadMenuSeparatorItem();
radMenuItem_OpenNewWorkspace = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_RecentWorkspaces = new Telerik.WinControls.UI.RadMenuItem();
radMenuHeaderItem_NewWorkspace = new Telerik.WinControls.UI.RadMenuHeaderItem();
radMenuSeparatorItem3 = new Telerik.WinControls.UI.RadMenuSeparatorItem();
radMenuHeaderItem_RecentWorkspaces = new Telerik.WinControls.UI.RadMenuHeaderItem();
radMenuItem_Tools = new Telerik.WinControls.UI.RadMenuItem();
radMenu1 = new Telerik.WinControls.UI.RadMenu();
radMenuItem_Updates = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_EditUpdate = new Telerik.WinControls.UI.RadMenuItem();
radMenuSeparatorItem2 = new Telerik.WinControls.UI.RadMenuSeparatorItem();
radMenuItem_CreateUpdate = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_RemoveUpdate = new Telerik.WinControls.UI.RadMenuItem();
radWaitingBar_Updates = new Telerik.WinControls.UI.RadWaitingBar();
dotsRingWaitingBarIndicatorElement1 = new Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement();
radWaitingBar_Actions = new Telerik.WinControls.UI.RadWaitingBar();
dotsRingWaitingBarIndicatorElement2 = new Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement();
radMenu1 = new Telerik.WinControls.UI.RadMenu();
((System.ComponentModel.ISupportInitialize)radSplitContainer1).BeginInit();
radSplitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitPanel1).BeginInit();
splitPanel1.SuspendLayout();
tableLayoutPanel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radListControl_Updates).BeginInit();
((System.ComponentModel.ISupportInitialize)radTreeView_Sets).BeginInit();
((System.ComponentModel.ISupportInitialize)splitPanel2).BeginInit();
splitPanel2.SuspendLayout();
tableLayoutPanel1.SuspendLayout();
tableLayoutPanel_ActionSet.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radGridView_Actions).BeginInit();
((System.ComponentModel.ISupportInitialize)radGridView_Actions.MasterTemplate).BeginInit();
((System.ComponentModel.ISupportInitialize)radMenu1).BeginInit();
tableLayoutPanel_ActionSetInfo.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radLabel1).BeginInit();
((System.ComponentModel.ISupportInitialize)radTextBoxControl_Version).BeginInit();
((System.ComponentModel.ISupportInitialize)radCheckBox_IsPublic).BeginInit();
((System.ComponentModel.ISupportInitialize)radWaitingBar_Updates).BeginInit();
((System.ComponentModel.ISupportInitialize)radWaitingBar_Actions).BeginInit();
((System.ComponentModel.ISupportInitialize)radMenu1).BeginInit();
((System.ComponentModel.ISupportInitialize)this).BeginInit();
SuspendLayout();
//
@@ -79,10 +86,6 @@ partial class MainForm
radSplitContainer1.Dock = DockStyle.Fill;
radSplitContainer1.Location = new Point(0, 28);
radSplitContainer1.Name = "radSplitContainer1";
//
//
//
radSplitContainer1.RootElement.MinSize = new Size(25, 25);
radSplitContainer1.Size = new Size(800, 422);
radSplitContainer1.TabIndex = 0;
radSplitContainer1.TabStop = false;
@@ -92,14 +95,11 @@ partial class MainForm
splitPanel1.Controls.Add(tableLayoutPanel2);
splitPanel1.Location = new Point(0, 0);
splitPanel1.Name = "splitPanel1";
//
//
//
splitPanel1.RootElement.MinSize = new Size(25, 25);
splitPanel1.Size = new Size(200, 422);
splitPanel1.Size = new Size(232, 422);
splitPanel1.SizeInfo.AbsoluteSize = new Size(232, 200);
splitPanel1.SizeInfo.AutoSizeScale = new SizeF(-0.190954775F, 0F);
splitPanel1.SizeInfo.SizeMode = Telerik.WinControls.UI.Docking.SplitPanelSizeMode.Absolute;
splitPanel1.SizeInfo.SplitterCorrection = new Size(-152, 0);
splitPanel1.SizeInfo.SplitterCorrection = new Size(-120, 0);
splitPanel1.TabIndex = 0;
splitPanel1.TabStop = false;
//
@@ -107,57 +107,57 @@ partial class MainForm
//
tableLayoutPanel2.ColumnCount = 1;
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel2.Controls.Add(radListControl_Updates, 0, 0);
tableLayoutPanel2.Controls.Add(radTreeView_Sets, 0, 0);
tableLayoutPanel2.Dock = DockStyle.Fill;
tableLayoutPanel2.Location = new Point(0, 0);
tableLayoutPanel2.Name = "tableLayoutPanel2";
tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
tableLayoutPanel2.Size = new Size(200, 422);
tableLayoutPanel2.Size = new Size(232, 422);
tableLayoutPanel2.TabIndex = 1;
//
// radListControl_Updates
// radTreeView_Sets
//
radListControl_Updates.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
radListControl_Updates.ItemHeight = 24;
radListControl_Updates.Location = new Point(3, 3);
radListControl_Updates.Name = "radListControl_Updates";
radListControl_Updates.Size = new Size(194, 416);
radListControl_Updates.TabIndex = 0;
radListControl_Updates.SelectedIndexChanged += RadListControl_Updates_SelectedIndexChanged;
radTreeView_Sets.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
radTreeView_Sets.ItemHeight = 24;
radTreeView_Sets.LineColor = Color.FromArgb(152, 152, 152);
radTreeView_Sets.LineStyle = Telerik.WinControls.UI.TreeLineStyle.Solid;
radTreeView_Sets.Location = new Point(3, 3);
radTreeView_Sets.Name = "radTreeView_Sets";
radTreeView_Sets.Size = new Size(226, 416);
radTreeView_Sets.TabIndex = 0;
radTreeView_Sets.SelectedNodeChanged += RadTreeView_Sets_SelectedNodeChanged;
//
// splitPanel2
//
splitPanel2.Controls.Add(tableLayoutPanel1);
splitPanel2.Location = new Point(204, 0);
splitPanel2.Controls.Add(tableLayoutPanel_ActionSet);
splitPanel2.Location = new Point(236, 0);
splitPanel2.Name = "splitPanel2";
//
//
//
splitPanel2.RootElement.MinSize = new Size(25, 25);
splitPanel2.Size = new Size(596, 422);
splitPanel2.SizeInfo.AutoSizeScale = new SizeF(0.190954745F, 0F);
splitPanel2.SizeInfo.SplitterCorrection = new Size(152, 0);
splitPanel2.Size = new Size(564, 422);
splitPanel2.SizeInfo.AutoSizeScale = new SizeF(-0.0600000024F, 0F);
splitPanel2.SizeInfo.SplitterCorrection = new Size(120, 0);
splitPanel2.TabIndex = 1;
splitPanel2.TabStop = false;
//
// tableLayoutPanel1
// tableLayoutPanel_ActionSet
//
tableLayoutPanel1.ColumnCount = 1;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel1.Controls.Add(radGridView_Actions, 0, 0);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(0, 0);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 1;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
tableLayoutPanel1.Size = new Size(596, 422);
tableLayoutPanel1.TabIndex = 0;
tableLayoutPanel_ActionSet.ColumnCount = 1;
tableLayoutPanel_ActionSet.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel_ActionSet.Controls.Add(radGridView_Actions, 0, 1);
tableLayoutPanel_ActionSet.Controls.Add(tableLayoutPanel_ActionSetInfo, 0, 0);
tableLayoutPanel_ActionSet.Dock = DockStyle.Fill;
tableLayoutPanel_ActionSet.Location = new Point(0, 0);
tableLayoutPanel_ActionSet.Name = "tableLayoutPanel_ActionSet";
tableLayoutPanel_ActionSet.RowCount = 2;
tableLayoutPanel_ActionSet.RowStyles.Add(new RowStyle());
tableLayoutPanel_ActionSet.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel_ActionSet.Size = new Size(564, 422);
tableLayoutPanel_ActionSet.TabIndex = 0;
//
// radGridView_Actions
//
radGridView_Actions.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
radGridView_Actions.Location = new Point(3, 3);
radGridView_Actions.Location = new Point(3, 33);
//
//
//
@@ -167,9 +167,9 @@ partial class MainForm
radGridView_Actions.MasterTemplate.AllowRowResize = false;
radGridView_Actions.MasterTemplate.AllowSearchRow = true;
radGridView_Actions.MasterTemplate.EnableGrouping = false;
radGridView_Actions.MasterTemplate.ViewDefinition = tableViewDefinition1;
radGridView_Actions.MasterTemplate.ViewDefinition = tableViewDefinition2;
radGridView_Actions.Name = "radGridView_Actions";
radGridView_Actions.Size = new Size(590, 416);
radGridView_Actions.Size = new Size(558, 386);
radGridView_Actions.TabIndex = 0;
radGridView_Actions.CellFormatting += RadGridView_Actions_CellFormatting;
radGridView_Actions.UserAddedRow += RadGridView_Actions_UserAddedRow;
@@ -177,9 +177,58 @@ partial class MainForm
radGridView_Actions.CellValueChanged += RadGridView_Actions_CellValueChanged;
radGridView_Actions.ContextMenuOpening += RadGridView_Actions_ContextMenuOpening;
//
// tableLayoutPanel_ActionSetInfo
//
tableLayoutPanel_ActionSetInfo.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel_ActionSetInfo.AutoSize = true;
tableLayoutPanel_ActionSetInfo.ColumnCount = 3;
tableLayoutPanel_ActionSetInfo.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel_ActionSetInfo.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel_ActionSetInfo.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel_ActionSetInfo.Controls.Add(radLabel1, 0, 0);
tableLayoutPanel_ActionSetInfo.Controls.Add(radTextBoxControl_Version, 1, 0);
tableLayoutPanel_ActionSetInfo.Controls.Add(radCheckBox_IsPublic, 2, 0);
tableLayoutPanel_ActionSetInfo.Location = new Point(0, 0);
tableLayoutPanel_ActionSetInfo.Margin = new Padding(0);
tableLayoutPanel_ActionSetInfo.Name = "tableLayoutPanel_ActionSetInfo";
tableLayoutPanel_ActionSetInfo.RowCount = 1;
tableLayoutPanel_ActionSetInfo.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel_ActionSetInfo.Size = new Size(564, 30);
tableLayoutPanel_ActionSetInfo.TabIndex = 1;
//
// radLabel1
//
radLabel1.Anchor = AnchorStyles.Left;
radLabel1.Location = new Point(3, 4);
radLabel1.Name = "radLabel1";
radLabel1.Size = new Size(58, 22);
radLabel1.TabIndex = 0;
radLabel1.Text = "Version:";
//
// radTextBoxControl_Version
//
radTextBoxControl_Version.Anchor = AnchorStyles.Left;
radTextBoxControl_Version.AutoSize = true;
radTextBoxControl_Version.Location = new Point(67, 3);
radTextBoxControl_Version.Name = "radTextBoxControl_Version";
radTextBoxControl_Version.NullText = "e.g. 1.2.5.0";
radTextBoxControl_Version.Size = new Size(150, 24);
radTextBoxControl_Version.TabIndex = 1;
radTextBoxControl_Version.TextChanged += RadTextBoxControl1_TextChanged;
//
// radCheckBox_IsPublic
//
radCheckBox_IsPublic.Anchor = AnchorStyles.Left;
radCheckBox_IsPublic.Location = new Point(223, 5);
radCheckBox_IsPublic.Name = "radCheckBox_IsPublic";
radCheckBox_IsPublic.Size = new Size(76, 20);
radCheckBox_IsPublic.TabIndex = 2;
radCheckBox_IsPublic.Text = "Is public";
radCheckBox_IsPublic.ToggleStateChanged += RadCheckBox1_ToggleStateChanged;
//
// radMenuItem_Workspace
//
radMenuItem_Workspace.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_WorkspacePreferences, radMenuItem_SaveWorkspace, radMenuSeparatorItem1, radMenuItem_OpenNewWorkspace, radMenuItem_RecentWorkspaces });
radMenuItem_Workspace.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_WorkspacePreferences, radMenuItem_SaveWorkspace, radMenuSeparatorItem1, radMenuHeaderItem_NewWorkspace, radMenuSeparatorItem3, radMenuHeaderItem_RecentWorkspaces });
radMenuItem_Workspace.Name = "radMenuItem_Workspace";
radMenuItem_Workspace.Text = "Workspace";
//
@@ -201,48 +250,34 @@ partial class MainForm
radMenuSeparatorItem1.Text = "radMenuSeparatorItem1";
radMenuSeparatorItem1.TextAlignment = ContentAlignment.MiddleLeft;
//
// radMenuItem_OpenNewWorkspace
// radMenuHeaderItem_NewWorkspace
//
radMenuItem_OpenNewWorkspace.Name = "radMenuItem_OpenNewWorkspace";
radMenuItem_OpenNewWorkspace.Text = "Open new workspace";
radMenuHeaderItem_NewWorkspace.Name = "radMenuHeaderItem_NewWorkspace";
radMenuHeaderItem_NewWorkspace.Text = "New workspace";
//
// radMenuItem_RecentWorkspaces
// radMenuSeparatorItem3
//
radMenuItem_RecentWorkspaces.Name = "radMenuItem_RecentWorkspaces";
radMenuItem_RecentWorkspaces.Text = "Recent workspaces";
radMenuSeparatorItem3.Name = "radMenuSeparatorItem3";
radMenuSeparatorItem3.Text = "radMenuSeparatorItem3";
radMenuSeparatorItem3.TextAlignment = ContentAlignment.MiddleLeft;
//
// radMenuHeaderItem_RecentWorkspaces
//
radMenuHeaderItem_RecentWorkspaces.Name = "radMenuHeaderItem_RecentWorkspaces";
radMenuHeaderItem_RecentWorkspaces.Text = "Recent workspaces";
//
// radMenuItem_Tools
//
radMenuItem_Tools.Name = "radMenuItem_Tools";
radMenuItem_Tools.Text = "Tools";
//
// radMenu1
//
radMenu1.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_Workspace, radMenuItem_Updates, radMenuItem_Tools });
radMenu1.Location = new Point(0, 0);
radMenu1.Name = "radMenu1";
radMenu1.Size = new Size(800, 28);
radMenu1.TabIndex = 1;
//
// radMenuItem_Updates
//
radMenuItem_Updates.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_EditUpdate, radMenuSeparatorItem2, radMenuItem_CreateUpdate, radMenuItem_RemoveUpdate });
radMenuItem_Updates.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_CreateUpdate, radMenuItem_RemoveUpdate });
radMenuItem_Updates.Name = "radMenuItem_Updates";
radMenuItem_Updates.Text = "Updates";
radMenuItem_Updates.DropDownOpening += RadMenuItem_Updates_DropDownOpening;
//
// radMenuItem_EditUpdate
//
radMenuItem_EditUpdate.Name = "radMenuItem_EditUpdate";
radMenuItem_EditUpdate.Text = "Edit";
radMenuItem_EditUpdate.Click += RadMenuItem_EditUpdate_Click;
//
// radMenuSeparatorItem2
//
radMenuSeparatorItem2.Name = "radMenuSeparatorItem2";
radMenuSeparatorItem2.Text = "radMenuSeparatorItem2";
radMenuSeparatorItem2.TextAlignment = ContentAlignment.MiddleLeft;
//
// radMenuItem_CreateUpdate
//
radMenuItem_CreateUpdate.Name = "radMenuItem_CreateUpdate";
@@ -257,7 +292,7 @@ partial class MainForm
//
// radWaitingBar_Updates
//
radWaitingBar_Updates.AssociatedControl = radListControl_Updates;
radWaitingBar_Updates.AssociatedControl = radTreeView_Sets;
radWaitingBar_Updates.Location = new Point(0, 78);
radWaitingBar_Updates.Name = "radWaitingBar_Updates";
radWaitingBar_Updates.Size = new Size(70, 70);
@@ -289,6 +324,14 @@ partial class MainForm
//
dotsRingWaitingBarIndicatorElement2.Name = "dotsRingWaitingBarIndicatorElement2";
//
// radMenu1
//
radMenu1.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_Workspace, radMenuItem_Updates, radMenuItem_Tools });
radMenu1.Location = new Point(0, 0);
radMenu1.Name = "radMenu1";
radMenu1.Size = new Size(800, 28);
radMenu1.TabIndex = 1;
//
// MainForm
//
AutoScaleBaseSize = new Size(7, 15);
@@ -310,15 +353,21 @@ partial class MainForm
((System.ComponentModel.ISupportInitialize)splitPanel1).EndInit();
splitPanel1.ResumeLayout(false);
tableLayoutPanel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)radListControl_Updates).EndInit();
((System.ComponentModel.ISupportInitialize)radTreeView_Sets).EndInit();
((System.ComponentModel.ISupportInitialize)splitPanel2).EndInit();
splitPanel2.ResumeLayout(false);
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel_ActionSet.ResumeLayout(false);
tableLayoutPanel_ActionSet.PerformLayout();
((System.ComponentModel.ISupportInitialize)radGridView_Actions.MasterTemplate).EndInit();
((System.ComponentModel.ISupportInitialize)radGridView_Actions).EndInit();
((System.ComponentModel.ISupportInitialize)radMenu1).EndInit();
tableLayoutPanel_ActionSetInfo.ResumeLayout(false);
tableLayoutPanel_ActionSetInfo.PerformLayout();
((System.ComponentModel.ISupportInitialize)radLabel1).EndInit();
((System.ComponentModel.ISupportInitialize)radTextBoxControl_Version).EndInit();
((System.ComponentModel.ISupportInitialize)radCheckBox_IsPublic).EndInit();
((System.ComponentModel.ISupportInitialize)radWaitingBar_Updates).EndInit();
((System.ComponentModel.ISupportInitialize)radWaitingBar_Actions).EndInit();
((System.ComponentModel.ISupportInitialize)radMenu1).EndInit();
((System.ComponentModel.ISupportInitialize)this).EndInit();
ResumeLayout(false);
PerformLayout();
@@ -334,20 +383,23 @@ partial class MainForm
private Telerik.WinControls.UI.RadMenuItem radMenuItem_WorkspacePreferences;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_Tools;
private TableLayoutPanel tableLayoutPanel2;
private TableLayoutPanel tableLayoutPanel1;
private TableLayoutPanel tableLayoutPanel_ActionSet;
private Telerik.WinControls.UI.RadGridView radGridView_Actions;
private Telerik.WinControls.UI.RadMenuSeparatorItem radMenuSeparatorItem1;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_OpenNewWorkspace;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_RecentWorkspaces;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_SaveWorkspace;
private Telerik.WinControls.UI.RadWaitingBar radWaitingBar_Updates;
private Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement dotsRingWaitingBarIndicatorElement1;
private Telerik.WinControls.UI.RadWaitingBar radWaitingBar_Actions;
private Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement dotsRingWaitingBarIndicatorElement2;
private Telerik.WinControls.UI.RadListControl radListControl_Updates;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_Updates;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_EditUpdate;
private Telerik.WinControls.UI.RadMenuSeparatorItem radMenuSeparatorItem2;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_CreateUpdate;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_RemoveUpdate;
private Telerik.WinControls.UI.RadMenuHeaderItem radMenuHeaderItem_NewWorkspace;
private Telerik.WinControls.UI.RadMenuSeparatorItem radMenuSeparatorItem3;
private Telerik.WinControls.UI.RadMenuHeaderItem radMenuHeaderItem_RecentWorkspaces;
private Telerik.WinControls.UI.RadTreeView radTreeView_Sets;
private TableLayoutPanel tableLayoutPanel_ActionSetInfo;
private Telerik.WinControls.UI.RadLabel radLabel1;
private Telerik.WinControls.UI.RadTextBoxControl radTextBoxControl_Version;
private Telerik.WinControls.UI.RadCheckBox radCheckBox_IsPublic;
}

View File

@@ -3,12 +3,10 @@ using ModpackUpdater.Apps.Manager.Api.Plugins.Features;
using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.LangRes;
using ModpackUpdater.Apps.Manager.Settings;
using Pilz.Plugins.Advanced;
using Pilz.Plugins.Advanced.UI.Telerik;
using Pilz.UI.Extensions;
using Pilz.Features;
using Pilz.UI.Symbols;
using Pilz.UI.Telerik.Dialogs;
using Pilz.UI.Telerik.Extensions.Extensions;
using Pilz.UI.WinForms.Extensions;
using Pilz.UI.WinForms.Telerik.Features;
using Telerik.WinControls;
using Telerik.WinControls.UI;
@@ -16,17 +14,17 @@ namespace ModpackUpdater.Apps.Manager.Ui;
public partial class MainForm : RadForm, IMainApi
{
private record RecentFilesItemTag(WorkspaceConfig Config, WorkspaceFeature Feature);
private record WorkspaceTag(IWorkspace Workspace, WorkspaceFeature Feature);
private bool loadingMainView;
private WorkspaceTag? wsInfo;
private InstallAction? tempAction;
Form IMainApi.MainWindow => this;
IWorkspace? IMainApi.CurWorkspace => wsInfo?.Workspace;
public IActionSetInfos? CurActionSet => radListControl_Updates.SelectedValue as IActionSetInfos;
private record RecentFilesItemTag(WorkspaceConfig Config, WorkspaceFeature Feature);
private record WorkspaceTag(IWorkspace Workspace, WorkspaceFeature Feature);
public IActionSetInfos? CurActionSet => radTreeView_Sets.SelectedNode?.Tag as IActionSetInfos;
public MainForm()
{
@@ -35,33 +33,36 @@ public partial class MainForm : RadForm, IMainApi
radMenuItem_Workspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.workspace, SymbolSize.Small);
radMenuItem_WorkspacePreferences.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.settings, SymbolSize.Small);
radMenuItem_SaveWorkspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.save, SymbolSize.Small);
radMenuItem_OpenNewWorkspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.new_window, SymbolSize.Small);
radMenuItem_RecentWorkspaces.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.time_machine, SymbolSize.Small);
//radMenuItem_OpenNewWorkspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.new_window, SymbolSize.Small);
//radMenuItem_RecentWorkspaces.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.time_machine, SymbolSize.Small);
radMenuItem_Updates.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small);
radMenuItem_EditUpdate.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.edit, SymbolSize.Small);
radMenuItem_CreateUpdate.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.add, SymbolSize.Small);
radMenuItem_RemoveUpdate.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.remove, SymbolSize.Small);
radMenuItem_Tools.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.tools, SymbolSize.Small);
radMenuItem_SaveWorkspace.Shortcuts.Add(new(Keys.Control, Keys.S));
PluginFeatureController.Instance.Features.Get(FeatureTypes.Workspace).InsertItemsTo(radMenuItem_OpenNewWorkspace.Items, customClickHandler: RadMenuItem_OpenNewWorkspace_Click);
PluginFeatureController.Instance.Functions.Get(FeatureTypes.Tools).InsertItemsTo(radMenuItem_Tools.Items, customClickHandler: RadMenuItem_ToolsItem_Click);
PluginFeatureController.Instance.Features.Get(FeatureTypes.Workspace).InsertItemsTo(radMenuItem_Workspace.Items,
customClickHandler: RadMenuItem_OpenNewWorkspace_Click,
insertPrioSplitters: true,
customDefault: radMenuItem_Workspace.Items.IndexOf(radMenuHeaderItem_NewWorkspace) + 1);
PluginFeatureController.Instance.Functions.Get(FeatureTypes.Tools).InsertItemsTo(radMenuItem_Tools.Items,
customClickHandler: RadMenuItem_ToolsItem_Click,
insertPrioSplitters: true);
}
private void LoadRecentWorkspaces()
{
var settings = Program.Settings.Get<WorkspaceSettings>();
var startIndex = radMenuItem_Workspace.Items.IndexOf(radMenuHeaderItem_RecentWorkspaces) + 1;
radMenuItem_RecentWorkspaces.Items.Clear();
foreach (var item in radMenuItem_Workspace.Items.Where(n => n.Tag is RecentFilesItemTag).ToArray())
radMenuItem_Workspace.Items.Remove(item);
foreach (var config in settings.Workspaces.ToArray())
foreach (var config in settings.Workspaces)
{
if (PluginFeatureController.Instance.Features.Get(FeatureTypes.Workspace).OfType<WorkspaceFeature>().FirstOrDefault(n => n.Identifier == config.ProviderId) is not WorkspaceFeature feature)
{
settings.Workspaces.Remove(config);
continue;
}
var item = new RadMenuItem
{
@@ -72,13 +73,8 @@ public partial class MainForm : RadForm, IMainApi
item.Click += RadMenuItem_OpenRecentWorkspace_Click;
radMenuItem_RecentWorkspaces.Items.Add(item);
radMenuItem_Workspace.Items.Insert(startIndex++, item);
}
if (radMenuItem_RecentWorkspaces.Items.Any())
radMenuItem_RecentWorkspaces.Visibility = ElementVisibility.Visible;
else
radMenuItem_RecentWorkspaces.Visibility = ElementVisibility.Collapsed;
}
private void AddToRecentFiles(IWorkspace workspace)
@@ -121,47 +117,74 @@ public partial class MainForm : RadForm, IMainApi
radWaitingBar_Updates.StartWaiting();
Text = wsInfo.Workspace.Config.DisplayText;
radListControl_Updates.BeginUpdate();
radListControl_Updates.Items.Clear();
radTreeView_Sets.BeginUpdate();
radTreeView_Sets.Nodes.Clear();
AddUpdateItem(wsInfo.Workspace.InstallInfos);
wsInfo.Workspace.UpdateInfos.Updates.ForEach(n => AddUpdateItem(n));
var nodeUpdates = new RadTreeNode
{
Text = "Updates",
Name = "updates",
SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small),
};
wsInfo.Workspace.UpdateInfos.Updates.ForEach(n => AddUpdateItem(n, nodeUpdates.Nodes));
radTreeView_Sets.Nodes.Add(nodeUpdates);
radListControl_Updates.EndUpdate();
radTreeView_Sets.EndUpdate();
radWaitingBar_Updates.StopWaiting();
}
private RadListDataItem AddUpdateItem(IActionSetInfos infos)
private RadTreeNode AddUpdateItem(IActionSetInfos infos)
{
return AddUpdateItem(infos, radTreeView_Sets.Nodes);
}
private RadTreeNode AddUpdateItem(IActionSetInfos infos, RadTreeNodeCollection parent)
{
var item = CreateUpdateItem(infos);
radListControl_Updates.Items.Add(item);
parent.Add(item);
return item;
}
private RadListDataItem InsertUpdateItem(IActionSetInfos infos)
private RadTreeNode InsertUpdateItem(IActionSetInfos infos)
{
var item = CreateUpdateItem(infos);
radListControl_Updates.Items.Insert(Math.Min(1, radListControl_Updates.Items.Count), item);
var nodeUpdates = radTreeView_Sets.Nodes["updates"];
nodeUpdates.Nodes.Insert(Math.Min(1, nodeUpdates.Nodes.Count), item);
return item;
}
private RadListDataItem CreateUpdateItem(IActionSetInfos infos)
private RadTreeNode CreateUpdateItem(IActionSetInfos infos)
{
var item = new RadListDataItem();
var item = new RadTreeNode();
UpdateUpdateItem(item, infos);
return item;
}
private void UpdateUpdateItem(RadListDataItem item)
private void UpdateUpdateItem(RadTreeNode item)
{
if (item.Value is IActionSetInfos infos)
if (item.Tag is IActionSetInfos infos)
UpdateUpdateItem(item, infos);
}
private void UpdateUpdateItem(RadListDataItem item, IActionSetInfos infos)
private void SaveActionsSet()
{
if (item.Value != infos)
item.Value = infos;
if (loadingMainView || CurActionSet is not IActionSetInfos infos)
return;
if (!Version.TryParse(radTextBoxControl_Version.Text.Trim(), out Version? version))
version = new Version("1.0.0.0");
infos.Version = version;
infos.IsPublic = radCheckBox_IsPublic.Checked;
UpdateUpdateItem(radTreeView_Sets.SelectedNode);
}
private void UpdateUpdateItem(RadTreeNode item, IActionSetInfos infos)
{
if (item.Tag != infos)
item.Tag = infos;
if (infos is UpdateInfo)
item.Text = string.Format(GeneralLangRes.Node_Update, infos.Version?.ToString() ?? "?");
@@ -175,9 +198,10 @@ public partial class MainForm : RadForm, IMainApi
public void UpdateItem(IActionSetInfos actionSetInfos)
{
RadListDataItem? item = null;
RadTreeNode? item = null;
var nodeUpdates = radTreeView_Sets.Nodes["updates"];
foreach (var iitem in radListControl_Updates.Items)
foreach (var iitem in nodeUpdates.Nodes)
{
if (item == null && iitem.Value == actionSetInfos)
item = iitem;
@@ -186,7 +210,7 @@ public partial class MainForm : RadForm, IMainApi
if (item == null)
InsertUpdateItem(actionSetInfos);
else if (wsInfo?.Workspace.UpdateInfos != null && !wsInfo.Workspace.UpdateInfos.Updates.Contains(actionSetInfos))
radListControl_Updates.Items.Remove(item);
item.Remove();
else
UpdateUpdateItem(item);
}
@@ -200,7 +224,29 @@ public partial class MainForm : RadForm, IMainApi
}
}
private void LoadActionSet(IActionSetInfos infos)
private void LoadMainView()
{
loadingMainView = true;
tableLayoutPanel_ActionSet.Visible = false;
tableLayoutPanel_ActionSetInfo.Visible = false;
if (CurActionSet is IActionSet set)
{
if (set is IActionSetInfos infos)
{
radTextBoxControl_Version.Text = infos.Version?.ToString();
radCheckBox_IsPublic.Checked = infos.IsPublic;
tableLayoutPanel_ActionSetInfo.Visible = true;
}
tableLayoutPanel_ActionSet.Visible = true;
LoadActionSet(set);
}
loadingMainView = false;
}
private void LoadActionSet(IActionSet infos)
{
radGridView_Actions.BeginUpdate();
radGridView_Actions.Rows.Clear();
@@ -265,6 +311,12 @@ public partial class MainForm : RadForm, IMainApi
Width = 50,
IsVisible = infos is UpdateInfo,
},
new GridViewTextBoxColumn
{
Name = "destpath",
HeaderText = ActionsListLangRes.Col_DestPath,
Width = 250,
},
new GridViewComboBoxColumn
{
Name = "srctype",
@@ -316,12 +368,6 @@ public partial class MainForm : RadForm, IMainApi
IsVisible = infos is UpdateInfo,
},
new GridViewTextBoxColumn
{
Name = "destpath",
HeaderText = ActionsListLangRes.Col_DestPath,
Width = 250,
},
new GridViewTextBoxColumn
{
Name = "website",
HeaderText = ActionsListLangRes.Col_Website,
@@ -411,10 +457,9 @@ public partial class MainForm : RadForm, IMainApi
func.Execute(new MainApiParameters(this));
}
private void RadListControl_Updates_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e)
private void RadTreeView_Sets_SelectedNodeChanged(object sender, RadTreeViewEventArgs e)
{
if (radListControl_Updates.Items.ElementAtOrDefault(e.Position)?.Value is IActionSetInfos infos)
LoadActionSet(infos);
LoadMainView();
}
private void RadGridView_Actions_CellFormatting(object sender, CellFormattingEventArgs e)
@@ -445,9 +490,19 @@ public partial class MainForm : RadForm, IMainApi
private void RadGridView_Actions_CellValueChanged(object sender, GridViewCellEventArgs e)
{
if (e.Row?.Tag is not InstallAction action)
if (e.Row is null)
return;
if (e.Row.Tag is not InstallAction action)
{
if (CurActionSet is UpdateInfo)
action = tempAction ??= new UpdateAction();
else if (CurActionSet is InstallInfos)
action = tempAction ??= new InstallAction();
else
return;
}
var uaction = action as UpdateAction;
var newValue = e.Row.Cells[e.Column.Name].Value;
var colName = e.Column.Name;
@@ -529,56 +584,39 @@ public partial class MainForm : RadForm, IMainApi
private void RadMenuItem_Updates_DropDownOpening(object sender, System.ComponentModel.CancelEventArgs e)
{
radMenuItem_EditUpdate.Enabled = radListControl_Updates.SelectedItem?.Value is IActionSetInfos;
radMenuItem_RemoveUpdate.Enabled = radListControl_Updates.SelectedItem?.Value is UpdateInfo;
}
private void RadMenuItem_EditUpdate_Click(object sender, EventArgs e)
{
if (radListControl_Updates.SelectedItem?.Value is IActionSetInfos infos
&& RadDialogBase.ShowDialog(new UpdatePropertiesEditorFlyout(infos), TitlesLangRes.EditUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.edit, SymbolSize.Small)).IsValid())
UpdateUpdateItem(radListControl_Updates.SelectedItem);
radMenuItem_RemoveUpdate.Enabled = radTreeView_Sets.SelectedNode?.Tag is UpdateInfo;
}
private void RadMenuItem_CreateUpdate_Click(object sender, EventArgs e)
{
var infos = new UpdateInfo();
if (wsInfo?.Workspace.UpdateInfos is null)
return;
if (wsInfo?.Workspace.UpdateInfos is not null
&& RadDialogBase.ShowDialog(new UpdatePropertiesEditorFlyout(infos), TitlesLangRes.EditUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.edit, SymbolSize.Small)).IsValid())
var infos = new UpdateInfo
{
Version = wsInfo.Workspace.InstallInfos?.Version,
};
wsInfo.Workspace.UpdateInfos.Updates.Insert(0, infos);
InsertUpdateItem(infos);
}
}
private void RadMenuItem_RemoveUpdate_Click(object sender, EventArgs e)
{
if (radListControl_Updates.SelectedItem?.Value is UpdateInfo infos && wsInfo?.Workspace.UpdateInfos is not null
if (radTreeView_Sets.SelectedNode?.Tag is UpdateInfo infos && wsInfo?.Workspace.UpdateInfos is not null
&& RadMessageBox.Show(MsgBoxLangRes.RemoveUpdate, MsgBoxLangRes.RemoveUpdate_Title, MessageBoxButtons.YesNo, RadMessageIcon.Exclamation).IsYes())
{
wsInfo.Workspace.UpdateInfos.Updates.Remove(infos);
radListControl_Updates.Items.Remove(radListControl_Updates.SelectedItem);
radTreeView_Sets.SelectedNode.Remove();
}
}
private void RadGridView_Actions_UserAddedRow(object sender, GridViewRowEventArgs e)
{
foreach (var row in e.Rows)
{
if (CurActionSet is UpdateInfo uinfo)
{
var action = new UpdateAction();
// ...
uinfo.Actions.Add(action);
}
if (CurActionSet is InstallInfos iinfo)
{
var action = new InstallAction();
// ...
iinfo.Actions.Add(action);
}
}
if (tempAction is UpdateAction uaction && CurActionSet is UpdateInfo uinfo)
uinfo.Actions.Add(uaction);
else if (tempAction is InstallAction iaction && CurActionSet is InstallInfos iinfo)
iinfo.Actions.Add(iaction);
tempAction = null;
}
private void RadGridView_Actions_UserDeletingRow(object sender, GridViewRowCancelEventArgs e)
@@ -594,6 +632,20 @@ public partial class MainForm : RadForm, IMainApi
private void RadGridView_Actions_ContextMenuOpening(object sender, ContextMenuOpeningEventArgs e)
{
PluginFeatureController.Instance.Functions.Get(FeatureTypes.ActionsContextMenu).InsertItemsTo(e.ContextMenu.Items, customClickHandler: RadMenuItem_ToolsItem_Click);
if (e.ContextMenuProvider is GridDataCellElement)
{
e.ContextMenu.Items.Add(new RadMenuSeparatorItem());
PluginFeatureController.Instance.Functions.Get(FeatureTypes.ActionsContextMenu).InsertItemsTo(e.ContextMenu.Items, customClickHandler: RadMenuItem_ToolsItem_Click, insertPrioSplitters: true);
}
}
private void RadTextBoxControl1_TextChanged(object sender, EventArgs e)
{
SaveActionsSet();
}
private void RadCheckBox1_ToggleStateChanged(object sender, StateChangedEventArgs args)
{
SaveActionsSet();
}
}

View File

@@ -1,114 +0,0 @@
namespace ModpackUpdater.Apps.Manager.Ui;
partial class UpdatePropertiesEditorFlyout
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Vom Komponenten-Designer generierter Code
/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
tableLayoutPanel1 = new TableLayoutPanel();
radLabel1 = new Telerik.WinControls.UI.RadLabel();
radTextBox_Version = new Telerik.WinControls.UI.RadTextBox();
radCheckBox_IsPublic = new Telerik.WinControls.UI.RadCheckBox();
tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radLabel1).BeginInit();
((System.ComponentModel.ISupportInitialize)radTextBox_Version).BeginInit();
((System.ComponentModel.ISupportInitialize)radCheckBox_IsPublic).BeginInit();
SuspendLayout();
//
// tableLayoutPanel1
//
tableLayoutPanel1.AutoSize = true;
tableLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
tableLayoutPanel1.ColumnCount = 1;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.Controls.Add(radLabel1, 0, 0);
tableLayoutPanel1.Controls.Add(radTextBox_Version, 0, 1);
tableLayoutPanel1.Controls.Add(radCheckBox_IsPublic, 0, 3);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(0, 30);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 4;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
tableLayoutPanel1.Size = new Size(300, 84);
tableLayoutPanel1.TabIndex = 4;
//
// radLabel1
//
radLabel1.Location = new Point(3, 3);
radLabel1.Name = "radLabel1";
radLabel1.Size = new Size(55, 22);
radLabel1.TabIndex = 0;
radLabel1.Text = "Version";
//
// radTextBox_Version
//
radTextBox_Version.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
radTextBox_Version.Location = new Point(3, 31);
radTextBox_Version.Name = "radTextBox_Version";
radTextBox_Version.NullText = "1.5.37.0";
radTextBox_Version.Size = new Size(294, 24);
radTextBox_Version.TabIndex = 1;
//
// radCheckBox_IsPublic
//
radCheckBox_IsPublic.Location = new Point(3, 61);
radCheckBox_IsPublic.Name = "radCheckBox_IsPublic";
radCheckBox_IsPublic.Size = new Size(76, 20);
radCheckBox_IsPublic.TabIndex = 3;
radCheckBox_IsPublic.Text = "Is public";
//
// UpdatePropertiesEditorFlyout
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
BackColor = Color.Transparent;
Controls.Add(tableLayoutPanel1);
MinimumSize = new Size(300, 0);
Name = "UpdatePropertiesEditorFlyout";
Size = new Size(300, 144);
Controls.SetChildIndex(tableLayoutPanel1, 0);
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)radLabel1).EndInit();
((System.ComponentModel.ISupportInitialize)radTextBox_Version).EndInit();
((System.ComponentModel.ISupportInitialize)radCheckBox_IsPublic).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private TableLayoutPanel tableLayoutPanel1;
private Telerik.WinControls.UI.RadLabel radLabel1;
private Telerik.WinControls.UI.RadTextBox radTextBox_Version;
private Telerik.WinControls.UI.RadCheckBox radCheckBox_IsPublic;
}

View File

@@ -1,32 +0,0 @@
using Pilz.UI;
using Pilz.UI.Telerik.Dialogs;
namespace ModpackUpdater.Apps.Manager.Ui;
public partial class UpdatePropertiesEditorFlyout : RadFlyoutBase, ILoadContent
{
private readonly IActionSetInfos infos;
public UpdatePropertiesEditorFlyout(IActionSetInfos infos)
{
this.infos = infos;
InitializeComponent();
}
public void LoadContent()
{
radTextBox_Version.Text = infos.Version?.ToString();
radCheckBox_IsPublic.Checked = infos.IsPublic;
}
protected override bool ValidateOK()
{
if (!Version.TryParse(radTextBox_Version.Text.Trim(), out Version? version))
return false;
infos.Version = version;
infos.IsPublic = radCheckBox_IsPublic.Checked;
return base.ValidateOK();
}
}

View File

@@ -36,6 +36,7 @@ partial class UpdatesCollectorUi
var resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdatesCollectorUi));
tableLayoutPanel1 = new TableLayoutPanel();
radButton_Continue = new Telerik.WinControls.UI.RadButton();
radProgressBar1 = new Telerik.WinControls.UI.RadProgressBar();
radSplitContainer1 = new Telerik.WinControls.UI.RadSplitContainer();
splitPanel1 = new Telerik.WinControls.UI.SplitPanel();
tableLayoutPanel2 = new TableLayoutPanel();
@@ -44,9 +45,10 @@ partial class UpdatesCollectorUi
tableLayoutPanel3 = new TableLayoutPanel();
radListView_VersionTags = new Telerik.WinControls.UI.RadListView();
radWaitingBar1 = new Telerik.WinControls.UI.RadWaitingBar();
dotsRingWaitingBarIndicatorElement1 = new Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement();
dotsSpinnerWaitingBarIndicatorElement1 = new Telerik.WinControls.UI.DotsSpinnerWaitingBarIndicatorElement();
tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)radButton_Continue).BeginInit();
((System.ComponentModel.ISupportInitialize)radProgressBar1).BeginInit();
((System.ComponentModel.ISupportInitialize)radSplitContainer1).BeginInit();
radSplitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitPanel1).BeginInit();
@@ -64,12 +66,14 @@ partial class UpdatesCollectorUi
// tableLayoutPanel1
//
tableLayoutPanel1.AutoSize = true;
tableLayoutPanel1.ColumnCount = 2;
tableLayoutPanel1.ColumnCount = 3;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel1.Controls.Add(radButton_Continue, 1, 0);
tableLayoutPanel1.Controls.Add(radButton_Continue, 2, 0);
tableLayoutPanel1.Controls.Add(radProgressBar1, 0, 0);
tableLayoutPanel1.Dock = DockStyle.Bottom;
tableLayoutPanel1.Location = new Point(0, 420);
tableLayoutPanel1.Location = new Point(0, 419);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 1;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
@@ -89,6 +93,15 @@ partial class UpdatesCollectorUi
radButton_Continue.TextAlignment = ContentAlignment.MiddleLeft;
radButton_Continue.TextImageRelation = TextImageRelation.ImageBeforeText;
//
// radProgressBar1
//
radProgressBar1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
radProgressBar1.Location = new Point(3, 3);
radProgressBar1.Name = "radProgressBar1";
radProgressBar1.Size = new Size(194, 24);
radProgressBar1.TabIndex = 2;
radProgressBar1.Visible = false;
//
// radSplitContainer1
//
radSplitContainer1.Controls.Add(splitPanel1);
@@ -96,11 +109,7 @@ partial class UpdatesCollectorUi
radSplitContainer1.Dock = DockStyle.Fill;
radSplitContainer1.Location = new Point(0, 0);
radSplitContainer1.Name = "radSplitContainer1";
//
//
//
radSplitContainer1.RootElement.MinSize = new Size(25, 25);
radSplitContainer1.Size = new Size(800, 420);
radSplitContainer1.Size = new Size(800, 419);
radSplitContainer1.TabIndex = 1;
radSplitContainer1.TabStop = false;
//
@@ -109,11 +118,7 @@ partial class UpdatesCollectorUi
splitPanel1.Controls.Add(tableLayoutPanel2);
splitPanel1.Location = new Point(0, 0);
splitPanel1.Name = "splitPanel1";
//
//
//
splitPanel1.RootElement.MinSize = new Size(25, 25);
splitPanel1.Size = new Size(516, 420);
splitPanel1.Size = new Size(516, 419);
splitPanel1.SizeInfo.AutoSizeScale = new SizeF(0.148241222F, 0F);
splitPanel1.SizeInfo.SplitterCorrection = new Size(118, 0);
splitPanel1.TabIndex = 0;
@@ -132,7 +137,7 @@ partial class UpdatesCollectorUi
tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
tableLayoutPanel2.Size = new Size(516, 420);
tableLayoutPanel2.Size = new Size(516, 419);
tableLayoutPanel2.TabIndex = 1;
//
// radListView_Updates
@@ -148,7 +153,7 @@ partial class UpdatesCollectorUi
radListView_Updates.ItemSpacing = -1;
radListView_Updates.Location = new Point(3, 3);
radListView_Updates.Name = "radListView_Updates";
radListView_Updates.Size = new Size(510, 414);
radListView_Updates.Size = new Size(510, 413);
radListView_Updates.TabIndex = 0;
radListView_Updates.ViewType = Telerik.WinControls.UI.ListViewType.DetailsView;
radListView_Updates.SelectedItemChanged += RadListView_Updates_SelectedItemChanged;
@@ -159,11 +164,7 @@ partial class UpdatesCollectorUi
splitPanel2.Controls.Add(tableLayoutPanel3);
splitPanel2.Location = new Point(520, 0);
splitPanel2.Name = "splitPanel2";
//
//
//
splitPanel2.RootElement.MinSize = new Size(25, 25);
splitPanel2.Size = new Size(280, 420);
splitPanel2.Size = new Size(280, 419);
splitPanel2.SizeInfo.AutoSizeScale = new SizeF(-0.148241192F, 0F);
splitPanel2.SizeInfo.SplitterCorrection = new Size(-118, 0);
splitPanel2.TabIndex = 1;
@@ -182,7 +183,7 @@ partial class UpdatesCollectorUi
tableLayoutPanel3.RowCount = 1;
tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
tableLayoutPanel3.Size = new Size(280, 420);
tableLayoutPanel3.Size = new Size(280, 419);
tableLayoutPanel3.TabIndex = 0;
//
// radListView_VersionTags
@@ -196,7 +197,7 @@ partial class UpdatesCollectorUi
radListView_VersionTags.ItemSpacing = -1;
radListView_VersionTags.Location = new Point(3, 3);
radListView_VersionTags.Name = "radListView_VersionTags";
radListView_VersionTags.Size = new Size(274, 414);
radListView_VersionTags.Size = new Size(274, 413);
radListView_VersionTags.TabIndex = 0;
radListView_VersionTags.ViewType = Telerik.WinControls.UI.ListViewType.DetailsView;
radListView_VersionTags.SelectedIndexChanged += RadListView_VersionTags_SelectedIndexChanged;
@@ -204,27 +205,26 @@ partial class UpdatesCollectorUi
// radWaitingBar1
//
radWaitingBar1.AssociatedControl = radListView_Updates;
radWaitingBar1.Location = new Point(0, 0);
radWaitingBar1.Location = new Point(220, 171);
radWaitingBar1.Name = "radWaitingBar1";
radWaitingBar1.Size = new Size(70, 70);
radWaitingBar1.TabIndex = 2;
radWaitingBar1.Text = "radWaitingBar1";
radWaitingBar1.WaitingIndicators.Add(dotsRingWaitingBarIndicatorElement1);
radWaitingBar1.WaitingIndicators.Add(dotsSpinnerWaitingBarIndicatorElement1);
radWaitingBar1.WaitingIndicatorSize = new Size(100, 14);
radWaitingBar1.WaitingSpeed = 50;
radWaitingBar1.WaitingStyle = Telerik.WinControls.Enumerations.WaitingBarStyles.DotsRing;
radWaitingBar1.WaitingSpeed = 100;
radWaitingBar1.WaitingStyle = Telerik.WinControls.Enumerations.WaitingBarStyles.DotsSpinner;
//
// dotsRingWaitingBarIndicatorElement1
// dotsSpinnerWaitingBarIndicatorElement1
//
dotsRingWaitingBarIndicatorElement1.Name = "dotsRingWaitingBarIndicatorElement1";
dotsSpinnerWaitingBarIndicatorElement1.Name = "dotsSpinnerWaitingBarIndicatorElement1";
//
// UpdatesCollectorUi
//
AutoScaleBaseSize = new Size(7, 15);
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Controls.Add(radWaitingBar1);
ClientSize = new Size(800, 449);
Controls.Add(radSplitContainer1);
Controls.Add(tableLayoutPanel1);
Icon = (Icon)resources.GetObject("$this.Icon");
@@ -234,6 +234,7 @@ partial class UpdatesCollectorUi
Shown += UpdatesCollectorUi_Shown;
tableLayoutPanel1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)radButton_Continue).EndInit();
((System.ComponentModel.ISupportInitialize)radProgressBar1).EndInit();
((System.ComponentModel.ISupportInitialize)radSplitContainer1).EndInit();
radSplitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)splitPanel1).EndInit();
@@ -261,6 +262,7 @@ partial class UpdatesCollectorUi
private TableLayoutPanel tableLayoutPanel3;
private Telerik.WinControls.UI.RadListView radListView_Updates;
private Telerik.WinControls.UI.RadWaitingBar radWaitingBar1;
private Telerik.WinControls.UI.DotsRingWaitingBarIndicatorElement dotsRingWaitingBarIndicatorElement1;
private Telerik.WinControls.UI.RadListView radListView_VersionTags;
private Telerik.WinControls.UI.RadProgressBar radProgressBar1;
private Telerik.WinControls.UI.DotsSpinnerWaitingBarIndicatorElement dotsSpinnerWaitingBarIndicatorElement1;
}

View File

@@ -42,6 +42,12 @@ public partial class UpdatesCollectorUi : RadForm
{
var updates = await factory.FindUpdates(action, workspace.ModpackConfig?.MinecraftVersion, workspace.ModpackConfig?.ModLoader ?? ModLoader.Any);
BeginInvoke(() =>
{
radProgressBar1.Value1 += 1;
radProgressBar1.Text = $"{radProgressBar1.Value1} / {radProgressBar1.Maximum}";
});
if (updates == null || updates.Length == 0 || updates[0].Value == action.SourceTag)
continue;
@@ -96,8 +102,15 @@ public partial class UpdatesCollectorUi : RadForm
private async void UpdatesCollectorUi_Shown(object sender, EventArgs e)
{
radWaitingBar1.StartWaiting();
radProgressBar1.Value1 = 0;
radProgressBar1.Maximum = actions.Length;
radProgressBar1.Text = null;
radProgressBar1.Visible = true;
CurrentUpdates = await FindUpdates();
LoadUpdates(CurrentUpdates);
radProgressBar1.Visible = false;
radWaitingBar1.StopWaiting();
}

View File

@@ -1,7 +1,7 @@
using Pilz.UI.Telerik.Dialogs;
using Pilz.UI.Telerik.Symbols;
using Pilz.UI.Telerik.Theming;
using Telerik.WinControls;
using ModpackUpdater.Apps.Manager;
using Pilz.UI.WinForms.Telerik.Dialogs;
using Pilz.UI.WinForms.Telerik.Symbols;
using Pilz.UI.WinForms.Telerik.Theming;
using Telerik.WinControls.Themes;
namespace ModpackUpdater.Apps;
@@ -12,10 +12,12 @@ public static class AppGlobals
public static void Initialize()
{
ThemeHelper.ApplyApplicationTheme(ApplicationTheme.Auto,
() => new Windows11CompactTheme(),
() => new Windows11CompactDarkTheme());
ThemeHelper.ApplyApplicationTheme(new ThemeDefinition(ApplicationTheme.Auto, HighContrastMode.Auto), n => n.Theme switch
{
ApplicationTheme.Light => new Windows11CompactTheme(),
ApplicationTheme.Gray or ApplicationTheme.Dark => new Windows11CompactDarkTheme(),
_ => throw new NotImplementedException(),
});
RadFlyoutBase.ConfirmSvg = Symbols.GetSvgImage(AppSymbols.checkmark, Pilz.UI.Symbols.SymbolSize.Small);
RadFlyoutBase.CancelSvg = Symbols.GetSvgImage(AppSymbols.cancel, Pilz.UI.Symbols.SymbolSize.Small);
}

View File

@@ -1,18 +1,17 @@
using Pilz.UI.Telerik;
using Pilz.UI.Telerik.Symbols;
using Pilz.UI.WinForms.Telerik.Symbols;
using System.Reflection;
namespace ModpackUpdater.Apps;
namespace ModpackUpdater.Apps.Manager;
internal class AppSymbolFactory : RadSymbolFactory<AppSymbols>
{
public override Assembly GetImageResourceAssembly()
{
return Assembly.GetExecutingAssembly();
return typeof(AppSymbols).Assembly;
}
public override string GetImageRessourcePath(AppSymbols svgImage)
{
return $"{GetType().Namespace}.Symbols.{svgImage}.svg";
return $"{typeof(AppSymbols).Namespace}.Symbols.{svgImage}.svg";
}
}

View File

@@ -35,4 +35,5 @@ public enum AppSymbols
heart_with_pulse,
broom,
renew,
list_view,
}

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
@@ -12,10 +11,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Pilz.UI" Version="2.3.10" />
<PackageReference Include="Pilz.UI.Telerik" Version="2.7.4" />
<PackageReference Include="UI.for.WinForms.Common" Version="2024.3.806" />
<PackageReference Include="UI.for.WinForms.Themes" Version="2024.3.806" />
<PackageReference Include="Pilz.UI" Version="3.1.1" />
<PackageReference Include="Pilz.UI.WinForms.Telerik.Symbols" Version="1.2.1" />
<PackageReference Include="UI.for.WinForms.Common" Version="2025.3.812" />
<PackageReference Include="UI.for.WinForms.Themes" Version="2025.3.812" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#B2EBF2" d="M7 4H41V44H7z"/><path fill="#00ACC1" d="M13 26H17V30H13zM13 18H17V22H13zM13 34H17V38H13zM13 10H17V14H13zM21 26H35V30H21zM21 18H35V22H21zM21 34H35V38H21zM21 10H35V14H21z"/></svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@@ -40,4 +40,45 @@ public static class Extensions
}
}
}
public static string GetSourceUrl(this InstallAction @this, Version version, string? overwriteVersion = null)
{
if (!string.IsNullOrWhiteSpace(overwriteVersion))
return @this.SourceUrl?.Replace("{version}", overwriteVersion);
if (version is not null)
return @this.SourceUrl?.Replace("{version}", version.ToString());
return @this.SourceUrl;
}
public static string GetInstallUrl(this ModpackConfig @this, string? overwriteRefTag = null)
{
if (!string.IsNullOrWhiteSpace(overwriteRefTag))
return @this.InstallUrl?.Replace("{ref}", overwriteRefTag);
if (!string.IsNullOrWhiteSpace(@this.RefTag))
return @this.InstallUrl?.Replace("{ref}", @this.RefTag);
return @this.InstallUrl;
}
public static string GetUpdateUrl(this ModpackConfig @this, string? overwriteRefTag = null)
{
if (!string.IsNullOrWhiteSpace(overwriteRefTag))
return @this.UpdateUrl?.Replace("{ref}", overwriteRefTag);
if (!string.IsNullOrWhiteSpace(@this.RefTag))
return @this.UpdateUrl?.Replace("{ref}", @this.RefTag);
return @this.UpdateUrl;
}
public static string GetDestPath(this InstallAction @this, string installDir)
{
var path = @this.DestPath;
var root = Path.GetPathRoot(path) ?? string.Empty;
var start = installDir;
while (root.Length == 3 && root.StartsWith(".."))
{
path = path[root.Length..];
root = Path.GetPathRoot(path);
start = Path.GetDirectoryName(start);
}
return Path.Combine(start, path);
}
}

View File

@@ -24,7 +24,7 @@ public class ModpackFactory
modrinth.Dispose();
}
public async Task<string> ResolveSourceUrl(InstallAction action)
public async Task<string> ResolveSourceUrl(InstallAction action, Version? targetVersion = null, string? overwriteVersion = null)
{
if (action.SourceType == SourceType.GitHub)
{
@@ -53,7 +53,7 @@ public class ModpackFactory
return file.Url;
}
return action.SourceUrl;
return action.GetSourceUrl(targetVersion, overwriteVersion: overwriteVersion);
}
public async Task<KeyValuePair<string, string>[]> FindUpdates(InstallAction action, string? gameVersion, ModLoader modLoader)
@@ -82,7 +82,7 @@ public class ModpackFactory
}
else if (action.SourceType == SourceType.Modrinth)
{
var res = await modrinth.Version.GetProjectVersionListAsync(action.SourceName);
var res = await modrinth.Version.GetProjectVersionListAsync(action.SourceName, gameVersions: GetGameVersionForModrinth(gameVersion), loaders: GetModLoaderForModrinth(modLoader));
return res.Select(v => new KeyValuePair<string, string>($"{v.VersionNumber} {v.ProjectVersionType} {v.Name}", v.Id)).ToArray();
}
@@ -103,4 +103,26 @@ public class ModpackFactory
_ => null,
};
}
private string[] GetModLoaderForModrinth(ModLoader modLoader)
{
return modLoader switch
{
ModLoader.Any => [],
ModLoader.Forge => ["forge"],
ModLoader.NeoForge => ["neoforge"],
ModLoader.Fabric => ["fabric"],
ModLoader.LiteLoader => ["liteloader"],
ModLoader.Cauldron => ["cauldron"],
ModLoader.Quilt => ["quilt"],
_ => null,
};
}
private string[] GetGameVersionForModrinth(string? gameVersion)
{
if (string.IsNullOrWhiteSpace(gameVersion) || gameVersion == "*")
return [];
return [gameVersion];
}
}

View File

@@ -1,11 +1,12 @@
using Unleash;
using System.Diagnostics.CodeAnalysis;
using Unleash;
using Unleash.ClientFactory;
namespace ModpackUpdater.Manager;
public class ModpackFeatures(ModpackConfig modpackConfig)
{
private IUnleash api;
private IUnleash? api;
private UnleashContext context;
private UnleashSettings settings;
@@ -26,20 +27,28 @@ public class ModpackFeatures(ModpackConfig modpackConfig)
return CheckFeature(feature, context);
}
public bool IsInvalid()
{
return string.IsNullOrWhiteSpace(modpackConfig.UnleashApiUrl) || string.IsNullOrWhiteSpace(modpackConfig.UnleashInstanceId);
}
[MemberNotNullWhen(true, nameof(api))]
private bool InitializeApi()
{
if (api == null && !string.IsNullOrWhiteSpace(modpackConfig.UnleashApiUrl) && !string.IsNullOrWhiteSpace(modpackConfig.UnleashInstanceId))
{
if (api != null
|| string.IsNullOrWhiteSpace(modpackConfig.UnleashApiUrl)
|| string.IsNullOrWhiteSpace(modpackConfig.UnleashInstanceId))
return api != null;
settings = new UnleashSettings
{
AppName = "Modpack Updater",
UnleashApi = new Uri(modpackConfig.UnleashApiUrl),
FetchTogglesInterval = TimeSpan.FromSeconds(60 * 5),
FetchTogglesInterval = TimeSpan.FromSeconds(0),
InstanceTag = modpackConfig.UnleashInstanceId,
};
api = new UnleashClientFactory().CreateClient(settings, synchronousInitialization: true);
}
return api != null;
}

View File

@@ -1,4 +1,6 @@
using Newtonsoft.Json;
using Castle.Core.Logging;
using Newtonsoft.Json;
using Pilz.Extensions.Collections;
using System.IO.Compression;
using FileMode = System.IO.FileMode;
@@ -19,6 +21,10 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
public delegate void CheckingProgressUpdatedEventHandler(int toCheck, int processed);
public ILogger Log { get; set; } = NullLogger.Instance;
public string? OverwriteRefTag { get; set; }
public string? OverwriteVersion { get; set; }
private readonly HttpClient http = new();
private readonly ModpackFactory factory = new();
@@ -27,15 +33,15 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
http.Dispose();
}
private async Task<UpdateInfos> DownloadUpdateInfos()
public async Task<UpdateInfos> DownloadUpdateInfos()
{
var content = await http.GetStringAsync(updateConfig.UpdateUrl);
var content = await http.GetStringAsync(updateConfig.GetUpdateUrl(overwriteRefTag: OverwriteRefTag));
return UpdateInfos.Parse(content);
}
private async Task<InstallInfos> DownloadInstallInfos()
public async Task<InstallInfos> DownloadInstallInfos()
{
var content = await http.GetStringAsync(updateConfig.InstallUrl);
var content = await http.GetStringAsync(updateConfig.GetInstallUrl(overwriteRefTag: OverwriteRefTag));
return InstallInfos.Parse(content);
}
@@ -59,28 +65,32 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
}
installInfos = await DownloadInstallInfos();
result.CurrentVersion = modpackInfo.Version ?? new Version("0.0.0.0");
var curVersion = result.CurrentVersion;
if (options.IgnoreInstalledVersion || modpackInfo.Version == null)
result.CurrentVersion = new Version("0.0.0.0");
else
result.CurrentVersion = modpackInfo.Version;
// Check install actions
if (!exists)
if (!exists || options.IgnoreInstalledVersion)
{
result.IsInstalled = false;
if (installInfos is not null && installInfos.IsPublic && installInfos.Actions.Count != 0)
{
var actions = installInfos.Actions.Where(n => n.Side.IsSide(options.Side) && (!n.IsExtra || options.IncludeExtras));
var actions = installInfos.Actions.Where(n => n.Side.IsSide(options.Side) && (!n.IsExtra || options.IncludeExtras) && GetOptionValue(options, installInfos, n.TargetOption));
if (actions.Any())
{
result.Actions.AddRange(actions);
result.LatestVersion = installInfos.Version;
curVersion = installInfos.Version;
result.CurrentVersion = installInfos.Version;
}
}
if (result.Actions.Count == 0)
result.HasError = true;
}
else
result.IsInstalled = true;
// Check update actions
if (!result.HasError && (exists || options.AllowUpdaterAfterInstall))
@@ -92,10 +102,9 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
var updatesOrderes = updateInfos.Updates.Where(n => n.IsPublic || options.IncludeNonPublic).OrderByDescending(n => n.Version);
var checkingVersionIndex = 0;
var checkingVersion = updatesOrderes.ElementAtOrDefault(checkingVersionIndex);
var actionsZeroIndex = result.Actions.Count; // Ensure we insert update actions behind install actions
result.IsInstalled = true;
var actionsZeroIndex = result.Actions.Count; // Ensure we insert update actions BEHIND install actions
while (checkingVersion is not null && checkingVersion.Version > curVersion)
while (checkingVersion is not null && checkingVersion.Version > result.CurrentVersion)
{
var actionsToAdd = new List<UpdateAction>();
@@ -105,7 +114,7 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
ResolveInherit(action, installInfos);
// Check & add
if (action.Side.IsSide(options.Side) && (!action.IsExtra || options.IncludeExtras) && !result.Actions.Any(n => n.DestPath == action.DestPath))
if (action.Side.IsSide(options.Side) && (!action.IsExtra || options.IncludeExtras) && GetOptionValue(options, installInfos, action.TargetOption) && !result.Actions.Any(n => n.DestPath == action.DestPath))
actionsToAdd.Add(action);
}
@@ -122,6 +131,22 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
result.HasError = true;
}
// Check option uninstall actions
result.OptionsAvailable.AddRange(installInfos.OptionSets); // Find used options
result.OptionsEnabled.AddRange(installInfos.OptionSets
.Where(set => set.Side.IsSide(options.Side))
.SelectMany(set => set.Options
.Where(opt => opt.Side.IsSide(options.Side) && options.Options.GetOptionValue(set, opt))
.Select(opt => $"{set.Id}.{opt.Id}"))); // Find used options
var uninstallOptionActions = installInfos.OptionSets
.Where(set => set.Side.IsSide(options.Side))
.SelectMany(set => set.Options
.Where(opt => opt.Side.IsSide(options.Side) && !result.OptionsEnabled.Contains($"{set.Id}.{opt.Id}") && modpackInfo.Options.ContainsKey($"{set.Id}.{opt.Id}"))
.SelectMany(opt => opt.UninstallActions))
.ForEach(action => ResolveInherit(action, installInfos))
.Where(action => !result.Actions.Any(n => n.DestPath == action.DestPath)); // Find actions that are not already there
result.Actions.InsertRange(0, uninstallOptionActions); // Add uninstall update actions BEFORE install actions
return result;
}
@@ -143,17 +168,18 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
foreach (InstallAction iaction in checkResult.Actions)
{
var destFilePath = Path.Combine(modpackInfo.LocaLPath, iaction.DestPath);
var destFilePath = iaction.GetDestPath(modpackInfo.LocaLPath);
var sourceUrl = updateConfig.PreferDirectLinks && !string.IsNullOrWhiteSpace(iaction.SourceUrl)
? iaction.SourceUrl
: await factory.ResolveSourceUrl(iaction);
? iaction.GetSourceUrl(checkResult.LatestVersion, overwriteVersion: OverwriteVersion)
: await factory.ResolveSourceUrl(iaction, targetVersion: checkResult.LatestVersion, overwriteVersion: OverwriteVersion);
if (iaction is UpdateAction uaction)
{
switch (uaction.Type)
{
case UpdateActionType.Update:
await InstallFile(destFilePath, sourceUrl, uaction.IsZip, uaction.ZipPath, localZipCache);
if (!await InstallFile(destFilePath, sourceUrl, uaction.IsZip, uaction.ZipPath, localZipCache))
return false;
break;
case UpdateActionType.Delete:
{
@@ -198,14 +224,15 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
break;
}
}
else
await InstallFile(destFilePath, sourceUrl, iaction.IsZip, iaction.ZipPath, localZipCache);
else if (!await InstallFile(destFilePath, sourceUrl, iaction.IsZip, iaction.ZipPath, localZipCache))
return false;
processed += 1;
InstallProgessUpdated?.Invoke(checkResult, processed);
}
// Save new modpack info
modpackInfo.Options.Clear();
modpackInfo.Version = checkResult.LatestVersion;
modpackInfo.ConfigUrl = updateConfig.ConfigUrl;
modpackInfo.Save();
@@ -217,11 +244,13 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
return true;
}
private async Task InstallFile(string destFilePath, string sourceUrl, bool isZip, string zipPath, List<LocalZipCacheEntry> localZipCache)
private async Task<bool> InstallFile(string destFilePath, string sourceUrl, bool isZip, string zipPath, List<LocalZipCacheEntry> localZipCache)
{
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
if (!isZip || localZipCache.FirstOrDefault(n => n.DownloadUrl == sourceUrl) is not LocalZipCacheEntry cachedZipInfo)
{
try
{
// Download
var fsDestinationPath = isZip ? Path.Combine(Path.GetTempPath(), $"mc_update_file_{DateTime.Now.ToBinary()}.zip") : destFilePath;
@@ -253,13 +282,53 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
else
cachedZipInfo = null;
}
catch (HttpRequestException ex)
{
Log.Error("Could not download file: " + sourceUrl, ex);
return false;
}
catch (Exception ex)
{
Log.Error("Could not install file: " + sourceUrl, ex);
return false;
}
}
// Handle zip file content
if (cachedZipInfo != null)
{
// Copy content
if (zipPath.StartsWith("{first}"))
{
var firstDirName = Path.GetFileName(Directory.GetDirectories(cachedZipInfo.ExtractedZipPath).First());
zipPath = zipPath.Replace("{first}", firstDirName);
}
var zipSrc = Path.Combine(cachedZipInfo.ExtractedZipPath, zipPath);
Extensions.CopyDirectory(zipSrc, destFilePath, true);
}
return true;
}
private static bool GetOptionValue(UpdateCheckOptions options, InstallInfos installInfos, string optionStr)
{
if (optionStr is null)
return true;
var optionsStr = optionStr.Split(',');
foreach (var set in installInfos.OptionSets)
{
if (!set.Side.IsSide(options.Side))
continue;
foreach (var opt in set.Options)
{
if (opt.Side.IsSide(options.Side) && optionsStr.Contains($"{set.Id}.{opt.Id}") && options.Options.GetOptionValue(set, opt))
return true;
}
}
return false;
}
}

View File

@@ -7,11 +7,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CurseForge.APIClient" Version="3.0.0" />
<PackageReference Include="Modrinth.Net" Version="3.4.5" />
<PackageReference Include="Octokit" Version="13.0.1" />
<PackageReference Include="Castle.Core" Version="5.2.1" />
<PackageReference Include="CurseForge.APIClient" Version="4.2.0" />
<PackageReference Include="LaunchDarkly.EventSource" Version="5.2.1" />
<PackageReference Include="Modrinth.Net" Version="3.6.0" />
<PackageReference Include="Octokit" Version="14.0.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="Unleash.Client" Version="4.1.12" />
<PackageReference Include="Unleash.Client" Version="5.5.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -7,4 +7,6 @@ public class UpdateCheckOptions
public bool AllowUpdaterAfterInstall { get; set; } = true;
public Side Side { get; set; } = Side.Client;
public bool IncludeExtras { get; set; }
public bool IgnoreInstalledVersion { get; set; }
public InstallOptionValueDictionary Options { get; } = [];
}

View File

@@ -5,6 +5,8 @@ public class UpdateCheckResult
public Version CurrentVersion { get; set; }
public Version LatestVersion { get; set; }
public List<InstallAction> Actions { get; private set; } = [];
public List<InstallOptionSet> OptionsAvailable { get; } = [];
public List<string> OptionsEnabled { get; } = [];
public bool IsInstalled { get; set; }
public bool HasError { get; set; }
public bool IsInMaintenance { get; set; }

View File

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32526.322
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModpackUpdater.Apps.Client", "ModpackUpdater.Apps.Client\ModpackUpdater.Apps.Client.csproj", "{81F9A2F7-D36B-0F08-12D7-9EC2606C9080}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModpackUpdater.Apps.Client.Gui", "ModpackUpdater.Apps.Client.Gui\ModpackUpdater.Apps.Client.Gui.csproj", "{81F9A2F7-D36B-0F08-12D7-9EC2606C9080}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModpackUpdater", "ModpackUpdater\ModpackUpdater.csproj", "{0E6B6470-8C1D-0CDD-3681-461297A01960}"
EndProject
@@ -17,6 +17,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModpackUpdater.Apps.Manager
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModpackUpdater.Apps", "ModpackUpdater.Apps\ModpackUpdater.Apps.csproj", "{EF2EAFAF-01CD-46BD-BE45-0125B51316A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModpackUpdater.Apps.Client", "ModpackUpdater.Apps.Client\ModpackUpdater.Apps.Client.csproj", "{415A7854-C358-4DCD-8C9E-D8413097A06D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModpackUpdater.Apps.AppUpdates", "ModpackUpdater.Apps.AppUpdates\ModpackUpdater.Apps.AppUpdates.csproj", "{7D8F9265-7BAC-4541-A6A8-168F45D0EA56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,6 +47,14 @@ Global
{EF2EAFAF-01CD-46BD-BE45-0125B51316A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF2EAFAF-01CD-46BD-BE45-0125B51316A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF2EAFAF-01CD-46BD-BE45-0125B51316A4}.Release|Any CPU.Build.0 = Release|Any CPU
{415A7854-C358-4DCD-8C9E-D8413097A06D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{415A7854-C358-4DCD-8C9E-D8413097A06D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{415A7854-C358-4DCD-8C9E-D8413097A06D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{415A7854-C358-4DCD-8C9E-D8413097A06D}.Release|Any CPU.Build.0 = Release|Any CPU
{7D8F9265-7BAC-4541-A6A8-168F45D0EA56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D8F9265-7BAC-4541-A6A8-168F45D0EA56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D8F9265-7BAC-4541-A6A8-168F45D0EA56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D8F9265-7BAC-4541-A6A8-168F45D0EA56}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -53,6 +65,8 @@ Global
{D3A92EBD-FF6E-09D0-00A1-20221AAA198E} = {96B711FA-1AF2-469B-BC02-6D1E540E8E9D}
{227A37AA-73F0-431D-B976-B9B3A8ADD8C2} = {743892CF-E482-4FBD-9BAB-02920C140F2B}
{EF2EAFAF-01CD-46BD-BE45-0125B51316A4} = {743892CF-E482-4FBD-9BAB-02920C140F2B}
{415A7854-C358-4DCD-8C9E-D8413097A06D} = {743892CF-E482-4FBD-9BAB-02920C140F2B}
{7D8F9265-7BAC-4541-A6A8-168F45D0EA56} = {743892CF-E482-4FBD-9BAB-02920C140F2B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {322E6A6B-9F3E-4E88-8945-C98A9EF613BF}

View File

@@ -0,0 +1,6 @@
namespace ModpackUpdater;
public interface IActionSet
{
IEnumerable<InstallAction> Actions { get; }
}

View File

@@ -1,10 +1,7 @@
namespace ModpackUpdater;
public interface IActionSetInfos
public interface IActionSetInfos : IActionSet
{
Version Version { get; set; }
public bool IsPublic { get; set; }
IEnumerable<InstallAction> Actions { get; }
bool IsPublic { get; set; }
}

View File

@@ -50,6 +50,9 @@ public class InstallAction
[DefaultValue(false)]
public bool IsExtra { get; set; }
[DefaultValue(null)]
public string TargetOption { get; set; }
[JsonProperty, Obsolete]
private string DownloadUrl
{

View File

@@ -13,7 +13,9 @@ public class InstallInfos : IActionSetInfos
public List<InstallAction> Actions { get; } = [];
IEnumerable<InstallAction> IActionSetInfos.Actions => Actions;
public List<InstallOptionSet> OptionSets { get; } = [];
IEnumerable<InstallAction> IActionSet.Actions => Actions;
public static InstallInfos Parse(string content)
{

View File

@@ -0,0 +1,24 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.ComponentModel;
namespace ModpackUpdater;
public class InstallOption : IActionSet
{
public string Id { get; set; }
[DefaultValue(null)]
public string Description { get; set; }
[DefaultValue(Side.Both)]
[JsonConverter(typeof(StringEnumConverter))]
public Side Side { get; set; } = Side.Both;
[DefaultValue(false)]
public bool Default { get; }
public List<UpdateAction> UninstallActions { get; } = [];
IEnumerable<InstallAction> IActionSet.Actions => UninstallActions.Cast<InstallAction>();
}

View File

@@ -0,0 +1,22 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.ComponentModel;
namespace ModpackUpdater;
public class InstallOptionSet
{
public string Id { get; set; }
[DefaultValue(null)]
public string Description { get; set; }
[DefaultValue(Side.Both)]
[JsonConverter(typeof(StringEnumConverter))]
public Side Side { get; set; } = Side.Both;
[DefaultValue(false)]
public bool Multiselect { get; set; }
public List<InstallOption> Options { get; } = [];
}

View File

@@ -0,0 +1,28 @@
namespace ModpackUpdater;
public class InstallOptionValueDictionary : Dictionary<string, bool>
{
public void SetOption(InstallOptionSet set, InstallOption option, bool value)
{
if (!set.Options.Contains(option))
throw new KeyNotFoundException("Options seems to be not a part of the option set.");
if (option.Default == value)
UnsetOption(set, option);
else
this[$"{set.Id}.{option.Id}"] = value;
}
public void UnsetOption(InstallOptionSet set, InstallOption option)
{
if (!set.Options.Contains(option))
throw new KeyNotFoundException("Options seems to be not a part of the option set.");
Remove($"{set.Id}.{option.Id}");
}
public bool GetOptionValue(InstallOptionSet set, InstallOption option)
{
if (TryGetValue($"{set.Id}.{option.Id}", out var value))
return value;
return option.Default;
}
}

View File

@@ -6,20 +6,14 @@ namespace ModpackUpdater;
public class ModpackConfig
{
public bool Maintenance { get; set; }
public string Name { get; set; }
public string UpdateUrl { get; set; }
public string InstallUrl { get; set; }
public string UnleashApiUrl { get; set; }
public string UnleashInstanceId { get; set; }
public bool PreferDirectLinks { get; set; }
public string MinecraftVersion { get; set; }
public string RefTag { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ModLoader ModLoader { get; set; }

View File

@@ -12,6 +12,7 @@ public class ModpackInfo
public Version Version { get; set; }
public string ConfigUrl { get; set; }
public string ExtrasKey { get; set; }
public InstallOptionValueDictionary Options { get; } = [];
[JsonIgnore]
public string LocaLPath { get; private set; }

View File

@@ -6,7 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pilz.Cryptography" Version="2.1.1" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
<PackageReference Include="Pilz.Extensions" Version="2.1.1" />
</ItemGroup>
</Project>

View File

@@ -13,5 +13,5 @@ public class UpdateInfo : IActionSetInfos
public List<UpdateAction> Actions { get; } = [];
IEnumerable<InstallAction> IActionSetInfos.Actions => Actions.Cast<InstallAction>();
IEnumerable<InstallAction> IActionSet.Actions => Actions.Cast<InstallAction>();
}

3
Version.cs Normal file
View File

@@ -0,0 +1,3 @@
using Pilz;
[assembly: AssemblyAppVersion(AssemblyAppVersionAttribute.EntryAssemblyVersionKey)]

6
publish-client.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -e
for dir in ModpackUpdater.Apps.Client ModpackUpdater.Apps.Client.Gui; do
( cd "$dir" && ./publish.sh "$1" )
done