初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using PCL.Core.App;
|
||||
using PCL.Core.App.Localization;
|
||||
using PCL.Core.UI.Controls;
|
||||
using PCL.Core.Utils;
|
||||
using PCL.Core.IO.Net.Http;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PCL;
|
||||
|
||||
public partial class MyMsgLogin
|
||||
{
|
||||
private readonly JsonObject data;
|
||||
private string deviceCode; // 用于轮询的设备代码
|
||||
private string oAuthUrl = ""; // OAuth 轮询验证地址
|
||||
private string userCode; // 需要用户在网页上输入的设备代码
|
||||
private string website; // 验证网页的网址
|
||||
private Task? workingThread;
|
||||
|
||||
public MyMsgLogin()
|
||||
{
|
||||
InitializeComponent();
|
||||
// Handles
|
||||
Loaded += Load;
|
||||
Btn1.Click += Btn1_Click;
|
||||
Btn3.Click += Btn3_Click;
|
||||
PanBorder.MouseLeftButtonDown += Drag;
|
||||
LabTitle.MouseLeftButtonDown += Drag;
|
||||
}
|
||||
|
||||
private void Finished(object result)
|
||||
{
|
||||
if (myConverter.IsExited)
|
||||
return;
|
||||
myConverter.IsExited = true;
|
||||
myConverter.Result = result;
|
||||
ModBase.RunInUi(Close);
|
||||
Thread.Sleep(200);
|
||||
ModMain.frmMain.ShowWindowToTop();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
userCode = (string)data["user_code"];
|
||||
deviceCode = (string)data["device_code"];
|
||||
ModBase.ClipboardSet(deviceCode);
|
||||
if (data["verification_uri_complete"] is not null)
|
||||
{
|
||||
website = (string)data["verification_uri_complete"];
|
||||
LabCaption.Text = Lang.Text("Launch.Account.LoginDialog.MicrosoftInstructions.WithAutoFill", userCode, website);
|
||||
}
|
||||
else
|
||||
{
|
||||
website = (string)data["verification_uri"];
|
||||
LabCaption.Text = Lang.Text("Launch.Account.LoginDialog.MicrosoftInstructions", userCode, website);
|
||||
}
|
||||
|
||||
// 设置 UI
|
||||
LabTitle.Text = Lang.Text("Launch.Account.LoginDialog.MinecraftLogin");
|
||||
CustomEventService.SetEventData(Btn1, website);
|
||||
CustomEventService.SetEventData(Btn2, userCode);
|
||||
// 启动工作线程
|
||||
workingThread = WorkThreadAsync();
|
||||
}
|
||||
|
||||
private record ErrorBody(
|
||||
[property: JsonPropertyName("error")] string Error,
|
||||
[property: JsonPropertyName("error_description")] string Desc);
|
||||
|
||||
private async Task WorkThreadAsync()
|
||||
{
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
if (myConverter.IsExited)
|
||||
return;
|
||||
ModBase.OpenWebsite(website);
|
||||
ModBase.ClipboardSet(userCode);
|
||||
var delayTime = (data["interval"].ToObject<int>() - 1) * 1000;
|
||||
// 轮询
|
||||
var unknownFailureCount = 0;
|
||||
while (!myConverter.IsExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bodyData = $"grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id={Secrets.MSOAuthClientId}&device_code={deviceCode}&scope=XboxLive.signin%20offline_access";
|
||||
using var result = await HttpRequest
|
||||
.Create("https://login.microsoftonline.com/consumers/oauth2/v2.0/token")
|
||||
.WithFormContent(bodyData)
|
||||
.SendAsync(enableLogging: false)
|
||||
.ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
var error = await result.AsJsonAsync<ErrorBody>()
|
||||
.ConfigureAwait(false);
|
||||
switch(error?.Error)
|
||||
{
|
||||
case "authorization_pending":
|
||||
{
|
||||
await Task.Delay(delayTime)
|
||||
.ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception(error?.Error ?? "Unable to get body");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取结果
|
||||
var ctx = await result.AsStringAsync().ConfigureAwait(false);
|
||||
var resultJson = (JsonObject)ModBase.GetJson(ctx);
|
||||
ModProfile.ProfileLog($"令牌过期时间:{resultJson["expires_in"]} 秒");
|
||||
HintService.Hint(Lang.Text("Launch.Account.LoginDialog.Success"), HintType.Success);
|
||||
Finished(new[] { resultJson["access_token"].ToString(), resultJson["refresh_token"].ToString() });
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (unknownFailureCount <= 2)
|
||||
{
|
||||
unknownFailureCount += 1;
|
||||
ModBase.Log(ex, $"正版验证轮询第 {unknownFailureCount} 次失败");
|
||||
ModBase.Log(ex.Message);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Finished(new Exception(Lang.Text("Launch.Account.LoginDialog.PollingFailed"), ex));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region 弹窗
|
||||
|
||||
private readonly ModMain.MyMsgBoxConverter myConverter;
|
||||
private readonly int uuid = ModBase.GetUuid();
|
||||
|
||||
public MyMsgLogin(ModMain.MyMsgBoxConverter converter)
|
||||
{
|
||||
try
|
||||
{
|
||||
InitializeComponent();
|
||||
Btn1.Name += ModBase.GetUuid();
|
||||
Btn2.Name += ModBase.GetUuid();
|
||||
Btn3.Name += ModBase.GetUuid();
|
||||
myConverter = converter;
|
||||
ShapeLine.StrokeThickness = ModBase.GetWPFSize(1d);
|
||||
data = (JsonObject)converter.Content;
|
||||
oAuthUrl = converter.AuthUrl?.ToString() ?? "";
|
||||
Init();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModBase.Log(
|
||||
ex,
|
||||
Lang.Text("Launch.Account.LoginDialog.Error.Init"),
|
||||
ModBase.LogLevel.Hint,
|
||||
userSummary: Lang.Text("Launch.Account.LoginDialog.Error.Init"));
|
||||
}
|
||||
|
||||
Loaded += Load;
|
||||
}
|
||||
|
||||
private void Load(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 动画
|
||||
Opacity = 0d;
|
||||
ModAnimation.AniStart(
|
||||
ModAnimation.AaColor(ModMain.frmMain.PanMsgBackground, BlurBorder.BackgroundProperty,
|
||||
(myConverter.IsWarn
|
||||
? new ModBase.MyColor(140d, 80d, 0d, 0d)
|
||||
: new ModBase.MyColor(90d, 0d, 0d, 0d)) - ModMain.frmMain.PanMsgBackground.Background, 200),
|
||||
"PanMsgBackground Background");
|
||||
ModAnimation.AniStart(
|
||||
new[]
|
||||
{
|
||||
ModAnimation.AaOpacity(this, 1d, 120, 60),
|
||||
ModAnimation.AaDouble(i => TransformPos.Y += (double)i,
|
||||
-TransformPos.Y, 300, 60, new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
|
||||
ModAnimation.AaDouble(i => TransformRotate.Angle += (double)i,
|
||||
-TransformRotate.Angle, 300, 60,
|
||||
new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak))
|
||||
}, "MyMsgBox " + uuid);
|
||||
// 记录日志
|
||||
ModBase.Log($"[Control] 正版验证弹窗:{LabTitle.Text}\r\n{LabCaption.Text}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModBase.Log(
|
||||
ex,
|
||||
Lang.Text("Launch.Account.LoginDialog.Error.Load"),
|
||||
ModBase.LogLevel.Hint,
|
||||
userSummary: Lang.Text("Launch.Account.LoginDialog.Error.Load"));
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
// 动画
|
||||
ModAnimation.AniStart(new[]
|
||||
{
|
||||
ModAnimation.AaCode(() =>
|
||||
{
|
||||
if (!ModMain.WaitingMyMsgBox.Any())
|
||||
ModAnimation.AniStart(ModAnimation.AaColor(ModMain.frmMain.PanMsgBackground,
|
||||
BlurBorder.BackgroundProperty,
|
||||
new ModBase.MyColor(0d, 0d, 0d, 0d) - ModMain.frmMain.PanMsgBackground.Background, 200,
|
||||
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)));
|
||||
}, 30),
|
||||
ModAnimation.AaOpacity(this, -Opacity, 80, 20),
|
||||
ModAnimation.AaDouble(i => TransformPos.Y += (double)i, 20d - TransformPos.Y,
|
||||
150, 0, new ModAnimation.AniEaseOutFluent()),
|
||||
ModAnimation.AaDouble(i => TransformRotate.Angle += (double)i,
|
||||
6d - TransformRotate.Angle, 150, 0, new ModAnimation.AniEaseInFluent(ModAnimation.AniEasePower.Weak)),
|
||||
ModAnimation.AaCode(() => ((Grid)Parent).Children.Remove(this), after: true)
|
||||
}, "MyMsgBox " + uuid);
|
||||
}
|
||||
|
||||
// 实现回车和 Esc 的接口(#4857)
|
||||
public void Btn1_Click(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public void Btn3_Click(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
Finished(new ThreadInterruptedException());
|
||||
}
|
||||
|
||||
private void Drag(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// On Error Resume Next
|
||||
if (e.GetPosition(ShapeLine).Y <= 2d)
|
||||
ModMain.frmMain.DragMove();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user