Files

876 lines
35 KiB
C#
Raw Permalink Normal View History

using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using PCL.Core.Utils.Secret;
using PCL.Core.Utils.Validate;
using PCL.Network;
using PCL.Core.App.Localization;
using PCL.Core.IO.Net;
namespace PCL;
public static class ModProfile
{
/// <summary>
/// 当前选定的档案
/// </summary>
public static McProfile selectedProfile;
/// <summary>
/// 上次选定的档案编号
/// </summary>
public static int lastUsedProfile;
/// <summary>
/// 档案列表
/// </summary>
public static List<McProfile> profileList = new();
public static bool isCreatingProfile;
/// <summary>
/// 档案操作日志
/// </summary>
public static void ProfileLog(string content, ModBase.LogLevel level = ModBase.LogLevel.Normal)
{
var output = "[Profile] " + content;
ModBase.Log(output, level);
}
#region 获取正版档案 UUID
/// <summary>
/// 根据用户名返回对应 UUID,需要多线程
/// </summary>
/// <param name="name">玩家 ID</param>
public static object McLoginMojangUuid(string name, bool throwOnNotFound)
{
if (name.Trim().Length == 0)
return ModBase.StrFill("", "0", 32);
// 从缓存获取
var uuid = ModBase.ReadIni(ModBase.pathTemp + @"Cache\Uuid\Mojang.ini", name);
if ((uuid?.Length ?? 0) == 32)
return uuid;
// 从官网获取
try
{
JsonObject gotJson = null;
var finished = false;
ModBase.RunInNewThread(() =>
{
try
{
gotJson = (JsonObject)ModNet.NetGetCodeByRequestRetry(
"https://api.mojang.com/users/profiles/minecraft/" + name, isJson: true);
}
catch (Exception ex)
{
}
finally
{
finished = true;
}
}, $"{name} Uuid Get");
while (!finished)
Thread.Sleep(50);
if (gotJson is null)
throw new FileNotFoundException("正版玩家档案不存在(" + name + "");
uuid = (string)(gotJson["id"] ?? "");
}
catch (Exception ex)
{
ModBase.Log(ex, "从官网获取正版 UUID 失败(" + name + "");
if (!throwOnNotFound && ex is FileNotFoundException)
uuid = GetOfflineUuid(name, isLegacy: true); // 玩家档案不存在
else
throw new Exception("从官网获取正版 UUID 失败", ex);
}
// 写入缓存
if ((uuid?.Length ?? 0) != 32)
throw new Exception("获取的正版 UUID 长度不足(" + uuid + "");
ModBase.WriteIni(ModBase.pathTemp + @"Cache\Uuid\Mojang.ini", name, uuid);
return uuid;
}
#endregion
#region 类型声明
public class McProfile
{
public string AccessToken;
public string ClientToken;
/// <summary>
/// 档案描述,暂时没做功能
/// </summary>
public string Desc;
/// <summary>
/// 联网验证档案的验证有效期
/// </summary>
public long Expires;
/// <summary>
/// 用于识别正版档案的 ID 标识符
/// </summary>
[Obsolete("暂时弃用,应当使用 AccessToken 与 RefreshToken")]
public string IdentityId;
/// <summary>
/// 登录用户名,用于第三方验证
/// </summary>
public string Name;
/// <summary>
/// 登录密码,用于第三方验证
/// </summary>
public string Password;
/// <summary>
/// 原始 JSON 数据,用于正版验证部分功能
/// </summary>
public string RawJson;
public string RefreshToken;
/// <summary>
/// 验证服务器地址,用于第三方验证
/// </summary>
public string Server;
/// <summary>
/// 验证服务器名称,来自第三方验证服务器返回的 Metadata
/// </summary>
public string ServerName;
/// <summary>
/// 用于档案列表头像显示的皮肤 ID
/// </summary>
public string SkinHeadId;
/// <summary>
/// 档案类型
/// </summary>
public ModLaunch.McLoginType Type;
/// <summary>
/// 玩家 ID
/// </summary>
public string Username;
public string Uuid;
}
#endregion
#region 读写档案
/// <summary>
/// 重新获取已有档案列表
/// </summary>
public static void GetProfile()
{
ProfileLog("开始获取本地档案");
profileList.Clear();
var profilePath = Path.Combine(ModBase.pathAppdataConfig, "profiles.json");
try
{
if (!Directory.Exists(ModBase.pathAppdataConfig))
Directory.CreateDirectory(ModBase.pathAppdataConfig);
if (!File.Exists(profilePath))
{
File.Create(profilePath).Close();
ModBase.WriteFile(profilePath, "{\"lastUsed\":0,\"profiles\":[]}"); // 创建档案列表文件
}
var profileJobj = ModBase.GetJson(ModBase.ReadFile(profilePath));
lastUsedProfile = (int)profileJobj["lastUsed"];
var profileListJobj = (JsonArray)profileJobj["profiles"];
foreach (var Profile in profileListJobj)
{
McProfile newProfile = null;
if ((string)Profile["type"] == "microsoft")
newProfile = new McProfile
{
Type = ModLaunch.McLoginType.Ms,
Uuid = (string)Profile["uuid"],
Username = (string)Profile["username"],
AccessToken = EncryptHelper.SecretDecrypt((string?)Profile["accessToken"]),
RefreshToken = EncryptHelper.SecretDecrypt((string?)Profile["refreshToken"]),
Expires = (long)Profile["expires"],
Desc = (string)Profile["desc"],
RawJson = EncryptHelper.SecretDecrypt((string?)Profile["rawJson"]),
SkinHeadId = (string)Profile["skinHeadId"]
};
else if ((string)Profile["type"] == "authlib")
newProfile = new McProfile
{
Type = ModLaunch.McLoginType.Auth,
Uuid = (string)Profile["uuid"],
Username = (string)Profile["username"],
AccessToken = EncryptHelper.SecretDecrypt((string?)Profile["accessToken"]),
RefreshToken = EncryptHelper.SecretDecrypt((string?)Profile["refreshToken"]),
Expires = (long)Profile["expires"],
Server = (string)Profile["server"],
ServerName = (string)Profile["serverName"],
Name = EncryptHelper.SecretDecrypt((string?)Profile["name"]),
Password = EncryptHelper.SecretDecrypt((string?)Profile["password"]),
ClientToken = EncryptHelper.SecretDecrypt((string?)Profile["clientToken"]),
Desc = (string)Profile["desc"],
SkinHeadId = (string)Profile["skinHeadId"]
};
else
newProfile = new McProfile
{
Type = ModLaunch.McLoginType.Legacy,
Uuid = (string)Profile["uuid"],
Username = (string)Profile["username"],
Desc = (string)Profile["desc"],
SkinHeadId = (string)Profile["skinHeadId"]
};
profileList.Add(newProfile);
}
ProfileLog($"获取到 {profileList.Count} 个档案");
}
catch (Exception ex)
{
try
{
var profilePathBak =
Path.Combine(ModBase.pathAppdataConfig, $"profiles.json.bak{DateTime.Now.ToBinary()}");
File.Move(profilePath, profilePathBak);
}
catch (Exception ex1)
{
}
ModBase.Log(
ex,
Lang.Text("Launch.Account.Profile.Error.Corrupted"),
ModBase.LogLevel.Msgbox,
userSummary: Lang.Text("Launch.Account.Profile.Error.Corrupted"));
}
}
/// <summary>
/// 以当前的档案列表写入配置文件
/// </summary>
public static void SaveProfile(JsonArray listJson = null)
{
try
{
var json = new JsonObject();
if (listJson is not null)
{
json = new JsonObject { { "lastUsed", lastUsedProfile }, { "profiles", listJson } };
}
else
{
var list = new JsonArray();
foreach (var Profile in profileList)
{
JsonObject profileJobj = null;
if (Profile.Type == ModLaunch.McLoginType.Ms)
profileJobj = new JsonObject
{
{ "type", "microsoft" }, { "uuid", Profile.Uuid }, { "username", Profile.Username },
{ "accessToken", EncryptHelper.SecretEncrypt(Profile.AccessToken) },
{ "refreshToken", EncryptHelper.SecretEncrypt(Profile.RefreshToken) },
{ "expires", Profile.Expires }, { "desc", Profile.Desc },
{ "rawJson", EncryptHelper.SecretEncrypt(Profile.RawJson) },
{ "skinHeadId", Profile.SkinHeadId }
};
else if (Profile.Type == ModLaunch.McLoginType.Auth)
profileJobj = new JsonObject
{
{ "type", "authlib" }, { "uuid", Profile.Uuid }, { "username", Profile.Username },
{ "accessToken", EncryptHelper.SecretEncrypt(Profile.AccessToken) },
{ "refreshToken", EncryptHelper.SecretEncrypt(Profile.RefreshToken) },
{ "expires", Profile.Expires }, { "server", Profile.Server },
{ "serverName", Profile.ServerName }, { "name", EncryptHelper.SecretEncrypt(Profile.Name) },
{ "password", EncryptHelper.SecretEncrypt(Profile.Password) },
{ "clientToken", EncryptHelper.SecretEncrypt(Profile.ClientToken) },
{ "desc", Profile.Desc }, { "skinHeadId", Profile.SkinHeadId }
};
else
profileJobj = new JsonObject
{
{ "type", "offline" }, { "uuid", Profile.Uuid }, { "username", Profile.Username },
{ "desc", Profile.Desc }, { "skinHeadId", Profile.SkinHeadId }
};
list.Add(profileJobj);
}
ProfileLog($"开始保存档案,共 {list.Count} 个");
json = new JsonObject { { "lastUsed", lastUsedProfile }, { "profiles", list } };
}
var actualFile = Path.Combine(ModBase.pathAppdataConfig, "profiles.json");
var tempFile = actualFile + ".tmp";
var bakFile = actualFile + ".bak";
File.WriteAllBytes(tempFile, Encoding.UTF8.GetBytes(json.ToJsonString()));
if (File.Exists(actualFile))
File.Replace(tempFile, actualFile, bakFile);
else
File.Move(tempFile, actualFile);
ProfileLog("档案已保存");
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Launch.Account.Profile.Error.Write"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Launch.Account.Profile.Error.Write"));
}
}
#endregion
#region 新建与编辑
/// <summary>
/// 新建档案
/// </summary>
public static void CreateProfile()
{
int? selectedAuthTypeNum = default; // 验证类型序号
ModBase.RunInUiWait(() =>
{
List<IMyRadio> authTypeList;
#if DEBUG || DEBUGCI
authTypeList = _GetAvailableProfileSelection(true);
#else
var hasMinecraftAccount = profileList.Any(x => x.Type == ModLaunch.McLoginType.Ms);
var restricted = Lang.IsFeaturesUnrestricted && profileList.Count > 0;
var hasNetwork = NetworkHelper.IsNetworkAvailable();
if (hasMinecraftAccount || restricted || !hasNetwork)
authTypeList = _GetAvailableProfileSelection(true);
else
authTypeList = _GetAvailableProfileSelection(false);
#endif
selectedAuthTypeNum = ModMain.MyMsgBoxSelect(authTypeList, Lang.Text("Launch.Account.Profile.Create.SelectAuthType.Title"), Lang.Text("Common.Action.Continue"), Lang.Text("Common.Action.Cancel"));
});
if (selectedAuthTypeNum is null)
return;
isCreatingProfile = true;
if (selectedAuthTypeNum.HasValue && selectedAuthTypeNum.Value == 0) // 正版验证
ModBase.RunInUi(() => ModMain.frmLaunchLeft.RefreshPage(true, ModLaunch.McLoginType.Ms));
else if (selectedAuthTypeNum.HasValue && selectedAuthTypeNum.Value == 1) // 第三方验证
ModBase.RunInUi(() => ModMain.frmLaunchLeft.RefreshPage(true, ModLaunch.McLoginType.Auth));
else // 离线验证
ModBase.RunInUi(() => ModMain.frmLaunchLeft.RefreshPage(true, ModLaunch.McLoginType.Legacy));
}
private static List<IMyRadio> _GetAvailableProfileSelection(bool includeOfflineAndThirdParty) => includeOfflineAndThirdParty switch
{
true =>
[
new MyListItem
{
Title = Lang.Text("Launch.Account.Type.Microsoft"),
Type = MyListItem.CheckType.RadioBox,
SvgIcon = "lucide/shield-check"
},
new MyListItem
{
Title = Lang.Text("Launch.Account.Type.ThirdParty"),
Type = MyListItem.CheckType.RadioBox,
SvgIcon = "lucide/network"
},
new MyListItem
{
Title = Lang.Text("Launch.Account.Type.Offline"),
Type = MyListItem.CheckType.RadioBox,
SvgIcon = "lucide/link-2-off"
}
],
_ =>
[
new MyListItem
{
Title = Lang.Text("Launch.Account.Type.Microsoft"),
Type = MyListItem.CheckType.RadioBox,
SvgIcon = "lucide/shield-check"
}
]
};
/// <summary>
/// 编辑当前档案的 ID
/// </summary>
public static void EditProfileId()
{
if (selectedProfile.Type == ModLaunch.McLoginType.Ms)
{
string newUsername = null;
ModBase.RunInUiWait(() => newUsername = ModMain.MyMsgBoxInput(Lang.Text("Launch.Account.Profile.EditPlayerId.Title"), Lang.Text("Launch.Account.Profile.EditPlayerId.MicrosoftWarning"),
selectedProfile.Username,
[new StringLengthValidator(3, 16), new RegexValidator("([A-z]|[0-9]|_)+")],
Lang.Text("Launch.Account.Profile.EditPlayerId.Hint"), Lang.Text("Common.Action.Confirm")));
if (string.IsNullOrEmpty(newUsername))
return;
if (string.IsNullOrWhiteSpace(newUsername))
{
HintService.Hint(Lang.Text("Launch.Account.Profile.EditPlayerId.Empty"));
return;
}
if (ModMain.MyMsgBox(Lang.Text("Launch.Account.Profile.EditPlayerId.Confirm.Message"), Lang.Text("Launch.Account.Profile.EditPlayerId.Confirm.Title"), Lang.Text("Common.Action.Continue"), Lang.Text("Common.Action.Cancel"), isWarn: true) == 2)
return;
// 更新档案信息
// 刷新页面信息
ModBase.RunInNewThread(() =>
{
try
{
var checkResult = (JsonObject)ModBase.GetJson(Requester.Fetch(
$"https://api.minecraftservices.com/minecraft/profile/name/{newUsername}/available",
new FetchParam
{
Headers = new Dictionary<string, string>
{ { "Authorization", "Bearer " + selectedProfile.AccessToken } }
}));
if ((string)checkResult["status"] == "DUPLICATE")
{
ModMain.MyMsgBox(Lang.Text("Launch.Account.Profile.EditPlayerId.Duplicate"), Lang.Text("Launch.Account.Profile.EditPlayerId.Failed.Title"), Lang.Text("Common.Action.Confirm"), isWarn: true);
return;
}
if ((string)checkResult["status"] == "NOT_ALLOWED")
{
ModMain.MyMsgBox(Lang.Text("Launch.Account.Profile.EditPlayerId.NotAllowed"), Lang.Text("Launch.Account.Profile.EditPlayerId.Failed.Title"), Lang.Text("Common.Action.Confirm"), isWarn: true);
return;
}
var result = Requester.Fetch(
$"https://api.minecraftservices.com/minecraft/profile/name/{newUsername}",
new FetchParam
{
Method = "PUT",
ContentType = "application/json",
Headers = new Dictionary<string, string>
{ { "Authorization", "Bearer " + selectedProfile.AccessToken } }
});
var resultJson = (JsonObject)ModBase.GetJson(result);
HintService.Hint(Lang.Text("Launch.Account.Profile.EditPlayerId.Success", resultJson["name"]), HintType.Success);
profileList.Remove(selectedProfile);
selectedProfile.Username = (string)resultJson["name"];
profileList.Add(selectedProfile);
lastUsedProfile = profileList.Count - 1;
// 本方法在后台线程执行(阻塞网络请求),下面操作 WPF 控件,必须切回 UI 线程,
// 否则触发线程亲和性异常——与本文件其他从后台线程刷新界面处一样用 RunInUi 包裹。
ModBase.RunInUi(() =>
{
// 改名成功后正处于档案页(ProfileSkin),此时 RefreshPage 因目标页与当前页相同会提前
// 返回、不刷新显示的玩家 ID,需显式 Reload 当前档案页,使新 ID 立即生效。
ModMain.frmLoginProfileSkin?.Reload();
ModMain.frmLaunchLeft.RefreshPage(true);
});
SaveProfile();
}
catch (HttpRequestException ex)
{
var exSummary = ex.ToString();
if (exSummary.Contains("403"))
ModMain.MyMsgBox(Lang.Text("Launch.Account.Profile.EditPlayerId.Cooldown"), Lang.Text("Launch.Account.Profile.EditPlayerId.Failed.Title"), Lang.Text("Common.Action.Confirm"));
else
ModBase.Log(
ex,
Lang.Text("Launch.Account.Profile.Error.ChangeId"),
ModBase.LogLevel.Msgbox,
userSummary: Lang.Text("Launch.Account.Profile.Error.ChangeId"));
}
});
}
else if (selectedProfile.Type == ModLaunch.McLoginType.Auth)
{
var server = selectedProfile.Server;
ModBase.OpenWebsite(server.Replace("/api/yggdrasil/authserver" + (server.EndsWithF("/") ? "/" : ""),
"/user/profile"));
}
else
{
string newUsername = null;
ModBase.RunInUiWait(() => newUsername = ModMain.MyMsgBoxInput(Lang.Text("Launch.Account.Profile.EditPlayerId.Title"),
defaultInput: selectedProfile.Username,
validateRules: [new StringLengthValidator(3, 16), new RegexValidator("([A-z]|[0-9]|_)+")],
hintText: Lang.Text("Launch.Account.Profile.EditPlayerId.Hint"), button1: Lang.Text("Common.Action.Confirm"), button2: Lang.Text("Common.Action.Cancel")));
if (string.IsNullOrEmpty(newUsername))
return;
EditOfflineUuid(selectedProfile, GetOfflineUuid(newUsername));
}
}
/// <summary>
/// 编辑离线档案的 UUID
/// </summary>
/// <param name="profile">目标档案</param>
public static void EditOfflineUuid(McProfile profile, string uuid = null)
{
var profileIndex = profileList.IndexOf(profile);
string newUuid;
if (uuid is not null)
{
newUuid = uuid;
goto Write;
}
int uuidType;
int? uuidTypeInput = default;
ModBase.RunInUiWait(() =>
{
var uuidTypeList = new List<IMyRadio>
{
new MyRadioBox { Text = Lang.Text("Launch.Account.Profile.Uuid.Standard") }, new MyRadioBox { Text = Lang.Text("Launch.Account.Profile.Uuid.Legacy") },
new MyRadioBox { Text = Lang.Text("Common.Option.Customize") }
};
uuidTypeInput = ModMain.MyMsgBoxSelect(uuidTypeList, Lang.Text("Launch.Account.Profile.Uuid.SelectType.Title"), Lang.Text("Common.Action.Continue"), Lang.Text("Common.Action.Cancel"));
});
if (uuidTypeInput is null)
return;
uuidType = (int)uuidTypeInput;
if (uuidType == 0)
newUuid = GetOfflineUuid(profile.Username);
else if (uuidType == 1)
newUuid = GetOfflineUuid(profile.Username, isLegacy: true);
else
newUuid = ModMain.MyMsgBoxInput(Lang.Text("Launch.Account.Profile.Uuid.ChangeTitle", profile.Username), defaultInput: profile.Uuid,
hintText: Lang.Text("Launch.Account.Profile.Uuid.Hint"),
validateRules:
[new StringLengthValidator(32, 32), new RegexValidator("([A-z]|[0-9]){32}", Lang.Text("Launch.Account.Profile.Uuid.InvalidChars"))],
button1: Lang.Text("Common.Action.Continue"), button2: Lang.Text("Common.Action.Cancel"));
if (string.IsNullOrEmpty(newUuid))
return;
Write: ;
profileList[profileIndex].Uuid = newUuid;
selectedProfile = profileList[profileIndex];
SaveProfile();
HintService.Hint(Lang.Text("Launch.Account.Profile.Saved"), HintType.Success);
}
/// <summary>
/// 编辑指定档案的验证服务器显示名称
/// </summary>
public static void EditAuthServerName(McProfile profile, string serverName)
{
var profileIndex = profileList.IndexOf(profile);
profileList[profileIndex].ServerName = serverName;
SaveProfile();
HintService.Hint(Lang.Text("Launch.Account.Profile.Saved"), HintType.Success);
}
/// <summary>
/// 删除特定档案
/// </summary>
/// <param name="profile">目标档案</param>
public static void RemoveProfile(McProfile profile)
{
profileList.Remove(profile);
lastUsedProfile = default;
SaveProfile();
HintService.Hint(Lang.Text("Launch.Account.Profile.Deleted"), HintType.Success);
}
#endregion
#region 离线 UUID 获取
/// <summary>
/// 获取离线 UUID
/// </summary>
/// <param name="userName">玩家 ID</param>
/// <param name="isSplited">返回的 UUID 是否有连字符分割</param>
/// <param name="isLegacy">是否使用旧版 PCL 生成方式,若为 True 则返回的 UUID 总是不带连字符</param>
public static string GetOfflineUuid(string userName, bool isSplited = false, bool isLegacy = false)
{
if (isLegacy)
{
var fullUuid = ModBase.StrFill(userName.Length.ToString("X"), "0", 16) +
ModBase.StrFill(ModBase.GetHash(userName).ToString("X"), "0", 16);
return fullUuid.Substring(0, 12) + "3" + fullUuid.Substring(13, 3) + "9" + fullUuid.Substring(17, 15);
}
var md5Hash = MD5.Create();
var hash = md5Hash.ComputeHash(Encoding.UTF8.GetBytes("OfflinePlayer:" + userName));
hash[6] = (byte)(hash[6] & 0xF);
hash[6] = (byte)(hash[6] | 0x30);
hash[8] = (byte)(hash[8] & 0x3F);
hash[8] = (byte)(hash[8] | 0x80);
var parsed = new Guid(ToUuidString(hash));
ProfileLog("获取到离线 UUID: " + parsed);
if (isSplited) return parsed.ToString();
return parsed.ToString().Replace("-", "");
}
private static string ToUuidString(byte[] bytes)
{
var msb = 0L;
var lsb = 0L;
for (var i = 0; i <= 7; i++)
msb = (msb << 8) | (bytes[i] & 0xFF);
for (var i = 8; i <= 15; i++)
lsb = (lsb << 8) | (bytes[i] & 0xFF);
return $"{Digits(msb >> 32, 8)}-{Digits(msb >> 16, 4)}-{Digits(msb, 4)}-{Digits(lsb >> 48, 4)}-{Digits(lsb, 12)}";
}
private static object Digits(long val, int digs)
{
var hi = 1L << (digs * 4);
return (hi | (val & (hi - 1L))).ToString("X").Substring(1);
}
#endregion
#region 档案信息获取
/// <summary>
/// 获取档案详情信息用于显示
/// </summary>
/// <param name="profile">目标档案</param>
/// <returns>显示的详情信息</returns>
public static object GetProfileInfo(McProfile profile)
{
string info = null;
if (profile.Type == ModLaunch.McLoginType.Auth)
{
info += Lang.Text("Launch.Account.Type.ThirdParty");
if (!string.IsNullOrWhiteSpace(profile.ServerName))
info += $" / {profile.ServerName}";
}
else if (profile.Type == ModLaunch.McLoginType.Ms)
{
info += Lang.Text("Launch.Account.Type.Microsoft");
}
else
{
info += Lang.Text("Launch.Account.Type.Offline");
}
if (!string.IsNullOrWhiteSpace(profile.Desc))
info += $"{profile.Desc}";
return info;
}
/// <summary>
/// 获取当前档案的验证信息。
/// <param name="targetAuthType">验证类型,若为新档案需填</param>
/// </summary>
public static ModLaunch.McLoginData GetLoginData(ModLaunch.McLoginType targetAuthType = default)
{
ModLaunch.McLoginType authType = default;
if (selectedProfile is null) // 新档案
{
if (targetAuthType != default)
authType = targetAuthType;
else
authType = ModLaunch.McLoginType.Legacy;
if (authType == ModLaunch.McLoginType.Auth)
return new ModLaunch.McLoginServer(ModLaunch.McLoginType.Auth)
{
Description = "Authlib-Injector",
LoginType = ModLaunch.McLoginType.Auth,
IsExist = ModMain.frmLoginAuth is null
};
if (authType == ModLaunch.McLoginType.Ms) return new ModLaunch.McLoginMs();
return new ModLaunch.McLoginLegacy();
}
// 已有档案
authType = selectedProfile.Type;
if (authType == ModLaunch.McLoginType.Auth)
return new ModLaunch.McLoginServer(ModLaunch.McLoginType.Auth)
{
BaseUrl = selectedProfile.Server,
UserName = selectedProfile.Name,
Password = selectedProfile.Password,
Description = "Authlib-Injector",
LoginType = ModLaunch.McLoginType.Auth,
IsExist = ModMain.frmLoginAuth is null
};
if (authType == ModLaunch.McLoginType.Ms)
{
if (ModLaunch.mcLoginMsLoader.State == ModBase.LoadState.Finished)
return new ModLaunch.McLoginMs
{
OAuthRefreshToken = selectedProfile.RefreshToken,
UserName = selectedProfile.Username,
AccessToken = selectedProfile.AccessToken,
Uuid = selectedProfile.Uuid,
ProfileJson = selectedProfile.RawJson
};
return new ModLaunch.McLoginMs
{ OAuthRefreshToken = selectedProfile.RefreshToken, UserName = selectedProfile.Name };
}
return new ModLaunch.McLoginLegacy { UserName = selectedProfile.Username, Uuid = selectedProfile.Uuid };
}
/// <summary>
/// 检查当前档案是否有效
/// </summary>
/// <returns>若档案验证有效,则返回空字符串,否则返回错误原因</returns>
public static string IsProfileValid()
{
switch (selectedProfile.Type)
{
case ModLaunch.McLoginType.Legacy:
{
if (string.IsNullOrEmpty(selectedProfile.Username.Trim()))
return Lang.Text("Launch.Account.Profile.Validation.EmptyUsername");
if (selectedProfile.Username.Contains("\""))
return Lang.Text("Launch.Account.Profile.Validation.QuoteInUsername");
if (ModInstanceList.McMcInstanceSelected is not null && ModInstanceList.McMcInstanceSelected.Info.Drop >= 203 &&
selectedProfile.Username.Trim().Length > 16) return Lang.Text("Launch.Account.Profile.Validation.UsernameTooLong");
return "";
}
case ModLaunch.McLoginType.Ms:
{
return "";
}
case ModLaunch.McLoginType.Auth:
{
return "";
}
}
return Lang.Text("Launch.Account.Profile.Validation.UnknownAuthType");
}
#endregion
#region 皮肤
private static bool _isMsSkinChanging;
public static void ChangeSkinMs()
{
// 检查条件,获取新皮肤
if (_isMsSkinChanging)
{
HintService.Hint(Lang.Text("Launch.Skin.Change.Busy"));
return;
}
if (ModLaunch.mcLoginLoader.State == ModBase.LoadState.Failed)
{
HintService.Hint(Lang.Text("Launch.Skin.Change.LoginFailed"), HintType.Error);
return;
}
var skinInfo = ModSkin.McSkinSelect();
if (!skinInfo.IsVaild)
return;
HintService.Hint(Lang.Text("Launch.Skin.Change.Starting"));
_isMsSkinChanging = true;
// 开始实际获取
// 获取登录信息
// 获取新皮肤地址
ModBase.RunInNewThread(() =>
{
try
{
Retry: ;
if (ModLaunch.mcLoginMsLoader.State == ModBase.LoadState.Loading)
ModLaunch.mcLoginMsLoader.WaitForExit();
if (ModLaunch.mcLoginMsLoader.State != ModBase.LoadState.Finished)
ModLaunch.mcLoginMsLoader.WaitForExit(GetLoginData());
if (ModLaunch.mcLoginMsLoader.State != ModBase.LoadState.Finished)
{
HintService.Hint(Lang.Text("Launch.Skin.Change.LoginFailed"), HintType.Error);
return;
}
var accessToken = selectedProfile.AccessToken;
var headers = new Dictionary<string, string>();
headers.Add("Authorization", $"Bearer {accessToken}");
headers.Add("Accept", "*/*");
headers.Add("User-Agent", "MojangSharp/0.1");
var contents = new MultipartFormDataContent
{
{ new StringContent(skinInfo.IsSlim ? "slim" : "classic"), "variant" },
{
new ByteArrayContent(ModBase.ReadFileBytes(skinInfo.LocalFile)), "file",
ModBase.GetFileNameFromPath(skinInfo.LocalFile)
}
};
var res = Requester.Fetch("https://api.minecraftservices.com/minecraft/profile/skins",
new FetchParam
{
Method = "POST",
Content = contents,
Headers = headers
});
if (res.Contains("request requires user authentication"))
{
HintService.Hint(Lang.Text("Launch.Skin.Change.Reauthenticating"));
ModLaunch.mcLoginMsLoader.Start(GetLoginData(), true);
goto Retry;
}
if (res.Contains("\"error\""))
{
HintService.Hint(
Lang.Text(
"Launch.Skin.Change.FailedWithDetail",
((JsonObject)ModBase.GetJson(res))["error"]?.ToString() ?? res),
HintType.Error);
return;
}
ModBase.Log("[Skin] 皮肤修改返回值:" + "\r\n" + res);
var resultJson = (JsonObject)ModBase.GetJson(res);
if (resultJson.ContainsKey("errorMessage")) throw new Exception(resultJson["errorMessage"].ToString());
foreach (var skinNode in resultJson["skins"].AsArray()) { var skin = skinNode.AsObject();
if (skin["state"].ToString() == "ACTIVE")
{
MySkin.ReloadCache((string)skin["url"]);
return;
} }
throw new Exception("未知错误(" + res + "");
}
catch (Exception ex)
{
if (ex.GetType().Equals(typeof(TaskCanceledException)))
HintService.Hint(
Lang.Text("Launch.Skin.Change.Timeout.WithDetail", ex.ToString()),
HintType.Error);
else
ModBase.Log(
ex,
Lang.Text("Launch.Account.Profile.Error.ChangeSkin"),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Launch.Account.Profile.Error.ChangeSkin"));
}
finally
{
_isMsSkinChanging = false;
}
}, "Ms Skin Upload"); // 等待登录结束
// #5309
}
#endregion
}