From 3eed7c5b7fa7c17a227186a1ca0da7c884e6d491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=A0=E8=81=8A=E7=9A=84=E5=B0=8F=E6=B4=9B?= Date: Sat, 25 Jan 2025 22:24:56 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=20ClientToken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modules/Minecraft/ModLaunch.vb | 238 ++++++++++++++---- 1 file changed, 194 insertions(+), 44 deletions(-) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index d1df8ee..527d412 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -1,4 +1,4 @@ -Imports System.IO.Compression +Imports System.IO.Compression Public Module ModLaunch @@ -236,18 +236,15 @@ NextInner: '正版购买提示 If Not Setup.Get("HintBuy") AndAlso Setup.Get("LoginType") <> McLoginType.Ms Then If IsSystemLanguageChinese() Then - RunInNewThread( - Sub() - Select Case Setup.Get("SystemLaunchCount") - Case 3, 8, 15, 30, 50, 70, 90, 110, 130, 180, 220, 280, 330, 380, 450, 550, 660, 750, 880, 950, 1100, 1300, 1500, 1700, 1900 - If MyMsgBox("你已经启动了 " & Setup.Get("SystemLaunchCount") & " 次 Minecraft 啦!" & vbCrLf & - "如果觉得 Minecraft 还不错,可以购买正版支持一下,毕竟开发游戏也真的很不容易……不要一直白嫖啦。" & vbCrLf & vbCrLf & - "在登录一次正版账号后,就不会再出现这个提示了!", - "考虑一下正版?", "支持正版游戏!", "下次一定") = 1 Then - OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj") - End If - End Select - End Sub, "Buy Minecraft") + Select Case Setup.Get("SystemLaunchCount") + Case 3, 8, 15, 30, 50, 70, 90, 110, 130, 180, 220, 280, 330, 380, 450, 550, 660, 750, 880, 950, 1100, 1300, 1500, 1700, 1900 + If MyMsgBox("你已经启动了 " & Setup.Get("SystemLaunchCount") & " 次 Minecraft 啦!" & vbCrLf & + "如果觉得 Minecraft 还不错,可以购买正版支持一下,毕竟开发游戏也真的很不容易……不要一直白嫖啦。" & vbCrLf & vbCrLf & + "在登录一次正版账号后,就不会再出现这个提示了!", + "考虑一下正版?", "支持正版游戏!", "下次一定") = 1 Then + OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj") + End If + End Select ElseIf Setup.Get("LoginType") = McLoginType.Legacy Then Select Case MyMsgBox("你必须先登录正版账号,才能进行离线登录!", "正版验证", "购买正版", "试玩", "返回", Button1Action:=Sub() OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")) @@ -504,7 +501,7 @@ NextInner: #Region "分方式登录模块" '各个登录方式的主对象与输入构造 - Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart) With {.ReloadTimeout = 1} + Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart) Public McLoginLegacyLoader As New LoaderTask(Of McLoginLegacy, McLoginResult)("Loader Login Legacy", AddressOf McLoginLegacyStart) Public McLoginNideLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Nide", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10} Public McLoginAuthLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Auth", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10} @@ -632,6 +629,7 @@ LoginFinish: Dim Dict As New Dictionary(Of String, String) Dim Emails As New List(Of String) Dim Passwords As New List(Of String) + Try If Not Setup.Get("Login" & Input.Token & "Email") = "" Then Emails.AddRange(Setup.Get("Login" & Input.Token & "Email").ToString.Split("¨")) If Not Setup.Get("Login" & Input.Token & "Pass") = "" Then Passwords.AddRange(Setup.Get("Login" & Input.Token & "Pass").ToString.Split("¨")) @@ -699,56 +697,205 @@ LoginFinish: End Sub Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean) McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")") - Dim LoginJson As JObject = GetJson(NetRequestRetry( + Dim LittleSkinAccess = Setup.Get("Cache" & Data.Input.Token & "APIToken") + Dim LittleSkinRefresh = Setup.Get("Cache" & Data.Input.Token & "Refresh") + Dim RefreshData As New JObject() + Dim UserName As String + Dim UUID As String + Dim AccessToken As String + Dim RefreshToken As String + Dim IDToken As String + Dim LoginJson As JObject + 'AccessToken 和 RefreshToken 都要有,不然没法刷新 + Dim Noth As Boolean = LittleSkinRefresh = "" + Dim Nls As Boolean = LittleSkinAccess = "" + Log(Noth.ToString, LogLevel.Debug) + Log(Nls.ToString, LogLevel.Debug) + If Not LittleSkinAccess = "" AndAlso Not LittleSkinRefresh = "" Then + RefreshData.Add(New JProperty("grant_type", "refresh_token")) + RefreshData.Add(New JProperty("refresh_token", LittleSkinRefresh)) + RefreshData.Add(New JProperty("access_token", LittleSkinAccess)) + RefreshData.Add(New JProperty("client_id", LittleSkinClientId)) + LoginJson = GetJson(NetRequestOnce( + Url:="https://open.littleskin.cn/oauth/token", + Method:="POST", + Data:=RefreshData, + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, + ContentType:="application/json; charset=utf-8")) + AccessToken = LoginJson("access_token").ToString(0) + RefreshToken = LoginJson("refresh_token").ToString(0) + IDToken = LoginJson("id_token").ToString(0) + Dim SelectPrpfile As String = Base64Decode(IDToken.Split(".")(1)) + UserName = GetJson(GetJson(SelectPrpfile)("selectedProfile").ToString)("name").ToString + UUID = GetJson(GetJson(SelectPrpfile)("selectedProfile").ToString)("id").ToString + Data.Output.Uuid = UUID + Data.Output.Name = UserName + Setup.Set("Cache" & Data.Input.Token & "Refresh", RefreshToken) + Setup.Set("Cache" & Data.Input.Token & "APIToken", AccessToken) + Else + Throw New Exception("登录信息无效") + End If + + Dim RefreshInfo As JObject = New JObject() + RefreshInfo.Add(New JProperty("accessToken", Setup.Get("Cache" & Data.Input.Token & "Access"))) + RefreshInfo.Add(New JProperty("requestUser", True)) + + LoginJson = GetJson(NetRequestRetry( Url:=Data.Input.BaseUrl & "/refresh", Method:="POST", - Data:="{" & - If(RequestUser, " - ""requestUser"": true, - ""selectedProfile"": { - ""id"":""" & Setup.Get("Cache" & Data.Input.Token & "Uuid") & """, - ""name"":""" & Setup.Get("Cache" & Data.Input.Token & "Name") & """},", "") & " - ""accessToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Access") & """, - ""clientToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Client") & """}", + Data:=RefreshInfo.ToString(0), Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}}, ContentType:="application/json; charset=utf-8")) '将登录结果输出 + If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!") Data.Output.AccessToken = LoginJson("accessToken").ToString Data.Output.ClientToken = LoginJson("clientToken").ToString - Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString - Data.Output.Name = LoginJson("selectedProfile")("name").ToString Data.Output.Type = Data.Input.Token + If Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString AndAlso Data.Output.Name = LoginJson("selectedProfile")("name").ToString Then + + Else + Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString + Data.Output.Name = LoginJson("selectedProfile")("name").ToString + End If + '保存缓存 + Setup.Set("Cache" & Data.Input.Token & "Access", Data.Output.AccessToken) Setup.Set("Cache" & Data.Input.Token & "Client", Data.Output.ClientToken) Setup.Set("Cache" & Data.Input.Token & "Uuid", Data.Output.Uuid) Setup.Set("Cache" & Data.Input.Token & "Name", Data.Output.Name) - Setup.Set("Cache" & Data.Input.Token & "Username", Data.Input.UserName) - Setup.Set("Cache" & Data.Input.Token & "Pass", Data.Input.Password) + If Data.Input.UserName Is Nothing Then + ' 用一个通用字符模板填充内容 + Setup.Set("Cache" & Data.Input.Token & "Username", "LittleSkinOAuth 登录") + Setup.Set("Cache" & Data.Input.Token & "Pass", "LittleSkinOAuth 登录") + End If McLaunchLog("刷新登录成功(Refresh, " & Data.Input.Token & ")") + End Sub + + Private Function McLoginRequestLogin(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult)) As Boolean Try Dim NeedRefresh As Boolean = False - McLaunchLog("登录开始(Login, " & Data.Input.Token & ")") - Dim RequestData As New JObject( - New JProperty("agent", New JObject(New JProperty("name", "Minecraft"), New JProperty("version", 1))), - New JProperty("username", Data.Input.UserName), - New JProperty("password", Data.Input.Password), - New JProperty("requestUser", True)) - Dim LoginJson As JObject = GetJson(NetRequestRetry( + Dim RequestData As New JObject() + Dim LittleSkinOAuth As Boolean = False + Dim LoginJson + Dim AccessToken As String + Dim RefreshToken As String + 'LittleSkin OAuth 登录检查 + + If Data.Input.BaseUrl.ToLower.Contains("littleskin.cn") And Not LittleSkinClientId = "" Then + 'ClientId + RequestData.Add(New JProperty("client_id", LittleSkinClientId)) + '一堆权限 + RequestData.Add(New JProperty("scope", "offline_access openid Yggdrasil.PlayerProfiles.Select Yggdrasil.MinecraftToken.Create")) + LittleSkinOAuth = True + Else + RequestData.Add(New JProperty("agent", New JObject(New JProperty("name", "Minecraft"), New JProperty("version", 1)))) + RequestData.Add(New JProperty("username", Data.Input.UserName)) + RequestData.Add(New JProperty("password", Data.Input.Password)) + RequestData.Add(New JProperty("requestUser", True)) + LittleSkinOAuth = False + End If + + ' LittleSkin OAuth 登录步骤 1: 获取授权代码 + If LittleSkinOAuth Then + McLaunchLog("开始 LittleSkin OAuth 登录步骤 1/3(原始登录)") + LoginJson = GetJson(NetRequestOnce( + Url:="https://open.littleskin.cn/oauth/device_code", + Method:="POST", + Data:=RequestData.ToString(0), + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, + ContentType:="application/json; charset=utf-8")) + Else + LoginJson = GetJson(NetRequestRetry( Url:=Data.Input.BaseUrl & "/authenticate", Method:="POST", Data:=RequestData.ToString(0), Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}}, ContentType:="application/json; charset=utf-8")) + + + End If '检查登录结果 - If LoginJson("availableProfiles").Count = 0 Then - If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色,无法更换!", HintType.Critical) - Throw New Exception("$你还没有创建角色,请在创建角色后再试!") - ElseIf Data.Input.ForceReselectProfile AndAlso LoginJson("availableProfiles").Count = 1 Then - Hint("你的账户中只有一个角色,无法更换!", HintType.Critical) + ' LittleSkin + If LittleSkinOAuth Then + ' 轮询验证 + '花了点时间改了逻辑。现在按照 OAuth 标准编写的服务端应该都能用这个处理轮询登录 + Dim Converter As New MyMsgBoxConverter With {.Content = LoginJson, .ForceWait = True, .Type = MyMsgBoxType.Login, .AuthUrl = "https://open.littleskin.cn/oauth/token"} + WaitingMyMsgBox.Add(Converter) + While Converter.Result Is Nothing + Thread.Sleep(100) + End While + Log(Converter.Result.ToString, LogLevel.Hint) + Log(Converter.Result.ToString, LogLevel.Debug) + 'LittleSkin OAuth 登录步骤 2: 获取用户选定档案 + McLaunchLog("LittleSkin OAuth 登录步骤 2/3") + '因为还有个 ID Token,硬编码只解析 AccessToken 和 RefreshToken + Dim Result = Converter.Result + Dim IDToken As String + AccessToken = Result("access_token") + RefreshToken = Result("refresh_token") + Dim ProfileData As New JObject() + Dim UserName As String + Dim UUID As String + ' IDToken 编码的档案 + ' 这一行不能用 Try Catch,否则后面解析会抛出 NullReferenceException + IDToken = Result("id_token") + Dim SelectProfile = GetJson(Base64Decode(IDToken.ToString.Split(".")(1)))("selectedProfile").ToString + UserName = GetJson(SelectProfile)("name") + UUID = GetJson(SelectProfile)("id") + + 'LittleSkin OAuth 登录步骤 3::获取 Minecraft AccessToken 启动令牌 + McLaunchLog("LittleSkin OAuth 登录步骤 3/3") + Dim RequestInfo = New JObject() + RequestInfo.Add(New JProperty("uuid", UUID)) + '需要 Yggdrasil.MinecraftToken.Create + Result = GetJson(NetRequestOnce(Url:=Data.Input.BaseUrl & "/oauth", + Method:="POST", + Data:=RequestInfo.ToString(0), + Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}}, + ContentType:="application/json; charset=utf-8")) + + + + Dim ClientToken = Result("clientToken").ToString + Dim MCAccessToken = Result("accessToken").ToString + + McLaunchLog("LittleSkin OAuth 登录完成") + + Data.Output.AccessToken = MCAccessToken + Data.Output.ClientToken = ClientToken + Data.Output.Name = UserName + Data.Output.Uuid = UUID + Data.Output.Type = Data.Input.Token + + Setup.Set("Cache" & Data.Input.Token & "Access", Data.Output.AccessToken) + Setup.Set("Cache" & Data.Input.Token & "Refresh", RefreshToken) + Setup.Set("Cache" & Data.Input.Token & "Uuid", Data.Output.Uuid) + Setup.Set("Cache" & Data.Input.Token & "Name", Data.Output.Name) + '刷新需要 AccessToken 和 RefreshToken 配合使用 + 'Setup.Set("Cache" & Data.Input.Token & "Client", Data.Output.ClientToken) + Setup.Set("Cache" & Data.Input.Token & "APIToken", AccessToken) + If Data.Input.UserName Is Nothing AndAlso Data.Input.Password Is Nothing Then + Setup.Set("Cache" & Data.Input.Token & "Username", "LittleSkinOAuth 登录") + Setup.Set("Cache" & Data.Input.Token & "Pass", "LittleSkinOAuth 登录") + Else + Setup.Set("Cache" & Data.Input.Token & "Username", Data.Input.UserName) + Setup.Set("Cache" & Data.Input.Token & "Pass", Data.Input.Password) + End If + Return True + '原始登录 + Else + If LoginJson("availableProfiles").Count = 0 Then + If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色, 无法更换!", HintType.Critical) + Throw New Exception("$你还没有创建角色, 请在创建角色后再试!") + ElseIf Data.Input.ForceReselectProfile AndAlso LoginJson("availableProfiles").Count = 1 Then + Hint("你的账户中只有一个角色, 无法更换!", HintType.Critical) + + End If + End If Dim SelectedName As String = Nothing Dim SelectedId As String = Nothing @@ -760,7 +907,7 @@ LoginFinish: If Profile("id").ToString = CacheId Then SelectedName = Profile("name").ToString SelectedId = Profile("id").ToString - McLaunchLog("根据缓存选择的角色:" & SelectedName) + McLaunchLog("根据缓存选择的角色: " & SelectedName) End If Next '缓存无效,要求玩家选择 @@ -876,7 +1023,8 @@ Retry: ElseIf TypeOf Converter.Result Is Exception Then Throw CType(Converter.Result, Exception) Else - Return Converter.Result + Dim LoginData = Converter.Result + Return {LoginData("access_token"), LoginData("refresh_token")} End If End Function '微软登录步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken} @@ -896,7 +1044,10 @@ Retry: End If End Try + + Dim ResultJson As JObject = GetJson(Result) + Dim AccessToken As String = ResultJson("access_token").ToString Dim RefreshToken As String = ResultJson("refresh_token").ToString Return {AccessToken, RefreshToken} @@ -904,7 +1055,6 @@ Retry: '微软登录步骤 2:从 OAuth AccessToken 获取 XBLToken Private Function MsLoginStep2(AccessToken As String) As String McLaunchLog("开始微软登录步骤 2/6") - Dim Request As String = "{ ""Properties"": { ""AuthMethod"": ""RPS"", @@ -2048,8 +2198,8 @@ IgnoreCustomSkin: "@echo off" & vbCrLf & "title 启动 - " & McVersionCurrent.Name & vbCrLf & "echo 游戏正在启动,请稍候。" & vbCrLf & - "set APPDATA=""" & McVersionCurrent.PathIndie & """" & vbCrLf & - "cd /D """ & McVersionCurrent.PathIndie & """" & vbCrLf & + "set APPDATA=""" & PathMcFolder & """" & vbCrLf & + "cd /D """ & PathMcFolder & """" & vbCrLf & CustomCommandGlobal & vbCrLf & CustomCommandVersion & vbCrLf & """" & McLaunchJavaSelected.PathJava & """ " & McLaunchArgument & vbCrLf &