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="DynamicExpresso.Core" Version="2.13.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.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
|
|
@ -200,6 +198,18 @@ namespace MinecraftClient
|
|||
return;
|
||||
}
|
||||
|
||||
if (args.Contains("--upgrade"))
|
||||
{
|
||||
UpgradeHelper.HandleBlockingUpdate(forceUpgrade: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Contains("--force-upgrade"))
|
||||
{
|
||||
UpgradeHelper.HandleBlockingUpdate(forceUpgrade: true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Contains("--generate"))
|
||||
{
|
||||
string dataGenerator = "";
|
||||
|
|
@ -284,47 +294,7 @@ namespace MinecraftClient
|
|||
}
|
||||
|
||||
// Check for updates
|
||||
{
|
||||
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();
|
||||
});
|
||||
}
|
||||
UpgradeHelper.CheckUpdate();
|
||||
|
||||
// Load command-line arguments
|
||||
if (args.Length >= 1)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ namespace MinecraftClient.Proxy
|
|||
[TomlDoNotInlineObject]
|
||||
public class Configs
|
||||
{
|
||||
[TomlInlineComment("$Proxy.Enabled_Update$")]
|
||||
public bool Enabled_Update = false;
|
||||
|
||||
[TomlInlineComment("$Proxy.Enabled_Login$")]
|
||||
public bool Enabled_Login = false;
|
||||
|
||||
|
|
@ -79,7 +82,7 @@ namespace MinecraftClient.Proxy
|
|||
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);
|
||||
else
|
||||
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"?>
|
||||
<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
|
||||
|
||||
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">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.
|
||||
-->
|
||||
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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<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:complexType>
|
||||
<xsd:sequence>
|
||||
<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: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="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
|
|
@ -89,13 +109,13 @@
|
|||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<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 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>
|
||||
<data name="AppVars.Variables" xml:space="preserve">
|
||||
<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">
|
||||
<value>Whether to connect to the login server through a proxy.</value>
|
||||
</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">
|
||||
<value>Only required for password-protected proxies.</value>
|
||||
</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">
|
||||
<value>Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me"</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
@ -5220,7 +5220,8 @@ namespace MinecraftClient {
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string mcc_has_update {
|
||||
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>
|
||||
/// Looks up a localized string similar to Please use the newly generated MinecraftClient.ini.
|
||||
/// </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>
|
||||
</data>
|
||||
<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 name="mcc.help_us_translate" xml:space="preserve">
|
||||
<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}.
|
||||
Switching to autodetection mode.</value>
|
||||
</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">
|
||||
<value>Please use the newly generated MinecraftClient.ini</value>
|
||||
</data>
|
||||
|
|
@ -1978,4 +2021,4 @@ Logging in...</value>
|
|||
<data name="proxy.connected" xml:space="preserve">
|
||||
<value>Connected to proxy {0}:{1}</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
@ -37,8 +37,6 @@ namespace MinecraftClient
|
|||
public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net";
|
||||
|
||||
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();
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
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