mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Support downloading updates via command.
This commit is contained in:
parent
3713fa2dbe
commit
28827b720a
10 changed files with 2463 additions and 1974 deletions
50
MinecraftClient/Commands/Upgrade.cs
Normal file
50
MinecraftClient/Commands/Upgrade.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Commands
|
||||||
|
{
|
||||||
|
class Upgrade : Command
|
||||||
|
{
|
||||||
|
public override string CmdName { get { return "upgrade"; } }
|
||||||
|
public override string CmdUsage { get { return "upgrade [-f|check|cancel|download]"; } }
|
||||||
|
public override string CmdDesc { get { return string.Empty; } }
|
||||||
|
|
||||||
|
public override string Run(McClient handler, string command, Dictionary<string, object>? localVars)
|
||||||
|
{
|
||||||
|
if (HasArg(command))
|
||||||
|
{
|
||||||
|
string[] args = GetArgs(command);
|
||||||
|
return args[0] switch
|
||||||
|
{
|
||||||
|
"-f" => DownloadUpdate(force: true),
|
||||||
|
"-force" => DownloadUpdate(force: true),
|
||||||
|
"cancel" => CancelDownloadUpdate(),
|
||||||
|
"check" => CheckUpdate(),
|
||||||
|
"download" => DownloadUpdate(force: args.Length > 1 && (args[1] == "-f" || args[1] == "-force")),
|
||||||
|
_ => GetCmdDescTranslated(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return DownloadUpdate(force: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DownloadUpdate(bool force)
|
||||||
|
{
|
||||||
|
UpgradeHelper.DownloadLatestBuild(force);
|
||||||
|
return Translations.mcc_update_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CancelDownloadUpdate()
|
||||||
|
{
|
||||||
|
UpgradeHelper.CancelDownloadUpdate();
|
||||||
|
return Translations.mcc_update_cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CheckUpdate()
|
||||||
|
{
|
||||||
|
UpgradeHelper.CheckUpdate(forceUpdate: true);
|
||||||
|
return Translations.mcc_update_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
<PackageReference Include="DSharpPlus" Version="4.2.0" />
|
<PackageReference Include="DSharpPlus" Version="4.2.0" />
|
||||||
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
|
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
|
||||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="12.2.0" />
|
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="12.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MinecraftClient.Inventory.ItemPalettes;
|
using MinecraftClient.Inventory.ItemPalettes;
|
||||||
|
|
@ -200,6 +198,18 @@ namespace MinecraftClient
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.Contains("--upgrade"))
|
||||||
|
{
|
||||||
|
UpgradeHelper.HandleBlockingUpdate(forceUpgrade: false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Contains("--force-upgrade"))
|
||||||
|
{
|
||||||
|
UpgradeHelper.HandleBlockingUpdate(forceUpgrade: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Contains("--generate"))
|
if (args.Contains("--generate"))
|
||||||
{
|
{
|
||||||
string dataGenerator = "";
|
string dataGenerator = "";
|
||||||
|
|
@ -284,47 +294,7 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for updates
|
// Check for updates
|
||||||
{
|
UpgradeHelper.CheckUpdate();
|
||||||
bool needPromptUpdate = true;
|
|
||||||
if (Settings.CheckUpdate(Config.Head.CurrentVersion, Config.Head.LatestVersion))
|
|
||||||
{
|
|
||||||
needPromptUpdate = false;
|
|
||||||
ConsoleIO.WriteLineFormatted("§e" + string.Format(Translations.mcc_has_update, Settings.GithubReleaseUrl));
|
|
||||||
}
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
HttpClientHandler httpClientHandler = new() { AllowAutoRedirect = false };
|
|
||||||
HttpClient httpClient = new(httpClientHandler);
|
|
||||||
Task<HttpResponseMessage>? httpWebRequest = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
httpWebRequest = httpClient.GetAsync(Settings.GithubLatestReleaseUrl, HttpCompletionOption.ResponseHeadersRead);
|
|
||||||
httpWebRequest.Wait();
|
|
||||||
HttpResponseMessage res = httpWebRequest.Result;
|
|
||||||
if (res.Headers.Location != null)
|
|
||||||
{
|
|
||||||
Match match = Regex.Match(res.Headers.Location.ToString(), Settings.GithubReleaseUrl + @"/tag/(\d{4})(\d{2})(\d{2})-(\d+)");
|
|
||||||
if (match.Success && match.Groups.Count == 5)
|
|
||||||
{
|
|
||||||
string year = match.Groups[1].Value, month = match.Groups[2].Value, day = match.Groups[3].Value, run = match.Groups[4].Value;
|
|
||||||
string latestVersion = string.Format("GitHub build {0}, built on {1}-{2}-{3}", run, year, month, day);
|
|
||||||
if (needPromptUpdate)
|
|
||||||
if (Settings.CheckUpdate(Config.Head.CurrentVersion, Config.Head.LatestVersion))
|
|
||||||
ConsoleIO.WriteLineFormatted("§e" + string.Format(Translations.mcc_has_update, Settings.GithubReleaseUrl));
|
|
||||||
if (latestVersion != Config.Head.LatestVersion)
|
|
||||||
{
|
|
||||||
Config.Head.LatestVersion = latestVersion;
|
|
||||||
WriteBackSettings(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception) { }
|
|
||||||
finally { httpWebRequest?.Dispose(); }
|
|
||||||
httpClient.Dispose();
|
|
||||||
httpClientHandler.Dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load command-line arguments
|
// Load command-line arguments
|
||||||
if (args.Length >= 1)
|
if (args.Length >= 1)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ namespace MinecraftClient.Proxy
|
||||||
[TomlDoNotInlineObject]
|
[TomlDoNotInlineObject]
|
||||||
public class Configs
|
public class Configs
|
||||||
{
|
{
|
||||||
|
[TomlInlineComment("$Proxy.Enabled_Update$")]
|
||||||
|
public bool Enabled_Update = false;
|
||||||
|
|
||||||
[TomlInlineComment("$Proxy.Enabled_Login$")]
|
[TomlInlineComment("$Proxy.Enabled_Login$")]
|
||||||
public bool Enabled_Login = false;
|
public bool Enabled_Login = false;
|
||||||
|
|
||||||
|
|
@ -79,7 +82,7 @@ namespace MinecraftClient.Proxy
|
||||||
case Configs.ProxyType.SOCKS5: innerProxytype = ProxyType.Socks5; break;
|
case Configs.ProxyType.SOCKS5: innerProxytype = ProxyType.Socks5; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Username != "" && Config.Password != "")
|
if (!string.IsNullOrWhiteSpace(Config.Username)&& !string.IsNullOrWhiteSpace(Config.Password))
|
||||||
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port, Config.Username, Config.Password);
|
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port, Config.Username, Config.Password);
|
||||||
else
|
else
|
||||||
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port);
|
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,76 +1,96 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
Version 1.3
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
The primary goals of this format is to allow a simple XML format
|
: using a System.ComponentModel.TypeConverter
|
||||||
that is mostly human readable. The generation and parsing of the
|
: and then encoded with base64 encoding.
|
||||||
various data types are done through the TypeConverter classes
|
-->
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">1.3</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1">this is my long string</data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
[base64 mime encoded serialized .NET Framework object]
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
|
|
@ -89,13 +109,13 @@
|
||||||
<value>text/microsoft-resx</value>
|
<value>text/microsoft-resx</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="version">
|
<resheader name="version">
|
||||||
<value>1.3</value>
|
<value>2.0</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="reader">
|
<resheader name="reader">
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="AppVars.Variables" xml:space="preserve">
|
<data name="AppVars.Variables" xml:space="preserve">
|
||||||
<value>can be used in some other fields as %yourvar%
|
<value>can be used in some other fields as %yourvar%
|
||||||
|
|
@ -723,6 +743,9 @@ If the connection to the Minecraft game server is blocked by the firewall, set E
|
||||||
<data name="Proxy.Enabled_Login" xml:space="preserve">
|
<data name="Proxy.Enabled_Login" xml:space="preserve">
|
||||||
<value>Whether to connect to the login server through a proxy.</value>
|
<value>Whether to connect to the login server through a proxy.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Proxy.Enabled_Update" xml:space="preserve">
|
||||||
|
<value>Whether to download MCC updates via proxy.</value>
|
||||||
|
</data>
|
||||||
<data name="Proxy.Password" xml:space="preserve">
|
<data name="Proxy.Password" xml:space="preserve">
|
||||||
<value>Only required for password-protected proxies.</value>
|
<value>Only required for password-protected proxies.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -765,4 +788,4 @@ If the connection to the Minecraft game server is blocked by the firewall, set E
|
||||||
<data name="Signature.SignMessageInCommand" xml:space="preserve">
|
<data name="Signature.SignMessageInCommand" xml:space="preserve">
|
||||||
<value>Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me"</value>
|
<value>Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me"</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -5220,7 +5220,8 @@ namespace MinecraftClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to New version of MCC available: {0}.
|
/// Looks up a localized string similar to A new version of MCC is available and you can download it via /upgrade
|
||||||
|
///Or download it manually: {0}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string mcc_has_update {
|
internal static string mcc_has_update {
|
||||||
get {
|
get {
|
||||||
|
|
@ -5529,6 +5530,132 @@ namespace MinecraftClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to The current version is already up to date or is a development build..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_already_latest {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.already_latest", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Checking for updates has already started..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_already_running {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.already_running", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to The download has been cancelled..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_cancel {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.cancel", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Unable to check for updates from Github, please check your internet connection or enable proxy..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_check_fail {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.check_fail", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: Download is complete, decompressing..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_download_complete {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.download_complete", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: An error occurred while downloading or saving the file:.
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_download_fail {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.download_fail", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: New version detected: "{0}", download will start soon (platform: {1})..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_exist_update {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.exist_update", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to The latest release does not contain a build that matches your operating system and CPU architecture..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_platform_not_support {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.platform_not_support", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: {0:00.00}%, Downloaded {1:00.0}MB of {2:00.0}MB, Avg {3:0.0}KB/s.
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_progress {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.progress", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: Downloaded {0:00.0}MB, Avg {1:0.0}KB/s.
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_progress_type2 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.progress_type2", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Add "-f" to force the download..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_promote_force {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.promote_force", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Use "--force-upgrade" to force the download..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_promote_force_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.promote_force_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Self-updating: File saved as: {0}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_save_as {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.save_as", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Start checking for updates..
|
||||||
|
/// </summary>
|
||||||
|
internal static string mcc_update_start {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("mcc.update.start", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Please use the newly generated MinecraftClient.ini.
|
/// Looks up a localized string similar to Please use the newly generated MinecraftClient.ini.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1857,7 +1857,8 @@ You can use "/chunk status {0:0.0} {1:0.0} {2:0.0}" to check the chunk loading s
|
||||||
<value>Handshake successful. (Server ID: {0})</value>
|
<value>Handshake successful. (Server ID: {0})</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="mcc.has_update" xml:space="preserve">
|
<data name="mcc.has_update" xml:space="preserve">
|
||||||
<value>New version of MCC available: {0}</value>
|
<value>A new version of MCC is available and you can download it via /upgrade
|
||||||
|
Or download it manually: {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="mcc.help_us_translate" xml:space="preserve">
|
<data name="mcc.help_us_translate" xml:space="preserve">
|
||||||
<value>Help us translate MCC: {0}</value>
|
<value>Help us translate MCC: {0}</value>
|
||||||
|
|
@ -1962,6 +1963,48 @@ MCC is running with default settings.</value>
|
||||||
<value>Unknown or not supported MC version {0}.
|
<value>Unknown or not supported MC version {0}.
|
||||||
Switching to autodetection mode.</value>
|
Switching to autodetection mode.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="mcc.update.already_latest" xml:space="preserve">
|
||||||
|
<value>The current version is already up to date or is a development build.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.already_running" xml:space="preserve">
|
||||||
|
<value>Checking for updates has already started.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.cancel" xml:space="preserve">
|
||||||
|
<value>The download has been cancelled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.check_fail" xml:space="preserve">
|
||||||
|
<value>Unable to check for updates from Github, please check your internet connection or enable proxy.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.download_complete" xml:space="preserve">
|
||||||
|
<value>Self-updating: Download is complete, decompressing.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.download_fail" xml:space="preserve">
|
||||||
|
<value>Self-updating: An error occurred while downloading or saving the file:</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.exist_update" xml:space="preserve">
|
||||||
|
<value>Self-updating: New version detected: "{0}", download will start soon (platform: {1}).</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.platform_not_support" xml:space="preserve">
|
||||||
|
<value>The latest release does not contain a build that matches your operating system and CPU architecture.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.progress" xml:space="preserve">
|
||||||
|
<value>Self-updating: {0:00.00}%, Downloaded {1:00.0}MB of {2:00.0}MB, Avg {3:0.0}KB/s</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.progress_type2" xml:space="preserve">
|
||||||
|
<value>Self-updating: Downloaded {0:00.0}MB, Avg {1:0.0}KB/s</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.promote_force" xml:space="preserve">
|
||||||
|
<value>Add "-f" to force the download.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.promote_force_cmd" xml:space="preserve">
|
||||||
|
<value>Use "--force-upgrade" to force the download.</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.save_as" xml:space="preserve">
|
||||||
|
<value>Self-updating: File saved as: {0}</value>
|
||||||
|
</data>
|
||||||
|
<data name="mcc.update.start" xml:space="preserve">
|
||||||
|
<value>Start checking for updates.</value>
|
||||||
|
</data>
|
||||||
<data name="mcc.use_new_config" xml:space="preserve">
|
<data name="mcc.use_new_config" xml:space="preserve">
|
||||||
<value>Please use the newly generated MinecraftClient.ini</value>
|
<value>Please use the newly generated MinecraftClient.ini</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1978,4 +2021,4 @@ Logging in...</value>
|
||||||
<data name="proxy.connected" xml:space="preserve">
|
<data name="proxy.connected" xml:space="preserve">
|
||||||
<value>Connected to proxy {0}:{1}</value>
|
<value>Connected to proxy {0}:{1}</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -37,8 +37,6 @@ namespace MinecraftClient
|
||||||
public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net";
|
public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net";
|
||||||
|
|
||||||
public const string TranslationProjectUrl = "https://crwd.in/minecraft-console-client";
|
public const string TranslationProjectUrl = "https://crwd.in/minecraft-console-client";
|
||||||
public const string GithubReleaseUrl = "https://github.com/MCCTeam/Minecraft-Console-Client/releases";
|
|
||||||
public const string GithubLatestReleaseUrl = GithubReleaseUrl + "/latest";
|
|
||||||
|
|
||||||
public static GlobalConfig Config = new();
|
public static GlobalConfig Config = new();
|
||||||
|
|
||||||
|
|
@ -1688,61 +1686,6 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CheckUpdate(string? current, string? latest)
|
|
||||||
{
|
|
||||||
if (current == null || latest == null)
|
|
||||||
return false;
|
|
||||||
Regex reg = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s(\d{4})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{2}).*");
|
|
||||||
Regex reg2 = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s\w+\s(\d{2})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{4}).*");
|
|
||||||
|
|
||||||
DateTime? curTime = null, latestTime = null;
|
|
||||||
|
|
||||||
Match curMatch = reg.Match(current);
|
|
||||||
if (curMatch.Success && curMatch.Groups.Count == 5)
|
|
||||||
{
|
|
||||||
try { curTime = new(int.Parse(curMatch.Groups[2].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[4].Value)); }
|
|
||||||
catch { curTime = null; }
|
|
||||||
}
|
|
||||||
if (curTime == null)
|
|
||||||
{
|
|
||||||
curMatch = reg2.Match(current);
|
|
||||||
try { curTime = new(int.Parse(curMatch.Groups[4].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[2].Value)); }
|
|
||||||
catch { curTime = null; }
|
|
||||||
}
|
|
||||||
if (curTime == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Match latestMatch = reg.Match(latest);
|
|
||||||
if (latestMatch.Success && latestMatch.Groups.Count == 5)
|
|
||||||
{
|
|
||||||
try { latestTime = new(int.Parse(latestMatch.Groups[2].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[4].Value)); }
|
|
||||||
catch { latestTime = null; }
|
|
||||||
}
|
|
||||||
if (latestTime == null)
|
|
||||||
{
|
|
||||||
latestMatch = reg2.Match(latest);
|
|
||||||
try { latestTime = new(int.Parse(latestMatch.Groups[4].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[2].Value)); }
|
|
||||||
catch { latestTime = null; }
|
|
||||||
}
|
|
||||||
if (latestTime == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int curBuildId, latestBuildId;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
curBuildId = int.Parse(curMatch.Groups[1].Value);
|
|
||||||
latestBuildId = int.Parse(latestMatch.Groups[1].Value);
|
|
||||||
}
|
|
||||||
catch { return false; }
|
|
||||||
|
|
||||||
if (latestTime > curTime)
|
|
||||||
return true;
|
|
||||||
else if (latestTime >= curTime && latestBuildId > curBuildId)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int DoubleToTick(double time)
|
public static int DoubleToTick(double time)
|
||||||
{
|
{
|
||||||
time = Math.Min(int.MaxValue / 10, time);
|
time = Math.Min(int.MaxValue / 10, time);
|
||||||
|
|
|
||||||
321
MinecraftClient/UpgradeHelper.cs
Normal file
321
MinecraftClient/UpgradeHelper.cs
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Handlers;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MinecraftClient
|
||||||
|
{
|
||||||
|
internal static class UpgradeHelper
|
||||||
|
{
|
||||||
|
private const string GithubReleaseUrl = "https://github.com/MCCTeam/Minecraft-Console-Client/releases";
|
||||||
|
|
||||||
|
private static int running = 0; // Type: bool; 1 for running; 0 for stopped;
|
||||||
|
private static CancellationTokenSource cancellationTokenSource = new();
|
||||||
|
private static CancellationToken cancellationToken = CancellationToken.None;
|
||||||
|
|
||||||
|
private static long lastBytesTransferred = 0, minNotifyThreshold = 5 * 1024 * 1024;
|
||||||
|
private static DateTime downloadStartTime = DateTime.Now, lastNotifyTime = DateTime.Now;
|
||||||
|
private static TimeSpan minNotifyInterval = TimeSpan.FromMilliseconds(3000);
|
||||||
|
|
||||||
|
public static void CheckUpdate(bool forceUpdate = false)
|
||||||
|
{
|
||||||
|
bool needPromptUpdate = true;
|
||||||
|
if (!forceUpdate && CompareVersionInfo(Settings.Config.Head.CurrentVersion, Settings.Config.Head.LatestVersion))
|
||||||
|
{
|
||||||
|
needPromptUpdate = false;
|
||||||
|
ConsoleIO.WriteLineFormatted("§e" + string.Format(Translations.mcc_has_update, GithubReleaseUrl), true);
|
||||||
|
}
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
DoCheckUpdate(CancellationToken.None);
|
||||||
|
if (needPromptUpdate)
|
||||||
|
{
|
||||||
|
if (CompareVersionInfo(Settings.Config.Head.CurrentVersion, Settings.Config.Head.LatestVersion))
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLineFormatted("§e" + string.Format(Translations.mcc_has_update, GithubReleaseUrl), true);
|
||||||
|
}
|
||||||
|
else if (forceUpdate)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_already_latest + ' ' + Translations.mcc_update_promote_force_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DownloadLatestBuild(bool forceUpdate, bool isCommandLine = false)
|
||||||
|
{
|
||||||
|
if (Interlocked.Exchange(ref running, 1) == 1)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_already_running);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!cancellationTokenSource.TryReset())
|
||||||
|
cancellationTokenSource = new();
|
||||||
|
cancellationToken = cancellationTokenSource.Token;
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
string OSInfo = GetOSIdentifier();
|
||||||
|
if (Settings.Config.Logging.DebugMessages || string.IsNullOrEmpty(OSInfo))
|
||||||
|
ConsoleIO.WriteLine(string.Format("OS: {0}, Arch: {1}, Framework: {2}",
|
||||||
|
RuntimeInformation.OSDescription, RuntimeInformation.ProcessArchitecture, RuntimeInformation.FrameworkDescription));
|
||||||
|
if (string.IsNullOrEmpty(OSInfo))
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_platform_not_support);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string latestVersion = DoCheckUpdate(cancellationToken);
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrEmpty(latestVersion))
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_check_fail);
|
||||||
|
}
|
||||||
|
else if (!forceUpdate && !CompareVersionInfo(Settings.Config.Head.CurrentVersion, Settings.Config.Head.LatestVersion))
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_already_latest + ' ' +
|
||||||
|
(isCommandLine ? Translations.mcc_update_promote_force_cmd : Translations.mcc_update_promote_force));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(string.Format(Translations.mcc_update_exist_update, latestVersion, OSInfo));
|
||||||
|
|
||||||
|
HttpClientHandler httpClientHandler = new() { AllowAutoRedirect = true };
|
||||||
|
AddProxySettings(httpClientHandler);
|
||||||
|
|
||||||
|
ProgressMessageHandler progressMessageHandler = new(httpClientHandler);
|
||||||
|
progressMessageHandler.HttpReceiveProgress += (_, info) =>
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
if (now - lastNotifyTime > minNotifyInterval || info.BytesTransferred - lastBytesTransferred > minNotifyThreshold)
|
||||||
|
{
|
||||||
|
lastNotifyTime = now;
|
||||||
|
lastBytesTransferred = info.BytesTransferred;
|
||||||
|
if (info.TotalBytes.HasValue)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(string.Format(Translations.mcc_update_progress,
|
||||||
|
(double)info.BytesTransferred / info.TotalBytes * 100.0,
|
||||||
|
(double)info.BytesTransferred / 1024 / 1024,
|
||||||
|
(double)info.TotalBytes / 1024 / 1024,
|
||||||
|
(double)info.BytesTransferred / 1024 / (now - downloadStartTime).TotalSeconds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine(string.Format(Translations.mcc_update_progress_type2,
|
||||||
|
(double)info.BytesTransferred / 1024 / 1024,
|
||||||
|
(double)info.BytesTransferred / 1024 / (now - downloadStartTime).TotalSeconds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpClient httpClient = new(progressMessageHandler);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string downloadUrl = $"{GithubReleaseUrl}/download/{latestVersion}/MinecraftClient-{OSInfo}.zip";
|
||||||
|
using HttpResponseMessage response = await httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
|
downloadStartTime = DateTime.Now;
|
||||||
|
lastNotifyTime = DateTime.MinValue;
|
||||||
|
lastBytesTransferred = 0;
|
||||||
|
using Stream zipFileStream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
using ZipArchive zipArchive = new(zipFileStream, ZipArchiveMode.Read);
|
||||||
|
ConsoleIO.WriteLine(Translations.mcc_update_download_complete);
|
||||||
|
foreach (var archiveEntry in zipArchive.Entries)
|
||||||
|
{
|
||||||
|
if (archiveEntry.Name.StartsWith("MinecraftClient"))
|
||||||
|
{
|
||||||
|
string fileName = $"MinecraftClient-{latestVersion}{Path.GetExtension(archiveEntry.Name)}";
|
||||||
|
archiveEntry.ExtractToFile(fileName, true);
|
||||||
|
ConsoleIO.WriteLineFormatted("§e" + string.Format(Translations.mcc_update_save_as, fileName), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zipArchive.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
zipFileStream.Dispose();
|
||||||
|
response.Dispose();
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException) { }
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLine($"{Translations.mcc_update_download_fail}\n{e.GetType().Name}: {e.Message}\n{e.StackTrace}");
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.Dispose();
|
||||||
|
progressMessageHandler.Dispose();
|
||||||
|
httpClientHandler.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.Exchange(ref running, 0);
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CancelDownloadUpdate()
|
||||||
|
{
|
||||||
|
if (!cancellationTokenSource.IsCancellationRequested)
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleBlockingUpdate(bool forceUpgrade)
|
||||||
|
{
|
||||||
|
minNotifyInterval = TimeSpan.FromMilliseconds(500);
|
||||||
|
DownloadLatestBuild(forceUpgrade, true);
|
||||||
|
while (running == 1)
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DoCheckUpdate(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
string latestBuildInfo = string.Empty;
|
||||||
|
HttpClientHandler httpClientHandler = new() { AllowAutoRedirect = false };
|
||||||
|
AddProxySettings(httpClientHandler);
|
||||||
|
HttpClient httpClient = new(httpClientHandler);
|
||||||
|
Task<HttpResponseMessage>? httpWebRequest = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
httpWebRequest = httpClient.GetAsync(GithubReleaseUrl + "/latest", HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
|
httpWebRequest.Wait();
|
||||||
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
HttpResponseMessage res = httpWebRequest.Result;
|
||||||
|
if (res.Headers.Location != null)
|
||||||
|
{
|
||||||
|
Match match = Regex.Match(res.Headers.Location.ToString(), GithubReleaseUrl + @"/tag/(\d{4})(\d{2})(\d{2})-(\d+)");
|
||||||
|
if (match.Success && match.Groups.Count == 5)
|
||||||
|
{
|
||||||
|
string year = match.Groups[1].Value, month = match.Groups[2].Value, day = match.Groups[3].Value, run = match.Groups[4].Value;
|
||||||
|
string latestVersion = string.Format("GitHub build {0}, built on {1}-{2}-{3}", run, year, month, day);
|
||||||
|
latestBuildInfo = string.Format("{0}{1}{2}-{3}", year, month, day, run);
|
||||||
|
if (latestVersion != Settings.Config.Head.LatestVersion)
|
||||||
|
{
|
||||||
|
Settings.Config.Head.LatestVersion = latestVersion;
|
||||||
|
Program.WriteBackSettings(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.Dispose();
|
||||||
|
}
|
||||||
|
httpWebRequest.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
finally { httpWebRequest?.Dispose(); }
|
||||||
|
httpClient.Dispose();
|
||||||
|
httpClientHandler.Dispose();
|
||||||
|
return latestBuildInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetOSIdentifier()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
|
return "linux-arm64";
|
||||||
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
|
return "linux";
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
|
return "osx";
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
|
return "windows-x64";
|
||||||
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
|
||||||
|
return "windows-x86";
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CompareVersionInfo(string? current, string? latest)
|
||||||
|
{
|
||||||
|
if (current == null || latest == null)
|
||||||
|
return false;
|
||||||
|
Regex reg = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s(\d{4})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{2}).*");
|
||||||
|
Regex reg2 = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s\w+\s(\d{2})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{4}).*");
|
||||||
|
|
||||||
|
DateTime? curTime = null, latestTime = null;
|
||||||
|
|
||||||
|
Match curMatch = reg.Match(current);
|
||||||
|
if (curMatch.Success && curMatch.Groups.Count == 5)
|
||||||
|
{
|
||||||
|
try { curTime = new(int.Parse(curMatch.Groups[2].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[4].Value)); }
|
||||||
|
catch { curTime = null; }
|
||||||
|
}
|
||||||
|
if (curTime == null)
|
||||||
|
{
|
||||||
|
curMatch = reg2.Match(current);
|
||||||
|
try { curTime = new(int.Parse(curMatch.Groups[4].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[2].Value)); }
|
||||||
|
catch { curTime = null; }
|
||||||
|
}
|
||||||
|
if (curTime == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Match latestMatch = reg.Match(latest);
|
||||||
|
if (latestMatch.Success && latestMatch.Groups.Count == 5)
|
||||||
|
{
|
||||||
|
try { latestTime = new(int.Parse(latestMatch.Groups[2].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[4].Value)); }
|
||||||
|
catch { latestTime = null; }
|
||||||
|
}
|
||||||
|
if (latestTime == null)
|
||||||
|
{
|
||||||
|
latestMatch = reg2.Match(latest);
|
||||||
|
try { latestTime = new(int.Parse(latestMatch.Groups[4].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[2].Value)); }
|
||||||
|
catch { latestTime = null; }
|
||||||
|
}
|
||||||
|
if (latestTime == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int curBuildId, latestBuildId;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
curBuildId = int.Parse(curMatch.Groups[1].Value);
|
||||||
|
latestBuildId = int.Parse(latestMatch.Groups[1].Value);
|
||||||
|
}
|
||||||
|
catch { return false; }
|
||||||
|
|
||||||
|
if (latestTime > curTime)
|
||||||
|
return true;
|
||||||
|
else if (latestTime >= curTime && latestBuildId > curBuildId)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddProxySettings(HttpClientHandler httpClientHandler)
|
||||||
|
{
|
||||||
|
if (Settings.Config.Proxy.Enabled_Update)
|
||||||
|
{
|
||||||
|
string proxyAddress;
|
||||||
|
if (!string.IsNullOrWhiteSpace(Settings.Config.Proxy.Username) && !string.IsNullOrWhiteSpace(Settings.Config.Proxy.Password))
|
||||||
|
proxyAddress = string.Format("{0}://{3}:{4}@{1}:{2}",
|
||||||
|
Settings.Config.Proxy.Proxy_Type.ToString().ToLower(),
|
||||||
|
Settings.Config.Proxy.Server.Host,
|
||||||
|
Settings.Config.Proxy.Server.Port,
|
||||||
|
Settings.Config.Proxy.Username,
|
||||||
|
Settings.Config.Proxy.Password);
|
||||||
|
else
|
||||||
|
proxyAddress = string.Format("{0}://{1}:{2}",
|
||||||
|
Settings.Config.Proxy.Proxy_Type.ToString().ToLower(),
|
||||||
|
Settings.Config.Proxy.Server.Host, Settings.Config.Proxy.Server.Port);
|
||||||
|
httpClientHandler.Proxy = new WebProxy(proxyAddress, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue