龙腾猫跃 2 лет назад
Родитель
Сommit
83ebc61d8a
37 измененных файлов с 583 добавлено и 1953 удалено
  1. 1 1
      Plain Craft Launcher 2/Application.xaml
  2. 6 5
      Plain Craft Launcher 2/Application.xaml.vb
  3. 1 1
      Plain Craft Launcher 2/Controls/MyResizer.vb
  4. 1 1
      Plain Craft Launcher 2/FormMain.xaml
  5. 13 13
      Plain Craft Launcher 2/FormMain.xaml.vb
  6. BIN
      Plain Craft Launcher 2/Images/Heads/MCBBS.png
  7. 12 6
      Plain Craft Launcher 2/Modules/Base/ModBase.vb
  8. 71 71
      Plain Craft Launcher 2/Modules/Base/ModNet.vb
  9. 7 21
      Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
  10. 2 1
      Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
  11. 15 23
      Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
  12. 3 6
      Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
  13. 8 4
      Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
  14. 14 13
      Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
  15. 39 7
      Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
  16. 1 1
      Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
  17. 1 1
      Plain Craft Launcher 2/Modules/ModMusic.vb
  18. 2 2
      Plain Craft Launcher 2/My Project/AssemblyInfo.vb
  19. 81 97
      Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
  20. 0 1
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml
  21. 0 4
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
  22. 1 1
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml
  23. 2 2
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
  24. 0 559
      Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb
  25. 3 757
      Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb
  26. 1 82
      Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb
  27. 2 2
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
  28. 4 0
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
  29. 3 1
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb
  30. 33 8
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
  31. 2 4
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml
  32. 6 7
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
  33. 0 3
      Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
  34. 5 5
      Plain Craft Launcher 2/Resources/Custom.xaml
  35. BIN
      Plain Craft Launcher 2/Resources/Help.zip
  36. 243 243
      Plain Craft Launcher 2/Resources/ModData.txt
  37. BIN
      Plain Craft Launcher 2/Resources/java-wrapper.jar

+ 1 - 1
Plain Craft Launcher 2/Application.xaml

@@ -277,7 +277,7 @@
                 <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
                 <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
                 <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
                 <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
                 <Setter Property="UndoLimit" Value="15" />
                 <Setter Property="UndoLimit" Value="15" />
-                <Setter Property="MaxLength" Value="10000" />
+                <Setter Property="MaxLength" Value="1000" />
                 <Setter Property="BorderBrush" Value="{DynamicResource ColorBrushBg0}" />
                 <Setter Property="BorderBrush" Value="{DynamicResource ColorBrushBg0}" />
                 <Setter Property="Template">
                 <Setter Property="Template">
                     <Setter.Value>
                     <Setter.Value>

+ 6 - 5
Plain Craft Launcher 2/Application.xaml.vb

@@ -47,9 +47,12 @@ Public Class Application
                         Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
                         Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
                     End If
                     End If
 #If DEBUG Then
 #If DEBUG Then
-                ElseIf e.Args(0) = "--make" Then
                     '制作更新包
                     '制作更新包
-                    UpdateMake(e.Args(1))
+                ElseIf e.Args(0) = "--edit1" Then
+                    ExeEdit(e.Args(1), True)
+                    Environment.Exit(Result.Cancel)
+                ElseIf e.Args(0) = "--edit2" Then
+                    ExeEdit(e.Args(1), False)
                     Environment.Exit(Result.Cancel)
                     Environment.Exit(Result.Cancel)
 #End If
 #End If
                 End If
                 End If
@@ -112,13 +115,11 @@ Public Class Application
             Setup.Load("SystemDebugMode")
             Setup.Load("SystemDebugMode")
             Setup.Load("SystemDebugAnim")
             Setup.Load("SystemDebugAnim")
             Setup.Load("ToolDownloadThread")
             Setup.Load("ToolDownloadThread")
+            Setup.Load("ToolDownloadCert")
             '网络配置初始化
             '网络配置初始化
             ServicePointManager.Expect100Continue = True
             ServicePointManager.Expect100Continue = True
             ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
             ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
             ServicePointManager.DefaultConnectionLimit = 1024
             ServicePointManager.DefaultConnectionLimit = 1024
-            'ServicePointManager.ServerCertificateValidationCallback = New Security.RemoteCertificateValidationCallback(Function() As Boolean
-            '                                                                                                               Return True
-            '                                                                                                           End Function)
             '计时
             '计时
             Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
             Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
             ApplicationStartTick = GetTimeTick()
             ApplicationStartTick = GetTimeTick()

+ 1 - 1
Plain Craft Launcher 2/Controls/MyResizer.vb

@@ -128,7 +128,7 @@ Public Class MyResizer
 
 
     Private Declare Function GetMonitorInfo Lib "user32" (hMonitor As IntPtr, lpmi As MONITORINFO) As Boolean
     Private Declare Function GetMonitorInfo Lib "user32" (hMonitor As IntPtr, lpmi As MONITORINFO) As Boolean
 
 
-    Friend Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr
+    Private Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr
 
 
     Private Sub connectMouseHandlers(element As UIElement)
     Private Sub connectMouseHandlers(element As UIElement)
         AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown
         AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown

+ 1 - 1
Plain Craft Launcher 2/FormMain.xaml

@@ -134,7 +134,7 @@
                     <Grid x:Name="PanTitleInner" Visibility="Collapsed" Margin="-16,0,0,0" Opacity="0" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=PanTitle, Mode=OneWay}">
                     <Grid x:Name="PanTitleInner" Visibility="Collapsed" Margin="-16,0,0,0" Opacity="0" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=PanTitle, Mode=OneWay}">
                         <local:MyIconButton HorizontalAlignment="Left" Theme="White" x:Name="BtnTitleInner" Margin="12,0,0,0" Height="28" Width="28"
                         <local:MyIconButton HorizontalAlignment="Left" Theme="White" x:Name="BtnTitleInner" Margin="12,0,0,0" Height="28" Width="28"
                                              LogoScale="0.87" Logo="M1097.162313 584.707482 250.272424 584.707482 562.833701 896.95123C591.749274 925.859979 591.749274 972.659975 562.833701 1001.568724 533.91812 1030.477473 487.058315 1030.477473 458.142742 1001.568724L21.580946 565.451235C6.757449 550.631234-0.361728 531.131238 0.028357 511.679985L0.028357 511.631236 0.028357 511.582486C-0.361728 492.13124 6.757449 472.582488 21.580946 457.762487L458.142742 21.644997C487.058315-7.215002 533.91812-7.215002 562.833701 21.644997 591.749274 50.553746 591.749274 97.4025 562.833701 126.262499L250.272424 438.55499 1097.162313 438.55499C1137.585605 438.55499 1170.304578 471.266238 1170.304578 511.631236 1170.304578 551.947483 1137.585605 584.707482 1097.162313 584.707482L1097.162313 584.707482Z" />
                                              LogoScale="0.87" Logo="M1097.162313 584.707482 250.272424 584.707482 562.833701 896.95123C591.749274 925.859979 591.749274 972.659975 562.833701 1001.568724 533.91812 1030.477473 487.058315 1030.477473 458.142742 1001.568724L21.580946 565.451235C6.757449 550.631234-0.361728 531.131238 0.028357 511.679985L0.028357 511.631236 0.028357 511.582486C-0.361728 492.13124 6.757449 472.582488 21.580946 457.762487L458.142742 21.644997C487.058315-7.215002 533.91812-7.215002 562.833701 21.644997 591.749274 50.553746 591.749274 97.4025 562.833701 126.262499L250.272424 438.55499 1097.162313 438.55499C1137.585605 438.55499 1170.304578 471.266238 1170.304578 511.631236 1170.304578 551.947483 1137.585605 584.707482 1097.162313 584.707482L1097.162313 584.707482Z" />
-                        <TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,0,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" />
+                        <TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,60,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" />
                     </Grid>
                     </Grid>
                 </Grid>
                 </Grid>
                 <Grid x:Name="PanLeft" Grid.Row="1" HorizontalAlignment="Left" SnapsToDevicePixels="True">
                 <Grid x:Name="PanLeft" Grid.Row="1" HorizontalAlignment="Left" SnapsToDevicePixels="True">

+ 13 - 13
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -129,6 +129,12 @@ Public Class FormMain
         '3:BUG+ IMP* FEAT-
         '3:BUG+ IMP* FEAT-
         '2:BUG* IMP-
         '2:BUG* IMP-
         '1:BUG-
         '1:BUG-
+        If LastVersion < 314 Then 'Snapshot 2.6.14
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化 MC 下载,尝试解决各种导致 MC 下载失败的问题"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "由于 MCBBS 关站,移除其相关内容"))
+            FeatureCount += 17
+            BugCount += 16
+        End If
         If LastVersion < 312 Then 'Snapshot 2.6.13
         If LastVersion < 312 Then 'Snapshot 2.6.13
             FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
             FeatureCount += 6
             FeatureCount += 6
@@ -906,7 +912,7 @@ Public Class FormMain
                     Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
                     Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
                 ElseIf Not (PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod) Then
                 ElseIf Not (PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod) Then
                     '未处于 Mod 管理页面
                     '未处于 Mod 管理页面
-                    If MyMsgBox($"是否要将这些文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install
+                    If MyMsgBox($"是否要将这{If(FilePathList.Count = 1, "个", "些")}文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install
                 Else
                 Else
                     '处于 Mod 管理页面
                     '处于 Mod 管理页面
 Install:
 Install:
@@ -994,7 +1000,6 @@ Install:
                 Left -= 10000
                 Left -= 10000
                 ShowInTaskbar = False
                 ShowInTaskbar = False
                 Visibility = Visibility.Hidden
                 Visibility = Visibility.Hidden
-                'SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) Or WS_EX_TOOLWINDOW)
                 Log("[System] 窗口已隐藏,位置:(" & Left & "," & Top & ")")
                 Log("[System] 窗口已隐藏,位置:(" & Left & "," & Top & ")")
             Else
             Else
                 '取消隐藏
                 '取消隐藏
@@ -1017,16 +1022,9 @@ Install:
                     Topmost = False
                     Topmost = False
                     SetForegroundWindow(Handle)
                     SetForegroundWindow(Handle)
                     Focus()
                     Focus()
-                    'ShowWindow(Handle, 1) 'SW_RESTORE
-                    'SetWindowPos(Handle, 0, 0, 0, 0, 0, 3) 'InsertAfter: TOP, NOMOVE & NOSIZE
-                    'SetFocus(Handle)
                     Log("[System] 窗口已置顶,位置:(" & Left & ", " & Top & "), " & Width & " x " & Height)
                     Log("[System] 窗口已置顶,位置:(" & Left & ", " & Top & "), " & Width & " x " & Height)
                 End Sub)
                 End Sub)
     End Sub
     End Sub
-    'Private Declare Function SetFocus Lib "user32" (hWnd As IntPtr) As IntPtr
-    'Private Declare Function SetWindowPos Lib "user32" (hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, flags As UInteger) As Boolean
-    'Private Declare Function AttachThreadInput Lib "user32" (attachFrom As IntPtr, attachTo As IntPtr, isAttach As Boolean) As Boolean
-    'Private Declare Function SendMessage Lib "user32" (hWnd As IntPtr, msg As UInteger, wParam As Long, lParam As Long) As IntPtr
 
 
 #End Region
 #End Region
 
 
@@ -1573,7 +1571,11 @@ Install:
     ''' </summary>
     ''' </summary>
     Public Sub BackToTop() Handles BtnExtraBack.Click
     Public Sub BackToTop() Handles BtnExtraBack.Click
         Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
         Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
-        RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset)
+        If RealScroll IsNot Nothing Then
+            RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset)
+        Else
+            Log("[UI] 无法返回顶部,未找到合适的 RealScroll", LogLevel.Hint)
+        End If
     End Sub
     End Sub
     Private Function BtnExtraBack_ShowCheck() As Boolean
     Private Function BtnExtraBack_ShowCheck() As Boolean
         Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
         Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
@@ -1581,9 +1583,7 @@ Install:
     End Function
     End Function
     Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
     Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
         If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
         If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
-        Dim Scroll = CType(PanMainRight.Child, MyPageRight).PanScroll
-        If Scroll Is Nothing OrElse Scroll.Visibility <> Visibility.Visible Then Return Nothing
-        Return Scroll
+        Return CType(PanMainRight.Child, MyPageRight).PanScroll
     End Function
     End Function
 
 
 #End Region
 #End Region

BIN
Plain Craft Launcher 2/Images/Heads/MCBBS.png


+ 12 - 6
Plain Craft Launcher 2/Modules/Base/ModBase.vb

@@ -11,12 +11,12 @@ Public Module ModBase
 #Region "声明"
 #Region "声明"
 
 
     '下列版本信息由更新器自动修改
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.6.13" '不含分支前缀的显示用版本名
-    Public Const VersionStandardCode As String = "2.6.13." & VersionBranchCode '标准格式的四段式版本号
+    Public Const VersionBaseName As String = "2.6.14" '不含分支前缀的显示用版本名
+    Public Const VersionStandardCode As String = "2.6.14." & VersionBranchCode '标准格式的四段式版本号
 #If BETA Then
 #If BETA Then
     Public Const VersionCode As Integer = 313 'Release
     Public Const VersionCode As Integer = 313 'Release
 #Else
 #Else
-    Public Const VersionCode As Integer = 312 'Snapshot
+    Public Const VersionCode As Integer = 314 'Snapshot
 #End If
 #End If
     '自动生成的版本信息
     '自动生成的版本信息
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -158,9 +158,13 @@ Public Module ModBase
         ''' </summary>
         ''' </summary>
         Public Const IconButtonUser As String = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"
         Public Const IconButtonUser As String = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"
         ''' <summary>
         ''' <summary>
-        ''' 图标按钮,微软,0.9x
+        ''' 图标按钮,盾牌,1x
         ''' </summary>
         ''' </summary>
-        Public Const IconButtonMs As String = "M0,0h5v5h-5ZM6,0h5v5h-5v-5ZM0,6h5v5h-5v-5ZM6,6h5v5h-5v-5Z"
+        Public Const IconButtonShield As String = "M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z"
+        ''' <summary>
+        ''' 图标按钮,离线,0.85x
+        ''' </summary>
+        Public Const IconButtonOffline As String = "M533.293176 788.841412a60.235294 60.235294 0 1 1 85.202824 85.202823l-42.616471 42.586353c-129.355294 129.385412-339.124706 129.385412-468.510117 0-129.385412-129.385412-129.385412-339.124706 0-468.510117l42.586353-42.616471a60.235294 60.235294 0 1 1 85.202823 85.202824l-42.61647 42.586352a210.823529 210.823529 0 1 0 298.164706 298.164706l42.586352-42.61647z m255.548236-255.548236l42.61647-42.586352a210.823529 210.823529 0 1 0-298.164706-298.164706l-42.586352 42.61647a60.235294 60.235294 0 1 1-85.202824-85.202823l42.616471-42.586353c129.355294-129.385412 339.124706-129.385412 468.510117 0 129.385412 129.385412 129.385412 339.124706 0 468.510117l-42.586353 42.616471a60.235294 60.235294 0 1 1-85.202823-85.202824zM192.542118 192.542118a60.235294 60.235294 0 0 1 85.202823 0l553.712941 553.712941a60.235294 60.235294 0 0 1-85.202823 85.202823L192.542118 277.744941a60.235294 60.235294 0 0 1 0-85.202823z"
         ''' <summary>
         ''' <summary>
         ''' 图标,音符,1x
         ''' 图标,音符,1x
         ''' </summary>
         ''' </summary>
@@ -1980,9 +1984,10 @@ NextElement:
         Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
         Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
         Using Program As New Process() With {.StartInfo = Info}
         Using Program As New Process() With {.StartInfo = Info}
             Program.Start()
             Program.Start()
+            Dim Result As String = Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
             Program.WaitForExit(Timeout)
             Program.WaitForExit(Timeout)
             If Not Program.HasExited Then Program.Kill()
             If Not Program.HasExited Then Program.Kill()
-            Return Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
+            Return Result
         End Using
         End Using
     End Function
     End Function
 
 
@@ -2111,6 +2116,7 @@ NextElement:
     ''' </summary>
     ''' </summary>
     Public Sub OpenWebsite(Url As String)
     Public Sub OpenWebsite(Url As String)
         Try
         Try
+            If Not Url.ToLower.StartsWith("http") Then Throw New Exception(Url & " 不是一个有效的网址,它必须以 http 开头!")
             Log("[System] 正在打开网页:" & Url)
             Log("[System] 正在打开网页:" & Url)
             Process.Start(Url)
             Process.Start(Url)
         Catch ex As Exception
         Catch ex As Exception

+ 71 - 71
Plain Craft Launcher 2/Modules/Base/ModNet.vb

@@ -192,7 +192,6 @@ RequestFinished:
             If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Request.ProtocolVersion = HttpVersion.Version11
             If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Request.ProtocolVersion = HttpVersion.Version11
             Request.Timeout = Timeout
             Request.Timeout = Timeout
             Request.Accept = Accept
             Request.Accept = Accept
-            Request.KeepAlive = False
             SecretHeadersSign(Url, Request)
             SecretHeadersSign(Url, Request)
             Using res As HttpWebResponse = Request.GetResponse()
             Using res As HttpWebResponse = Request.GetResponse()
                 Using HttpStream As Stream = res.GetResponseStream()
                 Using HttpStream As Stream = res.GetResponseStream()
@@ -393,7 +392,6 @@ RequestFinished:
             End If
             End If
             Req.ContentType = ContentType
             Req.ContentType = ContentType
             Req.Timeout = Timeout
             Req.Timeout = Timeout
-            Req.KeepAlive = False
             SecretHeadersSign(Url, Req)
             SecretHeadersSign(Url, Req)
             If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Req.ProtocolVersion = HttpVersion.Version11
             If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Req.ProtocolVersion = HttpVersion.Version11
             If Method = "POST" OrElse Method = "PUT" Then
             If Method = "POST" OrElse Method = "PUT" Then
@@ -965,13 +963,12 @@ Capture:
                     '是否禁用多线程,以及规定碎片大小
                     '是否禁用多线程,以及规定碎片大小
                     Dim TargetUrl As String = GetSource().Url
                     Dim TargetUrl As String = GetSource().Url
                     If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("github.com") Then Return False
                     If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("github.com") Then Return False
-                    Dim RealFilePieceLimit = If(TargetUrl.Contains("download.mcbbs.net"), FilePieceLimit * 5, FilePieceLimit)
                     '寻找最大碎片
                     '寻找最大碎片
                     Dim FilePieceMax As NetThread = Threads
                     Dim FilePieceMax As NetThread = Threads
                     For Each Thread As NetThread In Threads
                     For Each Thread As NetThread In Threads
                         If Thread.DownloadUndone > FilePieceMax.DownloadUndone Then FilePieceMax = Thread
                         If Thread.DownloadUndone > FilePieceMax.DownloadUndone Then FilePieceMax = Thread
                     Next
                     Next
-                    If FilePieceMax Is Nothing OrElse FilePieceMax.DownloadUndone < RealFilePieceLimit Then Return False
+                    If FilePieceMax Is Nothing OrElse FilePieceMax.DownloadUndone < FilePieceLimit Then Return False
                     StartPosition = FilePieceMax.DownloadEnd - FilePieceMax.DownloadUndone * 0.4
                     StartPosition = FilePieceMax.DownloadEnd - FilePieceMax.DownloadUndone * 0.4
                     StartSource = GetSource()
                     StartSource = GetSource()
 
 
@@ -1021,7 +1018,7 @@ StartThread:
             Dim HttpRequest As HttpWebRequest
             Dim HttpRequest As HttpWebRequest
             Dim ResultStream As Stream = Nothing
             Dim ResultStream As Stream = Nothing
             '部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
             '部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
-            '延迟过长会导致某些特别慢的链接迟迟不被掐死,即使 MCBBS 源在 10s 下也会出现类似情况
+            '延迟过长会导致某些特别慢的链接迟迟不被掐死
             Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000)
             Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000)
             Info.State = NetState.Connect
             Info.State = NetState.Connect
             Try
             Try
@@ -1206,7 +1203,9 @@ SourceBreak:
                 'End SyncLock
                 'End SyncLock
                 Info.Source.Ex = ex
                 Info.Source.Ex = ex
                 '根据情况判断,是否在多线程下禁用下载源(连续错误过多,或不支持断点续传)
                 '根据情况判断,是否在多线程下禁用下载源(连续错误过多,或不支持断点续传)
-                If ex.Message.Contains("该下载源不支持") OrElse ex.Message.Contains("未能解析") OrElse ex.Message.Contains("(404)") OrElse ex.Message.Contains("(403)") OrElse ex.Message.Contains("(502)") OrElse ex.Message.Contains("无返回数据") OrElse ex.Message.Contains("空间不足") OrElse
+                'BMCLAPI 的部分源在高频率请求下会返回 403,所以不应因此禁用下载源
+                If ex.Message.Contains("该下载源不支持") OrElse ex.Message.Contains("未能解析") OrElse ex.Message.Contains("(404)") OrElse
+                   ex.Message.Contains("(502)") OrElse ex.Message.Contains("无返回数据") OrElse ex.Message.Contains("空间不足") OrElse ex.Message.Contains("获取到的片大小不一致") OrElse
                     (Info.Source.FailCount >= MathClamp(NetTaskThreadLimit, 5, 40) AndAlso DownloadDone < 1) OrElse
                     (Info.Source.FailCount >= MathClamp(NetTaskThreadLimit, 5, 40) AndAlso DownloadDone < 1) OrElse
                     Info.Source.FailCount > NetTaskThreadLimit Then
                     Info.Source.FailCount > NetTaskThreadLimit Then
                     Dim IsThisFail As Boolean = False
                     Dim IsThisFail As Boolean = False
@@ -1225,7 +1224,7 @@ Wrong:
                     '本线程引发下载源被禁用
                     '本线程引发下载源被禁用
                     If IsThisFail Then
                     If IsThisFail Then
                         Log("[Download] " & LocalName & " " & Uuid & "#:下载源被禁用(" & Info.Source.Id & "):" & Info.Source.Url)
                         Log("[Download] " & LocalName & " " & Uuid & "#:下载源被禁用(" & Info.Source.Id & "):" & Info.Source.Url)
-                        Log(ex, "下载源 " & Info.Source.Id & " 已被禁用", If(ex.Message.Contains("不支持断点续传") OrElse ex.Message.Contains("404") OrElse ex.Message.Contains("416"), LogLevel.Developer, LogLevel.Debug))
+                        Log(ex, "下载源 " & Info.Source.Id & " 已被禁用", If(ex.Message.Contains("不支持断点续传") OrElse ex.Message.Contains("(404)") OrElse ex.Message.Contains("(416)"), LogLevel.Developer, LogLevel.Debug))
                         If IsSourceFailed() Then
                         If IsSourceFailed() Then
                             '没有可用源
                             '没有可用源
                             Log("[Download] 文件 " & LocalName & " 已无可用下载源")
                             Log("[Download] 文件 " & LocalName & " 已无可用下载源")
@@ -1475,7 +1474,7 @@ Retry:
             End Get
             End Get
             Set(value As Integer)
             Set(value As Integer)
                 _FailCount = value
                 _FailCount = value
-                If State = LoadState.Loading AndAlso value >= Math.Min(2000, Math.Max(FileRemain * 5.5, NetTaskThreadLimit * 5.5 + 3)) Then
+                If State = LoadState.Loading AndAlso value >= Math.Min(10000, Math.Max(FileRemain * 5.5, NetTaskThreadLimit * 5.5 + 3)) Then
                     Log("[Download] 由于同加载器中失败次数过多引发强制失败:连续失败了 " & value & " 次", LogLevel.Debug)
                     Log("[Download] 由于同加载器中失败次数过多引发强制失败:连续失败了 " & value & " 次", LogLevel.Debug)
                     On Error Resume Next
                     On Error Resume Next
                     Dim ExList As New List(Of Exception)
                     Dim ExList As New List(Of Exception)
@@ -1846,69 +1845,70 @@ Retry:
         Private Sub StartManager()
         Private Sub StartManager()
             If IsManagerStarted Then Exit Sub
             If IsManagerStarted Then Exit Sub
             IsManagerStarted = True
             IsManagerStarted = True
-            RunInNewThread(Sub()
-                               Try
-                                   Dim LastLoopTime As Long
-                                   While True
-                                       LastLoopTime = GetTimeTick()
-                                       '若已完成,则清空
-                                       If FileRemain = 0 AndAlso Files.Count > 0 Then
-                                           SyncLock LockFiles
-                                               Files.Clear()
-                                           End SyncLock
-                                       End If
-                                       '开启新线程
-                                       If Speed < NetTaskSpeedLimitLow OrElse FileRemain > NetTaskThreadLimit Then
-                                           '速度小于下限或剩余文件还贼多,尝试开启线程
-                                           Dim IsSuccess As Boolean = False
-                                           Dim NewThreadCount As Integer
-                                           '确定最大线程追加数
-                                           NewThreadCount = Math.Max(FileRemain, MathClamp(NetTaskThreadCount / 2, 1, 4))
-                                           NewThreadCount = Math.Floor(NewThreadCount / 2) '双线程启用减半
-                                           NewThreadCount = MathClamp(NewThreadCount, 1, NetTaskThreadLimit)
-                                           '循环追加
-                                           Do
-                                               IsSuccess = False
-                                               '启动 Wait 的文件后立即会变成 Connect,导致第二次循环再次调用,所以需要先暂时存储进去……
-                                               '此外为了减少 LockFiles 的占用时间,所以先遍历列表再开始
-                                               Dim FilesWaiting As New List(Of NetFile)
-                                               Dim FilesLoading As New List(Of NetFile)
-                                               SyncLock LockFiles
-                                                   For Each File As NetFile In Files.Values
-                                                       If File.RandomCode Mod 2 = 0 Then Continue For
-                                                       If File.State = NetState.WaitForDownload Then
-                                                           FilesWaiting.Add(File)
-                                                       ElseIf File.State < NetState.Merge Then
-                                                           FilesLoading.Add(File)
-                                                       End If
-                                                   Next
-                                               End SyncLock
-                                               '为文件列表中的文件开始线程
-                                               For Each File As NetFile In FilesWaiting
-                                                   If NewThreadCount = 0 Then Exit For
-                                                   If File.TryBeginThread() Then
-                                                       IsSuccess = True
-                                                       NewThreadCount -= 1
-                                                   End If
-                                               Next
-                                               For Each File As NetFile In FilesLoading
-                                                   If NewThreadCount = 0 Then Exit For
-                                                   If File.TryBeginThread() Then
-                                                       IsSuccess = True
-                                                       NewThreadCount -= 1
-                                                   End If
-                                               Next
-                                           Loop While NewThreadCount > 0 AndAlso IsSuccess
-                                       End If
-                                       '等待直至 120 ms
-                                       Do While GetTimeTick() - LastLoopTime < 120
-                                           Thread.Sleep(10)
-                                       Loop
-                                   End While
-                               Catch ex As Exception
-                                   Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert)
-                               End Try
-                           End Sub, "NetManager ThreadStarter Single")
+            RunInNewThread(
+            Sub()
+                Try
+                    Dim LastLoopTime As Long
+                    While True
+                        LastLoopTime = GetTimeTick()
+                        '若已完成,则清空
+                        If FileRemain = 0 AndAlso Files.Count > 0 Then
+                            SyncLock LockFiles
+                                Files.Clear()
+                            End SyncLock
+                        End If
+                        '开启新线程
+                        If Speed < NetTaskSpeedLimitLow OrElse FileRemain > NetTaskThreadLimit Then
+                            '速度小于下限或剩余文件还贼多,尝试开启线程
+                            Dim IsSuccess As Boolean = False
+                            Dim NewThreadCount As Integer
+                            '确定最大线程追加数
+                            NewThreadCount = Math.Max(FileRemain, MathClamp(NetTaskThreadCount / 2, 1, 4))
+                            NewThreadCount = Math.Floor(NewThreadCount / 2) '双线程启用减半
+                            NewThreadCount = MathClamp(NewThreadCount, 1, NetTaskThreadLimit)
+                            '循环追加
+                            Do
+                                IsSuccess = False
+                                '启动 Wait 的文件后立即会变成 Connect,导致第二次循环再次调用,所以需要先暂时存储进去……
+                                '此外为了减少 LockFiles 的占用时间,所以先遍历列表再开始
+                                Dim FilesWaiting As New List(Of NetFile)
+                                Dim FilesLoading As New List(Of NetFile)
+                                SyncLock LockFiles
+                                    For Each File As NetFile In Files.Values
+                                        If File.RandomCode Mod 2 = 0 Then Continue For
+                                        If File.State = NetState.WaitForDownload Then
+                                            FilesWaiting.Add(File)
+                                        ElseIf File.State < NetState.Merge Then
+                                            FilesLoading.Add(File)
+                                        End If
+                                    Next
+                                End SyncLock
+                                '为文件列表中的文件开始线程
+                                For Each File As NetFile In FilesWaiting
+                                    If NewThreadCount = 0 Then Exit For
+                                    If File.TryBeginThread() Then
+                                        IsSuccess = True
+                                        NewThreadCount -= 1
+                                    End If
+                                Next
+                                For Each File As NetFile In FilesLoading
+                                    If NewThreadCount = 0 Then Exit For
+                                    If File.TryBeginThread() Then
+                                        IsSuccess = True
+                                        NewThreadCount -= 1
+                                    End If
+                                Next
+                                '等待 40 ms:BMCLAPI 部分时候有 20QPS 的限制
+                                Thread.Sleep(40)
+                            Loop While NewThreadCount > 0 AndAlso IsSuccess
+                        End If
+                        '等待 40 ms
+                        Thread.Sleep(40)
+                    End While
+                Catch ex As Exception
+                    Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert)
+                End Try
+            End Sub, "NetManager ThreadStarter Single")
             RunInNewThread(Sub()
             RunInNewThread(Sub()
                                Try
                                Try
                                    Dim LastLoopTime As Long
                                    Dim LastLoopTime As Long

+ 7 - 21
Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb

@@ -59,7 +59,6 @@
                     Entry.WikiId = i
                     Entry.WikiId = i
                     If SplitedLine.Count >= 2 Then
                     If SplitedLine.Count >= 2 Then
                         Entry.ChineseName = SplitedLine(1)
                         Entry.ChineseName = SplitedLine(1)
-                        Entry.MCBBS = If(SplitedLine.Count >= 3, SplitedLine(2), Nothing)
                         If Entry.ChineseName.Contains("*") Then '处理 *
                         If Entry.ChineseName.Contains("*") Then '处理 *
                             Entry.ChineseName = Entry.ChineseName.Replace("*", " (" &
                             Entry.ChineseName = Entry.ChineseName.Replace("*", " (" &
                                 String.Join(" ", If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Split("-").Select(Function(w) w.Substring(0, 1).ToUpper & w.Substring(1, w.Length - 1))) & ")")
                                 String.Join(" ", If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Split("-").Select(Function(w) w.Substring(0, 1).ToUpper & w.Substring(1, w.Length - 1))) & ")")
@@ -82,10 +81,6 @@
         ''' </summary>
         ''' </summary>
         Public ChineseName As String = ""
         Public ChineseName As String = ""
         ''' <summary>
         ''' <summary>
-        ''' MCBBS 发布帖编号(例如 195107)。若为 Nothing 则没有发布帖。
-        ''' </summary>
-        Public MCBBS As Integer?
-        ''' <summary>
         ''' CurseForge Slug(例如 advanced-solar-panels)。
         ''' CurseForge Slug(例如 advanced-solar-panels)。
         ''' </summary>
         ''' </summary>
         Public CurseForgeSlug As String = Nothing
         Public CurseForgeSlug As String = Nothing
@@ -95,7 +90,7 @@
         Public ModrinthSlug As String = Nothing
         Public ModrinthSlug As String = Nothing
 
 
         Public Overrides Function ToString() As String
         Public Overrides Function ToString() As String
-            Return If(CurseForgeSlug, "") & "&" & If(ModrinthSlug, "") & "|" & WikiId & "|" & ChineseName & If(MCBBS Is Nothing, "", "|" & MCBBS)
+            Return If(CurseForgeSlug, "") & "&" & If(ModrinthSlug, "") & "|" & WikiId & "|" & ChineseName
         End Function
         End Function
     End Class
     End Class
 
 
@@ -200,14 +195,6 @@
             End Get
             End Get
         End Property
         End Property
         ''' <summary>
         ''' <summary>
-        ''' MCBBS 的帖子 ID。若为 0 则没有。
-        ''' </summary>
-        Public ReadOnly Property MCBBS As Integer
-            Get
-                Return If(DatabaseEntry Is Nothing, 0, If(DatabaseEntry.MCBBS, 0))
-            End Get
-        End Property
-        ''' <summary>
         ''' 翻译后的中文名。若数据库没有则等同于 RawName。
         ''' 翻译后的中文名。若数据库没有则等同于 RawName。
         ''' </summary>
         ''' </summary>
         Public ReadOnly Property TranslatedName As String
         Public ReadOnly Property TranslatedName As String
@@ -884,10 +871,10 @@ NoSubtitle:
             Dim SearchResult As String = ""
             Dim SearchResult As String = ""
             For i = 0 To Math.Min(4, SearchResults.Count - 1) '就算全是准确的,也最多只要 5 个
             For i = 0 To Math.Min(4, SearchResults.Count - 1) '就算全是准确的,也最多只要 5 个
                 If Not SearchResults(i).AbsoluteRight AndAlso i >= Math.Min(2, SearchResults.Count - 1) Then Exit For '把 3 个结果拼合以提高准确度
                 If Not SearchResults(i).AbsoluteRight AndAlso i >= Math.Min(2, SearchResults.Count - 1) Then Exit For '把 3 个结果拼合以提高准确度
-                If SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ") & " "
-                If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.Replace("-", " ") & " "
+                If SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ").Replace("/", " ") & " "
+                If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.Replace("-", " ").Replace("/", " ") & " "
                 SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First.
                 SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First.
-                    Replace(":", "").Replace("(", "").Replace(")", "").ToLower & " "
+                    Replace(":", "").Replace("(", "").Replace(")", "").ToLower.Replace("/", " ") & " "
             Next
             Next
             Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer)
             Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer)
             '去除常见连接词
             '去除常见连接词
@@ -911,9 +898,8 @@ NoSubtitle:
         For Each Keyword In AllPossibleKeywords.Split(" ")
         For Each Keyword In AllPossibleKeywords.Split(" ")
             Keyword = Keyword.Trim("["c, "]"c)
             Keyword = Keyword.Trim("["c, "]"c)
             If Keyword = "" Then Continue For
             If Keyword = "" Then Continue For
-            If {"forge", "fabric", "for", "mod",
-                "forge/fabric", "fabric/forge", "fabric/quilt", "quilt/fabric"}.Contains(Keyword) Then '#208
-                Log("[Comp] 已跳过搜索关键词:" & Keyword)
+            If {"forge", "fabric", "for", "mod", "quilt"}.Contains(Keyword) Then '#208
+                Log("[Comp] 已跳过搜索关键词:" & Keyword, LogLevel.Developer)
                 Continue For
                 Continue For
             End If
             End If
             RightKeywords.Add(Keyword)
             RightKeywords.Add(Keyword)
@@ -927,7 +913,7 @@ NoSubtitle:
         '例外项:OptiForge、OptiFabric(拆词后因为包含 Forge/Fabric 导致无法搜到实际的 Mod)
         '例外项:OptiForge、OptiFabric(拆词后因为包含 Forge/Fabric 导致无法搜到实际的 Mod)
         If RawFilter.Replace(" ", "").ToLower.Contains("optiforge") Then Task.Input.SearchText = "optiforge"
         If RawFilter.Replace(" ", "").ToLower.Contains("optiforge") Then Task.Input.SearchText = "optiforge"
         If RawFilter.Replace(" ", "").ToLower.Contains("optifabric") Then Task.Input.SearchText = "optifabric"
         If RawFilter.Replace(" ", "").ToLower.Contains("optifabric") Then Task.Input.SearchText = "optifabric"
-        Log("[Comp] 工程列表搜索最终文本:" & Task.Input.SearchText, LogLevel.Developer)
+        Log("[Comp] 工程列表搜索最终文本:" & Task.Input.SearchText, LogLevel.Debug)
         Task.Progress = 0.1
         Task.Progress = 0.1
 
 
 #End Region
 #End Region

+ 2 - 1
Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb

@@ -583,7 +583,8 @@ Done:
         If LogMc IsNot Nothing Then
         If LogMc IsNot Nothing Then
             '常规信息
             '常规信息
             If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
             If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
-            If LogMc.Contains("A potential solution has been determined:") OrElse LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
+            If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
+            If LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
         End If
         End If
 
 
     End Sub
     End Sub

+ 15 - 23
Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb

@@ -221,9 +221,7 @@
     ''' </summary>
     ''' </summary>
     Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
     Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
     Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
     Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
-        Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json",
-                                                       BackupUrl:="https://download.mcbbs.net/mc/game/version_manifest.json",
-                                                       IsJson:=True)
+        Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
         Try
         Try
             Dim Versions As JArray = Json("versions")
             Dim Versions As JArray = Json("versions")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
@@ -384,8 +382,7 @@
     ''' </summary>
     ''' </summary>
     Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
     Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
     Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
     Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
-        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList",
-                                                      BackupUrl:="https://download.mcbbs.net/optifine/versionList", IsJson:=True)
+        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
         Try
         Try
             Dim Versions As New List(Of DlOptiFineListEntry)
             Dim Versions As New List(Of DlOptiFineListEntry)
             For Each Token As JObject In Json
             For Each Token As JObject In Json
@@ -469,8 +466,7 @@
     ''' </summary>
     ''' </summary>
     Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
     Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
     Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
     Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
-        Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default,
-                                                       BackupUrl:="https://download.mcbbs.net/forge/minecraft")
+        Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         '获取所有版本信息
         '获取所有版本信息
         Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
         Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
@@ -664,8 +660,7 @@
     ''' Forge 版本列表,BMCLAPI。
     ''' Forge 版本列表,BMCLAPI。
     ''' </summary>
     ''' </summary>
     Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
     Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
-        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input,
-                                                       BackupUrl:="https://download.mcbbs.net/forge/minecraft/" & Loader.Input, IsJson:=True)
+        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, IsJson:=True)
         Dim Versions As New List(Of DlForgeVersionEntry)
         Dim Versions As New List(Of DlForgeVersionEntry)
         Try
         Try
             Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
             Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
@@ -827,8 +822,7 @@
     ''' </summary>
     ''' </summary>
     Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
     Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
     Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
     Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json",
-                                                       BackupUrl:="https://download.mcbbs.net/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
+        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
         Try
         Try
             Dim Json As JObject = Result("versions")
             Dim Json As JObject = Result("versions")
             Dim Versions As New List(Of DlLiteLoaderListEntry)
             Dim Versions As New List(Of DlLiteLoaderListEntry)
@@ -914,8 +908,7 @@
     ''' </summary>
     ''' </summary>
     Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
     Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
     Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
     Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions",
-                                                       BackupUrl:="https://download.mcbbs.net/fabric-meta/v2/versions", IsJson:=True)
+        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
         Try
         Try
             Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
             Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
@@ -943,26 +936,25 @@
 
 
     Public Function DlSourceResourceGet(MojangBase As String) As String()
     Public Function DlSourceResourceGet(MojangBase As String) As String()
         MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
         MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
-        Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/assets").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/assets").Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
-                MojangBase,
-                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
+        Return {
+                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets"),
+                MojangBase
                }
                }
     End Function
     End Function
 
 
     Public Function DlSourceLibraryGet(MojangBase As String) As String()
     Public Function DlSourceLibraryGet(MojangBase As String) As String()
-        Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/maven").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/maven").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"),
-                MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
-                MojangBase,
+        Return {
                 MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
                 MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
-                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
+                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries"),
+                MojangBase
                }
                }
     End Function
     End Function
 
 
     Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
     Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
         If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
         If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
-        Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net").Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
-                MojangBase,
-                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
+        Return {
+                MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com"),
+                MojangBase
                }
                }
     End Function
     End Function
 
 

+ 3 - 6
Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb

@@ -727,8 +727,7 @@ Wait:
     Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
     Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
         Log("[Java] 开始获取 Java 下载信息")
         Log("[Java] 开始获取 Java 下载信息")
         Dim IndexFileStr As String = NetGetCodeByDownload(
         Dim IndexFileStr As String = NetGetCodeByDownload(
-            {"https://download.mcbbs.net/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
-             "https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
+            {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
              "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
              "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
         IsJson:=True)
         IsJson:=True)
         '获取下载地址
         '获取下载地址
@@ -740,8 +739,7 @@ Wait:
         Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
         Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
         '获取文件列表
         '获取文件列表
         Dim ListFileStr As String = NetGetCodeByDownload(
         Dim ListFileStr As String = NetGetCodeByDownload(
-            {Address.Replace("piston-meta.mojang.com", "download.mcbbs.net"),
-             Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"),
+            {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"),
              Address},
              Address},
         IsJson:=True)
         IsJson:=True)
         Dim BaseDir As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
         Dim BaseDir As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
@@ -753,8 +751,7 @@ Wait:
             If Checker.Check(BaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
             If Checker.Check(BaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
             Dim Url As String = Info("url")
             Dim Url As String = Info("url")
             Results.Add(New NetFile(
             Results.Add(New NetFile(
-                            {Url.Replace("piston-data.mojang.com", "download.mcbbs.net"),
-                             Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"),
+                            {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"),
                              Url},
                              Url},
                         BaseDir & File.Name, Checker))
                         BaseDir & File.Name, Checker))
         Next
         Next

+ 8 - 4
Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb

@@ -1911,9 +1911,9 @@ IgnoreCustomSkin:
 
 
         '获取自定义命令
         '获取自定义命令
         Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun")
         Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun")
-        If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal)
+        If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal, True)
         Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent)
         Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent)
-        If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion)
+        If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion, True)
 
 
         '输出 bat
         '输出 bat
         Try
         Try
@@ -2072,7 +2072,7 @@ IgnoreCustomSkin:
         '获取窗口标题
         '获取窗口标题
         Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent)
         Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent)
         If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle")
         If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle")
-        WindowTitle = ArgumentReplace(WindowTitle)
+        WindowTitle = ArgumentReplace(WindowTitle, False)
 
 
         '初始化等待
         '初始化等待
         Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle)
         Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle)
@@ -2124,7 +2124,7 @@ IgnoreCustomSkin:
     ''' <summary>
     ''' <summary>
     ''' 在启动结束时,对 PCL 约定的替换标记进行处理。
     ''' 在启动结束时,对 PCL 约定的替换标记进行处理。
     ''' </summary>
     ''' </summary>
-    Private Function ArgumentReplace(Raw As String) As String
+    Private Function ArgumentReplace(Raw As String, ReplaceTimeAndDate As Boolean) As String
         If Raw Is Nothing Then Return Nothing
         If Raw Is Nothing Then Return Nothing
         '路径替换
         '路径替换
         Raw = Raw.Replace("{minecraft}", PathMcFolder)
         Raw = Raw.Replace("{minecraft}", PathMcFolder)
@@ -2134,6 +2134,10 @@ IgnoreCustomSkin:
         '普通替换
         '普通替换
         Raw = Raw.Replace("{user}", McLoginLoader.Output.Name)
         Raw = Raw.Replace("{user}", McLoginLoader.Output.Name)
         Raw = Raw.Replace("{uuid}", McLoginLoader.Output.Uuid)
         Raw = Raw.Replace("{uuid}", McLoginLoader.Output.Uuid)
+        If ReplaceTimeAndDate Then '设置窗口标题时需要动态替换日期和时间
+            Raw = Raw.Replace("{date}", Date.Now.ToString("yyyy/M/d"))
+            Raw = Raw.Replace("{time}", Date.Now.ToString("HH:mm:ss"))
+        End If
         Select Case McLoginLoader.Input.Type
         Select Case McLoginLoader.Input.Type
             Case McLoginType.Legacy
             Case McLoginType.Legacy
                 If PageLinkHiper.HiperState = LoadState.Finished Then
                 If PageLinkHiper.HiperState = LoadState.Finished Then

+ 14 - 13
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -42,15 +42,19 @@
 #Region "读取默认(Original)文件夹,即当前、官启文件夹,可能没有结果"
 #Region "读取默认(Original)文件夹,即当前、官启文件夹,可能没有结果"
 
 
             '扫描当前文件夹
             '扫描当前文件夹
-            If CheckPermission(Path) AndAlso Directory.Exists(Path & "versions\") Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Path, .Type = McFolderType.Original})
-            For Each Folder As DirectoryInfo In New DirectoryInfo(Path).GetDirectories
-                If CheckPermission(Folder.FullName) AndAlso Directory.Exists(Folder.FullName & "versions\") OrElse Folder.Name = ".minecraft" Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Folder.FullName & "\", .Type = McFolderType.Original})
-            Next
+            Try
+                If Directory.Exists(Path & "versions\") Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Path, .Type = McFolderType.Original})
+                For Each Folder As DirectoryInfo In New DirectoryInfo(Path).GetDirectories
+                    If Directory.Exists(Folder.FullName & "versions\") OrElse Folder.Name = ".minecraft" Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Folder.FullName & "\", .Type = McFolderType.Original})
+                Next
+            Catch ex As Exception
+                Log(ex, "扫描 PCL 所在文件夹中是否有 MC 文件夹失败")
+            End Try
 
 
             '扫描官启文件夹
             '扫描官启文件夹
             Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\"
             Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\"
-            If (CacheMcFolderList.Count = 0 OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹
-                CheckPermission(MojangPath) AndAlso Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹
+            If (Not CacheMcFolderList.Any OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹
+                Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹
                 CacheMcFolderList.Add(New McFolder With {.Name = "官方启动器文件夹", .Path = MojangPath, .Type = McFolderType.Original})
                 CacheMcFolderList.Add(New McFolder With {.Name = "官方启动器文件夹", .Path = MojangPath, .Type = McFolderType.Original})
             End If
             End If
 
 
@@ -406,7 +410,7 @@
                         End If
                         End If
                         '无法获取
                         '无法获取
                         _Version.McName = "Unknown"
                         _Version.McName = "Unknown"
-                        Info = "PCL 无法识别该版本,请向作者反馈此问题"
+                        Info = "PCL 无法识别该版本的 MC 版本号"
                     Catch ex As Exception
                     Catch ex As Exception
                         Log(ex, "识别 Minecraft 版本时出错")
                         Log(ex, "识别 Minecraft 版本时出错")
                         _Version.McName = "Unknown"
                         _Version.McName = "Unknown"
@@ -1915,7 +1919,7 @@ NextVersion:
                 '获取下载信息
                 '获取下载信息
                 Try
                 Try
                     Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
                     Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
-                    DownloadInfo = GetJson(NetGetCodeByDownload({"https://download.mcbbs.net/mirrors/authlib-injector/artifact/latest.json", "https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"}, IsJson:=True))
+                    DownloadInfo = GetJson(NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"}, IsJson:=True))
                 Catch ex As Exception
                 Catch ex As Exception
                     Log(ex, "获取 Authlib-Injector 下载信息失败")
                     Log(ex, "获取 Authlib-Injector 下载信息失败")
                 End Try
                 End Try
@@ -1926,9 +1930,7 @@ NextVersion:
                         '开始下载
                         '开始下载
                         Dim DownloadAddress As String = DownloadInfo("download_url")
                         Dim DownloadAddress As String = DownloadInfo("download_url")
                         Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
                         Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
-                        Result.Add(New NetFile({
-                                DownloadAddress.Replace("bmclapi2.bangbang93.com", "download.mcbbs.net"), DownloadAddress
-                            }, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
+                        Result.Add(New NetFile({DownloadAddress}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
                     End If
                     End If
                 End If
                 End If
             End If
             End If
@@ -1958,7 +1960,7 @@ NextVersion:
                 '获取 Url 的真实地址
                 '获取 Url 的真实地址
                 Urls.Add(Token.Url)
                 Urls.Add(Token.Url)
                 If Token.Url.Contains("launcher.mojang.com/v1/objects") Then Urls = DlSourceLauncherOrMetaGet(Token.Url).ToList() 'Mappings
                 If Token.Url.Contains("launcher.mojang.com/v1/objects") Then Urls = DlSourceLauncherOrMetaGet(Token.Url).ToList() 'Mappings
-                If Token.Url.Contains("maven") Then Urls.Insert(0, Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOf("maven")), "https://download.mcbbs.net/").Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven"))
+                If Token.Url.Contains("maven") Then Urls.Insert(0, Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOf("maven")), "https://bmclapi2.bangbang93.com/").Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven"))
             End If
             End If
             If Token.LocalPath.Contains("transformer-discovery-service") Then
             If Token.LocalPath.Contains("transformer-discovery-service") Then
                 'Transformer 文件释放
                 'Transformer 文件释放
@@ -1970,7 +1972,6 @@ NextVersion:
                 Dim OptiFineBase As String = Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_")
                 Dim OptiFineBase As String = Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_")
                 OptiFineBase = "/maven/com/optifine/" & OptiFineBase
                 OptiFineBase = "/maven/com/optifine/" & OptiFineBase
                 If OptiFineBase.Contains("_pre") Then OptiFineBase = OptiFineBase.Replace("com/optifine/", "com/optifine/preview_")
                 If OptiFineBase.Contains("_pre") Then OptiFineBase = OptiFineBase.Replace("com/optifine/", "com/optifine/preview_")
-                Urls.Add("https://download.mcbbs.net" & OptiFineBase)
                 Urls.Add("https://bmclapi2.bangbang93.com" & OptiFineBase)
                 Urls.Add("https://bmclapi2.bangbang93.com" & OptiFineBase)
             ElseIf Urls.Count <= 2 Then
             ElseIf Urls.Count <= 2 Then
                 '普通文件
                 '普通文件

+ 39 - 7
Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb

@@ -13,6 +13,14 @@ Public Module ModMod
         Public Sub New(Path As String)
         Public Sub New(Path As String)
             Me.Path = If(Path, "")
             Me.Path = If(Path, "")
         End Sub
         End Sub
+        ''' <summary>
+        ''' Mod 的完整路径,去除最后的 .disabled 和 .old。
+        ''' </summary>
+        Public ReadOnly Property RawPath As String
+            Get
+                Return GetPathFromFullPath(Path) & RawFileName
+            End Get
+        End Property
 
 
         ''' <summary>
         ''' <summary>
         ''' Mod 的完整文件名。
         ''' Mod 的完整文件名。
@@ -768,6 +776,18 @@ VersionFindFail:
         End Property
         End Property
         Private _ModrinthHash As String
         Private _ModrinthHash As String
 
 
+#End Region
+
+#Region "API"
+
+        Public Overrides Function ToString() As String
+            Return $"{State} - {Path}"
+        End Function
+        Public Overrides Function Equals(obj As Object) As Boolean
+            Dim target = TryCast(obj, McMod)
+            Return target IsNot Nothing AndAlso Path = target.Path
+        End Function
+
 #End Region
 #End Region
 
 
         ''' <summary>
         ''' <summary>
@@ -844,6 +864,17 @@ VersionFindFail:
                 '加载 McMod 对象
                 '加载 McMod 对象
                 Dim ModEntry As New McMod(ModFile.FullName)
                 Dim ModEntry As New McMod(ModFile.FullName)
                 ModEntry.Load()
                 ModEntry.Load()
+                Dim DumpMod As McMod = ModList.FirstOrDefault(Function(m) m.RawFileName = ModEntry.RawFileName)
+                If DumpMod IsNot Nothing Then
+                    Dim DisabledMod As McMod = If(DumpMod.State = McMod.McModState.Disabled, DumpMod, ModEntry)
+                    Log($"[Mod] 重复的 Mod 文件:{DumpMod.FileName} 与 {ModEntry.FileName},已忽略 {DisabledMod.FileName}", LogLevel.Debug)
+                    If DisabledMod Is ModEntry Then
+                        Continue For
+                    Else
+                        ModList.Remove(DisabledMod)
+                        ModUpdateList.Remove(DisabledMod)
+                    End If
+                End If
                 ModList.Add(ModEntry)
                 ModList.Add(ModEntry)
                 '读取 Comp 缓存
                 '读取 Comp 缓存
                 If ModEntry.State = McMod.McModState.Unavaliable Then Continue For
                 If ModEntry.State = McMod.McModState.Unavaliable Then Continue For
@@ -860,13 +891,14 @@ VersionFindFail:
             Log($"[Mod] 共有 {ModList.Count} 个 Mod,其中 {ModUpdateList.Where(Function(m) m.Comp Is Nothing).Count} 个需要联网获取信息,{ModUpdateList.Where(Function(m) m.Comp IsNot Nothing).Count} 个需要更新信息")
             Log($"[Mod] 共有 {ModList.Count} 个 Mod,其中 {ModUpdateList.Where(Function(m) m.Comp Is Nothing).Count} 个需要联网获取信息,{ModUpdateList.Where(Function(m) m.Comp IsNot Nothing).Count} 个需要更新信息")
 
 
             '排序
             '排序
-            ModList = Sort(ModList, Function(Left As McMod, Right As McMod) As Boolean
-                                        If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
-                                            Return Left.State = McMod.McModState.Unavaliable
-                                        Else
-                                            Return Not Right.FileName.CompareTo(Left.FileName)
-                                        End If
-                                    End Function)
+            ModList = Sort(ModList,
+            Function(Left As McMod, Right As McMod) As Boolean
+                If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
+                    Return Left.State = McMod.McModState.Unavaliable
+                Else
+                    Return Not Right.FileName.CompareTo(Left.FileName)
+                End If
+            End Function)
 
 
             '回设
             '回设
             If Loader.IsAborted Then Exit Sub
             If Loader.IsAborted Then Exit Sub

+ 1 - 1
Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb

@@ -96,7 +96,7 @@
                                        '设置窗口标题
                                        '设置窗口标题
                                        For i = 1 To 3
                                        For i = 1 To 3
                                            If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
                                            If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
-                                               Dim RealTitle As String = WindowTitle.Replace("{time}", Date.Now.ToShortTimeString)
+                                               Dim RealTitle As String = WindowTitle.Replace("{date}", Date.Now.ToString("yyyy/M/d")).Replace("{time}", Date.Now.ToString("HH:mm:ss"))
                                                SetWindowText(WindowHandle, RealTitle.ToCharArray)
                                                SetWindowText(WindowHandle, RealTitle.ToCharArray)
                                            End If
                                            End If
                                            Thread.Sleep(64)
                                            Thread.Sleep(64)

+ 1 - 1
Plain Craft Launcher 2/Modules/ModMusic.vb

@@ -25,7 +25,7 @@
                 For Each File In EnumerateFiles(Path & "PCL\Musics\")
                 For Each File In EnumerateFiles(Path & "PCL\Musics\")
                     '文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
                     '文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
                     Dim Ext As String = File.Extension.ToLower
                     Dim Ext As String = File.Extension.ToLower
-                    If {"ini", "jpg", "txt", "cfg", "lrc", "db", "png"}.Contains(Ext) Then Continue For
+                    If {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(Ext) Then Continue For
                     MusicAllList.Add(File.FullName)
                     MusicAllList.Add(File.FullName)
                 Next
                 Next
             End If
             End If

+ 2 - 2
Plain Craft Launcher 2/My Project/AssemblyInfo.vb

@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
 ' 方法是按如下所示使用“*”
 ' 方法是按如下所示使用“*”
 
 
-<Assembly: AssemblyVersion("2.6.13.0")>
-<Assembly: AssemblyFileVersion("2.6.13.0")>
+<Assembly: AssemblyVersion("2.6.14.0")>
+<Assembly: AssemblyFileVersion("2.6.14.0")>
 <Assembly: NeutralResourcesLanguage("")>
 <Assembly: NeutralResourcesLanguage("")>

+ 81 - 97
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -519,10 +519,8 @@ Public Module ModDownloadLib
                 Dim Sources As New List(Of String)
                 Dim Sources As New List(Of String)
                 'BMCLAPI 源
                 'BMCLAPI 源
                 If DownloadInfo.IsPreview Then
                 If DownloadInfo.IsPreview Then
-                    Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
                     Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
                     Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
                 Else
                 Else
-                    Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
                     Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
                     Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
                 End If
                 End If
                 '官方源
                 '官方源
@@ -682,30 +680,28 @@ Retry:
         Dim Loaders As New List(Of LoaderBase)
         Dim Loaders As New List(Of LoaderBase)
         '获取下载地址
         '获取下载地址
         Loaders.Add(New LoaderTask(Of DlOptiFineListEntry, List(Of NetFile))("获取 OptiFine 下载地址",
         Loaders.Add(New LoaderTask(Of DlOptiFineListEntry, List(Of NetFile))("获取 OptiFine 下载地址",
-                                                            Sub(Task As LoaderTask(Of DlOptiFineListEntry, List(Of NetFile)))
-                                                                Dim Sources As New List(Of String)
-                                                                'BMCLAPI 源
-                                                                If DownloadInfo.IsPreview Then
-                                                                    Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
-                                                                    Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
-                                                                Else
-                                                                    Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
-                                                                    Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
-                                                                End If
-                                                                '官方源
-                                                                Dim PageData As String
-                                                                Try
-                                                                    PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
-                                                                    Task.Progress = 0.8
-                                                                    Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
-                                                                    Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
-                                                                Catch ex As Exception
-                                                                    Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
-                                                                End Try
-                                                                Task.Progress = 0.9
-                                                                '构造文件请求
-                                                                Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, TargetFolder, New FileChecker(MinSize:=64 * 1024))}
-                                                            End Sub) With {.ProgressWeight = 6})
+        Sub(Task As LoaderTask(Of DlOptiFineListEntry, List(Of NetFile)))
+            Dim Sources As New List(Of String)
+            'BMCLAPI 源
+            If DownloadInfo.IsPreview Then
+                Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
+            Else
+                Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
+            End If
+            '官方源
+            Dim PageData As String
+            Try
+                PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
+                Task.Progress = 0.8
+                Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
+                Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
+            Catch ex As Exception
+                Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
+            End Try
+            Task.Progress = 0.9
+            '构造文件请求
+            Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, TargetFolder, New FileChecker(MinSize:=64 * 1024))}
+        End Sub) With {.ProgressWeight = 6})
         '下载
         '下载
         Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 10, .Block = True})
         Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 10, .Block = True})
         Return Loaders
         Return Loaders
@@ -852,22 +848,11 @@ Retry:
                     Case "1.6.2"
                     Case "1.6.2"
                         Address.Add("https://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar")
                         Address.Add("https://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar")
                     Case "1.5.2"
                     Case "1.5.2"
-                        'Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar",
-                        '                  "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar"})
                         Address.Add("https://dl.liteloader.com/redist/1.5.2/liteloader-installer-1.5.2-01.jar")
                         Address.Add("https://dl.liteloader.com/redist/1.5.2/liteloader-installer-1.5.2-01.jar")
                     Case Else
                     Case Else
                         Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")")
                         Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")")
                 End Select
                 End Select
             Else
             Else
-                'BMCLAPI 源下载的是安装后的 Jar,而不是 Installer,故无法使用
-                ''BMCLAPI 源
-                'If DownloadInfo.IsPreview Then
-                '    Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar",
-                '                      "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar"})
-                'Else
-                '    Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar",
-                '                      "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar"})
-                'End If
                 '官方源
                 '官方源
                 Address.Add("http://jenkins.liteloader.com/job/LiteLoaderInstaller%20" & DownloadInfo.Inherit & "/lastSuccessfulBuild/artifact/" & If(DownloadInfo.Inherit = "1.8", "ant/dist/", "build/libs/") & DownloadInfo.FileName)
                 Address.Add("http://jenkins.liteloader.com/job/LiteLoaderInstaller%20" & DownloadInfo.Inherit & "/lastSuccessfulBuild/artifact/" & If(DownloadInfo.Inherit = "1.8", "ant/dist/", "build/libs/") & DownloadInfo.FileName)
             End If
             End If
@@ -1097,15 +1082,14 @@ Retry:
             Dim Loaders As New List(Of LoaderBase)
             Dim Loaders As New List(Of LoaderBase)
             '获取下载地址
             '获取下载地址
             Loaders.Add(New LoaderTask(Of DlForgeVersionEntry, List(Of NetFile))("获取下载地址",
             Loaders.Add(New LoaderTask(Of DlForgeVersionEntry, List(Of NetFile))("获取下载地址",
-                                                            Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
-                                                                Dim Files As New List(Of NetFile)
-                                                                Files.Add(New NetFile({
-                                                                        "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
-                                                                        "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
-                                                                        "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
-                                                                    }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
-                                                                Task.Output = Files
-                                                            End Sub) With {.ProgressWeight = 0.1, .Show = False})
+            Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
+                Dim Files As New List(Of NetFile)
+                Files.Add(New NetFile({
+                    "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
+                    "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
+                }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
+                Task.Output = Files
+            End Sub) With {.ProgressWeight = 0.1, .Show = False})
             '下载
             '下载
             Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile)) With {.ProgressWeight = 6})
             Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile)) With {.ProgressWeight = 6})
 
 
@@ -1123,7 +1107,7 @@ Retry:
 
 
     Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
     Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
         '选择 Java
         '选择 Java
-        Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+        Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
         If Java Is Nothing Then
         If Java Is Nothing Then
             If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
             If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
             '开始自动下载
             '开始自动下载
@@ -1133,7 +1117,7 @@ Retry:
                 Thread.Sleep(10)
                 Thread.Sleep(10)
             Loop
             Loop
             '检查下载结果
             '检查下载结果
-            Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+            Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
             If Task.IsAborted Then Exit Sub
             If Task.IsAborted Then Exit Sub
             If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
             If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
         End If
         End If
@@ -1160,50 +1144,52 @@ Retry:
             Dim LastResults As New Queue(Of String)
             Dim LastResults As New Queue(Of String)
             Using outputWaitHandle As New AutoResetEvent(False)
             Using outputWaitHandle As New AutoResetEvent(False)
                 Using errorWaitHandle As New AutoResetEvent(False)
                 Using errorWaitHandle As New AutoResetEvent(False)
-                    AddHandler process.OutputDataReceived, Function(sender, e)
-                                                               Try
-                                                                   If e.Data Is Nothing Then
-                                                                       outputWaitHandle.[Set]()
-                                                                   Else
-                                                                       LastResults.Enqueue(e.Data)
-                                                                       If LastResults.Count > 100 Then LastResults.Dequeue()
-                                                                       ForgeInjectorLine(e.Data, Task)
-                                                                   End If
-                                                               Catch ex As ObjectDisposedException
-                                                               Catch ex As Exception
-                                                                   Log(ex, "读取 Forge 安装器信息失败")
-                                                               End Try
-                                                               Try
-                                                                   If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
-                                                                       Log("[Installer] 由于任务取消,已中止 Forge 安装")
-                                                                       process.Kill()
-                                                                   End If
-                                                               Catch
-                                                               End Try
-                                                               Return Nothing
-                                                           End Function
-                    AddHandler process.ErrorDataReceived, Function(sender, e)
-                                                              Try
-                                                                  If e.Data Is Nothing Then
-                                                                      errorWaitHandle.[Set]()
-                                                                  Else
-                                                                      LastResults.Enqueue(e.Data)
-                                                                      If LastResults.Count > 100 Then LastResults.Dequeue()
-                                                                      ForgeInjectorLine(e.Data, Task)
-                                                                  End If
-                                                              Catch ex As ObjectDisposedException
-                                                              Catch ex As Exception
-                                                                  Log(ex, "读取 Forge 安装器错误信息失败")
-                                                              End Try
-                                                              Try
-                                                                  If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
-                                                                      Log("[Installer] 由于任务取消,已中止 Forge 安装")
-                                                                      process.Kill()
-                                                                  End If
-                                                              Catch
-                                                              End Try
-                                                              Return Nothing
-                                                          End Function
+                    AddHandler process.OutputDataReceived,
+                    Function(sender, e)
+                        Try
+                            If e.Data Is Nothing Then
+                                outputWaitHandle.[Set]()
+                            Else
+                                LastResults.Enqueue(e.Data)
+                                If LastResults.Count > 100 Then LastResults.Dequeue()
+                                ForgeInjectorLine(e.Data, Task)
+                            End If
+                        Catch ex As ObjectDisposedException
+                        Catch ex As Exception
+                            Log(ex, "读取 Forge 安装器信息失败")
+                        End Try
+                        Try
+                            If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
+                                Log("[Installer] 由于任务取消,已中止 Forge 安装")
+                                process.Kill()
+                            End If
+                        Catch
+                        End Try
+                        Return Nothing
+                    End Function
+                    AddHandler process.ErrorDataReceived,
+                    Function(sender, e)
+                        Try
+                            If e.Data Is Nothing Then
+                                errorWaitHandle.[Set]()
+                            Else
+                                LastResults.Enqueue(e.Data)
+                                If LastResults.Count > 100 Then LastResults.Dequeue()
+                                ForgeInjectorLine(e.Data, Task)
+                            End If
+                        Catch ex As ObjectDisposedException
+                        Catch ex As Exception
+                            Log(ex, "读取 Forge 安装器错误信息失败")
+                        End Try
+                        Try
+                            If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
+                                Log("[Installer] 由于任务取消,已中止 Forge 安装")
+                                process.Kill()
+                            End If
+                        Catch
+                        End Try
+                        Return Nothing
+                    End Function
                     process.Start()
                     process.Start()
                     process.BeginOutputReadLine()
                     process.BeginOutputReadLine()
                     process.BeginErrorReadLine()
                     process.BeginErrorReadLine()
@@ -1321,8 +1307,7 @@ Retry:
                 '添加主文件
                 '添加主文件
                 Dim Files As New List(Of NetFile) From {New NetFile({
                 Dim Files As New List(Of NetFile) From {New NetFile({
                     "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
                     "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
-                    "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
-                    "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
+                    "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
                 }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))}
                 }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))}
                 Task.Output = Files
                 Task.Output = Files
             End Sub) With {.ProgressWeight = 0.5, .Show = False})
             End Sub) With {.ProgressWeight = 0.5, .Show = False})
@@ -1650,7 +1635,7 @@ Retry:
         RunInNewThread(Sub()
         RunInNewThread(Sub()
                            Try
                            Try
                                Log("[Download] 刷新 Forge 推荐版本缓存开始")
                                Log("[Download] 刷新 Forge 推荐版本缓存开始")
-                               Dim Result As String = NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/forge/promos", "https://download.mcbbs.net/forge/promos"})
+                               Dim Result As String = NetGetCodeByDownload("https://bmclapi2.bangbang93.com/forge/promos")
                                If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
                                If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
                                Dim ResultJson As JContainer = GetJson(Result)
                                Dim ResultJson As JContainer = GetJson(Result)
                                '获取所有推荐版本列表
                                '获取所有推荐版本列表
@@ -1721,7 +1706,6 @@ Retry:
             '下载
             '下载
             'BMCLAPI 不支持 Fabric Installer 下载
             'BMCLAPI 不支持 Fabric Installer 下载
             Dim Address As New List(Of String)
             Dim Address As New List(Of String)
-            'Address.Add(Url.Replace("maven.fabricmc.net", "download.mcbbs.net/maven"))
             Address.Add(Url)
             Address.Add(Url)
             Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
             Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
             '启动
             '启动
@@ -1759,7 +1743,7 @@ Retry:
                                                                 Task.Progress = 0.5
                                                                 Task.Progress = 0.5
                                                                 '构造文件请求
                                                                 '构造文件请求
                                                                 Task.Output = New List(Of NetFile) From {New NetFile({
                                                                 Task.Output = New List(Of NetFile) From {New NetFile({
-                                                                    "https://download.mcbbs.net/fabric-meta/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json",
+                                                                    "https://bmclapi2.bangbang93.com/fabric-meta/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json",
                                                                     "https://meta.fabricmc.net/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json"
                                                                     "https://meta.fabricmc.net/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json"
                                                                 }, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))}
                                                                 }, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))}
                                                                 '新建 mods 文件夹
                                                                 '新建 mods 文件夹

+ 0 - 1
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml

@@ -16,7 +16,6 @@
                     <StackPanel Orientation="Horizontal" Height="35">
                     <StackPanel Orientation="Horizontal" Height="35">
                         <local:MyButton x:Name="BtnIntroWeb"   MinWidth="140" Text="转到 CurseForge" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
                         <local:MyButton x:Name="BtnIntroWeb"   MinWidth="140" Text="转到 CurseForge" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
                         <local:MyButton x:Name="BtnIntroWiki"  MinWidth="140" Text="转到 MC 百科" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
                         <local:MyButton x:Name="BtnIntroWiki"  MinWidth="140" Text="转到 MC 百科" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
-                        <local:MyButton x:Name="BtnIntroMCBBS" MinWidth="140" Text="转到 MCBBS" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
                         <local:MyButton x:Name="BtnIntroCopy"  MinWidth="140" Text="复制名称" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
                         <local:MyButton x:Name="BtnIntroCopy"  MinWidth="140" Text="复制名称" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
                     </StackPanel>
                     </StackPanel>
                 </StackPanel>
                 </StackPanel>

+ 0 - 4
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb

@@ -145,7 +145,6 @@
         '决定按钮显示
         '决定按钮显示
         BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
         BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
         BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
         BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
-        BtnIntroMCBBS.Visibility = If(Project.MCBBS = 0, Visibility.Collapsed, Visibility.Visible)
 
 
         AniControlEnabled -= 1
         AniControlEnabled -= 1
     End Sub
     End Sub
@@ -323,9 +322,6 @@
     Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
     Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
         OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
         OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
     End Sub
     End Sub
-    Private Sub BtnIntroMCBBS_Click(sender As Object, e As EventArgs) Handles BtnIntroMCBBS.Click
-        OpenWebsite("https://www.mcbbs.net/thread-" & Project.MCBBS & "-1-1.html")
-    End Sub
     Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
     Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
         ClipboardSet(CompItem.LabTitle.Text)
         ClipboardSet(CompItem.LabTitle.Text)
     End Sub
     End Sub

+ 1 - 1
Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml

@@ -33,7 +33,7 @@
                     <Path Name="PathTypeOne" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
                     <Path Name="PathTypeOne" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
                           Data="">
                           Data="">
                         <Path.RenderTransform>
                         <Path.RenderTransform>
-                            <ScaleTransform ScaleX="1.2" ScaleY="1.2" />
+                            <ScaleTransform ScaleX="1.05" ScaleY="1.05" />
                         </Path.RenderTransform>
                         </Path.RenderTransform>
                     </Path>
                     </Path>
                     <TextBlock Name="LabTypeOne" Text="" FontSize="14" VerticalAlignment="Center" Grid.Column="1" Foreground="{DynamicResource ColorBrush3}" Margin="15,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
                     <TextBlock Name="LabTypeOne" Text="" FontSize="14" VerticalAlignment="Center" Grid.Column="1" Foreground="{DynamicResource ColorBrush3}" Margin="15,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />

+ 2 - 2
Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb

@@ -286,7 +286,7 @@ UnknownType:
                 Setup.Set("LoginType", McLoginType.Ms)
                 Setup.Set("LoginType", McLoginType.Ms)
                 PanType.Visibility = Visibility.Collapsed
                 PanType.Visibility = Visibility.Collapsed
                 PanTypeOne.Visibility = Visibility.Visible
                 PanTypeOne.Visibility = Visibility.Visible
-                PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonMs)
+                PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield)
                 LabTypeOne.Text = "正版登录"
                 LabTypeOne.Text = "正版登录"
                 RadioLoginType5.Visibility = Visibility.Visible
                 RadioLoginType5.Visibility = Visibility.Visible
                 RadioLoginType0.Visibility = Visibility.Collapsed
                 RadioLoginType0.Visibility = Visibility.Collapsed
@@ -295,7 +295,7 @@ UnknownType:
                 Setup.Set("LoginType", McLoginType.Legacy)
                 Setup.Set("LoginType", McLoginType.Legacy)
                 PanType.Visibility = Visibility.Collapsed
                 PanType.Visibility = Visibility.Collapsed
                 PanTypeOne.Visibility = Visibility.Visible
                 PanTypeOne.Visibility = Visibility.Visible
-                PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonUser)
+                PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline)
                 LabTypeOne.Text = "离线登录"
                 LabTypeOne.Text = "离线登录"
             Case 3 '统一通行证
             Case 3 '统一通行证
                 If Setup.Get("CacheNideAccess") = "" Then
                 If Setup.Get("CacheNideAccess") = "" Then

+ 0 - 559
Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb

@@ -58,307 +58,21 @@
 
 
     '检查网络状态
     '检查网络状态
     Private Shared Sub InitPingCheck(Task As LoaderTask(Of Integer, Integer))
     Private Shared Sub InitPingCheck(Task As LoaderTask(Of Integer, Integer))
-        PingTime = 0
-        Try
-            Log("[HiPer] 网络检测:连通检测开始")
-            Dim StartTime = Date.Now.Ticks
-            Dim Result As String = NetGetCodeByClient("https://www.baidu.com/duty/", Encoding.UTF8, 20000, "")
-            Dim DuringTime As Integer = (Date.Now.Ticks - StartTime) / 10000
-            If Result.Contains("百度") Then
-                Log("[HiPer] 网络检测:连通检测成功(" & DuringTime & "ms)")
-                PingTime = DuringTime
-            Else
-                Log("[HiPer] 网络检测:连通检测失败(获取的内容有误)")
-                PingTime = -1
-            End If
-        Catch ex As ThreadInterruptedException
-        Catch ex As Exception
-            Log(ex, "连通检测失败")
-            PingTime = -1
-        End Try
     End Sub
     End Sub
     Private Shared Sub InitIpCheck()
     Private Shared Sub InitIpCheck()
-        IpCheckStatus = LoadState.Loading
-        Try
-            Log("[HiPer] 网络检测:IP 地址检测开始")
-            Dim IpCheckResult As String = NetRequestOnce("https://ipinfo.io/json", "GET", "", "application/json", 10000)
-            Dim Country As String = GetJson(IpCheckResult)("country")
-            IpIsInChina = Country = "CN"
-            IpCheckStatus = LoadState.Finished
-            Log("[HiPer] 网络检测:IP 地址检测结果:" & Country)
-        Catch ex As ThreadInterruptedException
-            IpCheckStatus = LoadState.Aborted
-        Catch ex As Exception
-            Log(ex, "IP 地址检测失败")
-            IpCheckStatus = LoadState.Failed
-            IpIsInChina = True
-        End Try
     End Sub
     End Sub
     Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean
     Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean
     Private Shared Sub InitCheck(Task As LoaderTask(Of Integer, Integer))
     Private Shared Sub InitCheck(Task As LoaderTask(Of Integer, Integer))
-        '检查协议
-        If Not Setup.Get("LinkEula") Then
-Reopen:
-            Select Case MyMsgBox("PCL 的联机服务由速聚授权提供。" & vbCrLf & "在使用前,你需要同意速聚的用户服务协议和隐私政策。", "协议授权", "同意", "拒绝", "查看用户服务协议和隐私政策")
-                Case 1
-                    Setup.Set("LinkEula", True)
-                Case 2
-                    Throw New Exception("$你拒绝了用户服务协议……")
-                Case 3
-                    OpenWebsite("https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxMTMyODk3Mg==&action=getalbum&album_id=2585385685407514625&scene=173&from_msgid=2247483720&from_itemidx=1&count=3&nolastread=1#wechat_redirect")
-                    GoTo Reopen
-            End Select
-        End If
-        '等待网络环境检查加载器结束
-        SetLoadDesc("正在检查网络环境……", "检查网络环境")
-        Do While Task.State = LoadState.Loading AndAlso Not (PingTime <> 0 AndAlso IpCheckStatus <> LoadState.Loading)
-            Thread.Sleep(50)
-        Loop
-        '获取网络环境检查结果
-        If IpCheckStatus = LoadState.Finished AndAlso Not IpIsInChina Then
-            If MyMsgBox("检测到你的 IP 不在中国,这会导致联机变得非常不稳定。" & vbCrLf &
-                        "如果你正开着加速器或者 VPN,请先关闭它们,然后再继续……", "警告", "继续", "取消", IsWarn:=True, ForceWait:=True) = 2 Then
-                Throw New Exception("$请在关闭加速器或者 VPN 后点击重试。")
-            End If
-            Task.Progress = 0.5
-            InitIpCheck() '重新进行检查
-        End If
-        '没有联网
-        If IpCheckStatus = LoadState.Failed AndAlso PingTime = -1 Then
-            Throw New Exception("$PCL 没法连上网……" & vbCrLf & "如果你改变了网络环境,请重启 PCL。")
-        End If
     End Sub
     End Sub
 
 
     '获取所需文件
     '获取所需文件
     Private Shared Sub InitGetFile(Task As LoaderTask(Of Integer, List(Of NetFile)))
     Private Shared Sub InitGetFile(Task As LoaderTask(Of Integer, List(Of NetFile)))
-        '初始化
-        SetLoadDesc("正在初始化……", "初始化")
-        Directory.CreateDirectory(PathHiper)
-        WriteFile(PathHiper & "HiPer 联机模块.vbs", "createobject(""wscript.shell"").run """"""" & PathHiper & "HiPer 联机模块.exe"""" v"", 0", Encoding:=Encoding.GetEncoding("GB18030")) '准备启动脚本
-        '获取索引码文件
-        SetLoadDesc("正在获取索引码文件……", "获取索引码文件")
-        Dim Cert As String = RunInUiWait(Function() FrmLinkHiper.TextCert.Text)
-        Log("[Hiper] 联机索引码:" & Cert)
-        If Cert = Setup.Get("LinkHiperCertLast") AndAlso File.Exists(PathHiper & "cert.yml") Then
-            Log("[Hiper] 索引码与上次输入的一致,跳过获取步骤")
-        Else
-            Dim CertRaw As String
-            Try
-                CertRaw = NetRequestOnce("https://cert.mcer.cn/" & Cert & ".yml", "GET", "", "")
-            Catch ex As Exception
-                If GetExceptionSummary(ex).Contains("(404)") Then
-                    Throw New CertOutdatedException '索引码无效或已过期
-                ElseIf GetExceptionSummary(ex).Contains("too many requests") Then
-                    Throw New Exception("你的尝试太频繁了,请暂时啥都别点,等两分钟后再试……")
-                Else
-                    Throw
-                End If
-            End Try
-            If Not CertRaw.Contains("WARNING <<< AUTO SYNC AREA") Then Throw New Exception("获取到的索引码文件内容有误!")
-            WriteFile(PathHiper & "cert.yml", CertRaw)
-            Setup.Set("LinkHiperCertLast", Cert)
-            Setup.Set("LinkHiperCertTime", "")
-        End If
-        Task.Progress = 0.25
-        '获取 CPU 架构
-        SetLoadDesc("正在获取 CPU 架构……", "获取 CPU 架构")
-        Dim Architecture As String
-        Select Case GetType(String).Assembly.GetName().ProcessorArchitecture
-            Case Reflection.ProcessorArchitecture.X86
-                Architecture = "386"
-            Case Reflection.ProcessorArchitecture.Amd64
-                Architecture = "amd64"
-            Case Reflection.ProcessorArchitecture.Arm
-                Architecture = "arm64"
-            Case Else
-                Architecture = "arm64"
-                Log("[Hiper] 当前 CPU 架构为 " & GetStringFromEnum(GetType(String).Assembly.GetName().ProcessorArchitecture) & ",没有最适合的项,这可能会导致联机模块无法启动!", LogLevel.Debug)
-        End Select
-        Log("[Hiper] CPU 架构:" & Architecture)
-        '检查更新:hiper.exe
-        SetLoadDesc("正在检查联机模块本体更新……", "检查联机模块本体更新")
-        Dim RequiredFiles As New List(Of NetFile)
-        Dim Checksums As String = Nothing, ChecksumHiper As String = Nothing
-        If File.Exists(PathHiper & "HiPer 联机模块.exe") Then
-            Try
-                Checksums = NetGetCodeByDownload({
-                    "http://mirror.hiper.cn.s2.the.bb/packages.sha1",
-                    "http://mirror.hiper.cn.s3.the.bb:175/packages.sha1",
-                    "https://gitcode.net/to/hiper/-/raw/master/packages.sha1",
-                    "https://cert.mcer.cn/mirror/packages.sha1"
-                })
-            Catch ex As Exception
-                Log(ex, "获取联机模块更新信息失败,将会强制启动联机模块", LogLevel.Hint)
-                GoTo FinishHiperFileCheck
-            End Try
-            ChecksumHiper = RegexSeek(Checksums, "[0-9a-f]{40}(?=  windows-" & Architecture & "/hiper.exe)")
-            If ChecksumHiper Is Nothing Then
-                Log("[Hiper] 未找到联机模块更新信息,将会强制启动联机模块", LogLevel.Hint)
-                GoTo FinishHiperFileCheck
-            End If
-            Log("[Hiper] hiper.exe 的所需 SHA1:" & ChecksumHiper)
-            If ChecksumHiper = GetFileSHA1(PathHiper & "HiPer 联机模块.exe") Then
-                Log("[Hiper] hiper.exe 文件校验通过,无需重新下载")
-                GoTo FinishHiperFileCheck
-            End If
-        End If
-        Log("[Hiper] 需要重新下载 hiper.exe")
-        RequiredFiles.Add(New NetFile({"http://mirror.hiper.cn.s2.the.bb/windows-" & Architecture & "/hiper.exe",
-                                       "http://mirror.hiper.cn.s3.the.bb:175/windows-" & Architecture & "/hiper.exe",
-                                       "https://gitcode.net/to/hiper/-/raw/master/windows-" & Architecture & "/hiper.exe?inline=false",
-                                       "https://cert.mcer.cn/mirror/windows-" & Architecture & "/hiper.exe"},
-                                      PathHiper & "HiPer 联机模块.exe",
-                                      New FileChecker(Hash:=ChecksumHiper, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
-FinishHiperFileCheck:
-        Task.Progress = 0.5
-        '检查更新:wintun.dll
-        Dim ChecksumWintun As String = Nothing
-        If File.Exists(PathHiper & "wintun.dll") Then
-            Try
-                If Checksums Is Nothing Then
-                    Checksums = NetGetCodeByDownload({
-                        "http://mirror.hiper.cn.s2.the.bb/packages.sha1",
-                        "http://mirror.hiper.cn.s3.the.bb:175/packages.sha1",
-                        "https://gitcode.net/to/hiper/-/raw/master/packages.sha1",
-                        "https://cert.mcer.cn/mirror/packages.sha1"
-                    })
-                End If
-            Catch ex As Exception
-                Log(ex, "获取联机模块更新信息失败,将会强制启动联机模块", LogLevel.Hint)
-                GoTo FinishWintunFileCheck
-            End Try
-            ChecksumWintun = RegexSeek(Checksums, "[0-9a-f]{40}(?=  windows-" & Architecture & "/wintun.dll)")
-            If ChecksumWintun Is Nothing Then
-                Log("[Hiper] 未找到联机模块更新信息,将会强制启动联机模块", LogLevel.Hint)
-                GoTo FinishWintunFileCheck
-            End If
-            Log("[Hiper] wintun.dll 的所需 SHA1:" & ChecksumWintun)
-            If ChecksumWintun = GetFileSHA1(PathHiper & "wintun.dll") Then
-                Log("[Hiper] wintun.dll 文件校验通过,无需重新下载")
-                GoTo FinishWintunFileCheck
-            End If
-        End If
-        Log("[Hiper] 需要重新下载 wintun.dll")
-        RequiredFiles.Add(New NetFile({"http://mirror.hiper.cn.s2.the.bb/windows-" & Architecture & "/wintun.dll",
-                                       "http://mirror.hiper.cn.s3.the.bb:175/windows-" & Architecture & "/wintun.dll",
-                                       "https://gitcode.net/to/hiper/-/raw/master/windows-" & Architecture & "/wintun.dll?inline=false",
-                                       "https://cert.mcer.cn/mirror/windows-" & Architecture & "/wintun.dll"},
-                                      PathHiper & "wintun.dll",
-                                      New FileChecker(Hash:=ChecksumWintun, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
-FinishWintunFileCheck:
-        Task.Progress = 0.75
-        '检查更新:MCB
-        If Not IsServerSide Then
-            SetLoadDesc("正在检查联机模块组件更新……", "检查联机模块组件更新")
-            Dim ChecksumMcb As String = Nothing
-            If File.Exists(PathHiper & "MCB 联机模块.exe") Then
-                Try
-                    Checksums = NetGetCodeByDownload({
-                        "http://mirror.hiper.cn.s2.the.bb/utils/minecraft-broadcast/packages.sha1",
-                        "http://mirror.hiper.cn.s3.the.bb:175/utils/minecraft-broadcast/packages.sha1",
-                        "https://gitcode.net/to/hiper/-/raw/master/utils/minecraft-broadcast/packages.sha1",
-                        "https://cert.mcer.cn/mirror/utils/minecraft-broadcast/packages.sha1"
-                    })
-                Catch ex As Exception
-                    Log(ex, "获取联机模块组件更新信息失败,将会强制启动联机模块组件", LogLevel.Hint)
-                    GoTo FinishMcbFileCheck
-                End Try
-                ChecksumMcb = RegexSeek(Checksums, "[0-9a-f]{40}(?=  mcb-windows-" & Architecture & ".exe)")
-                If ChecksumMcb Is Nothing Then
-                    Log("[Hiper] 未找到联机模块组件更新信息,将会强制启动联机模块组件", LogLevel.Hint)
-                    GoTo FinishMcbFileCheck
-                End If
-                Log("[Hiper] mcb.exe 的所需 SHA1:" & ChecksumMcb)
-                If ChecksumMcb = GetFileSHA1(PathHiper & "MCB 联机模块.exe") Then
-                    Log("[Hiper] mcb.exe 文件校验通过,无需重新下载")
-                    GoTo FinishMcbFileCheck
-                End If
-            End If
-            Log("[Hiper] 需要重新下载 mcb.exe")
-            RequiredFiles.Add(New NetFile({
-                                       "http://mirror.hiper.cn.s2.the.bb/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe",
-                                       "http://mirror.hiper.cn.s3.the.bb:175/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe",
-                                       "https://gitcode.net/to/hiper/-/raw/master/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe?inline=false",
-                                       "https://cert.mcer.cn/mirror/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe"},
-                                      PathHiper & "MCB 联机模块.exe",
-                                      New FileChecker(Hash:=ChecksumMcb, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
-FinishMcbFileCheck:
-        End If
-        '添加 point.yml
-        RequiredFiles.Add(New NetFile({"https://cert.mcer.cn/point.yml"}, PathHiper & "point.yml", New FileChecker(MinSize:=200)))
-        '开始下载
-        SetLoadDesc("正在下载联机模块……", "下载联机模块")
-        Task.Output = RequiredFiles
     End Sub
     End Sub
     Public Class CertOutdatedException
     Public Class CertOutdatedException
         Inherits Exception
         Inherits Exception
     End Class
     End Class
     '启动联机模块
     '启动联机模块
     Private Shared Sub InitLaunch(Task As LoaderTask(Of Integer, Integer))
     Private Shared Sub InitLaunch(Task As LoaderTask(Of Integer, Integer))
-        '关闭运行中的 HiPer,然后再刷新凭证文件
-        SetLoadDesc("正在关闭运行中的联机模块……", "关闭运行中的联机模块")
-        HiperStop(True)
-        '准备凭证文件
-        SetLoadDesc("正在准备索引码文件……", "准备索引码文件")
-        Dim CertFile As String = ReadFile(PathHiper & "cert.yml")
-        '添加防火墙配置
-        If IsServerSide Then
-            CertFile += ("\n\ninbound:\n  port: " & HostPort & "\n  proto: tcp\n  host: any").Replace("\n", vbCrLf)
-        Else
-            CertFile += ("\n\noutbound:\n  port: " & HostPort & "\n  proto: tcp\n  host: " & HostIp).Replace("\n", vbCrLf)
-        End If
-        CertFile += "\nlogging:\n  format: json".Replace("\n", vbCrLf)
-        '更新 point.yml 段
-        Const SyncAreaRegex As String = "(?<=#[ ]*WARNING >>> AUTO SYNC AREA)[\s\S]+?(?=#[ ]*WARNING <<< AUTO SYNC AREA)"
-        CertFile = CertFile.Replace(RegexSeek(CertFile, SyncAreaRegex), RegexSeek(ReadFile(PathHiper & "point.yml"), SyncAreaRegex))
-        WriteFile(PathHiper & "config.yml", CertFile)
-        AllNodes = RegexSearch(CertFile.Replace(vbLf, "cr"), "(?<="")[0-9]+.[0-9]+.[0-9]+.[0-9]+(?=""( )?:( )?cr    - "")").Distinct.
-                   Where(Function(l) Not (l = "6.6.1.1" OrElse l = "6.6.2.2" OrElse l = "6.6.3.3")).ToList
-        If AllNodes.Count < 2 Then Throw New Exception("$索引码文件格式有误,未找到节点 IP!")
-        Task.Progress = 0.05
-        '尝试启动
-        HiperStart(Task) '启动失败会直接抛出异常
-        Task.Progress = 0.2
-        '检查连接情况
-        SetLoadDesc("正在连接到联机节点 (1/2)……" & If(IpIsInChina, "", vbCrLf & "由于网络不在中国,可能要花费很长时间。"), "检查联机节点")
-        If Not IsServerSide AndAlso HostIp = HiperIp Then Throw New Exception("$还搁这自己连自己?!不是吧?!")
-        Dim PingNodeResults As New List(Of Integer)
-        Dim UnpingedNodes As New List(Of String)(AllNodes)
-        Do Until PingNodeResults.Count >= 2 OrElse Task.IsAborted
-            Dim SelectedNode = RandomOne(UnpingedNodes)
-            Dim PingNode = Ping(SelectedNode, 4000)
-            If PingNode = -1 Then
-                Task.Progress += (If(PingNodeResults.Count = 0, 0.58, 0.88) - Task.Progress) * 0.3
-            Else
-                SetLoadDesc("正在连接到联机节点 (2/2)……" & If(IpIsInChina, "", vbCrLf & "由于网络不在中国,可能要花费很长时间。"), "检查联机节点")
-                Task.Progress = 0.6
-                PingNodeResults.Add(PingNode)
-                UnpingedNodes.Remove(SelectedNode)
-            End If
-        Loop
-        PingNodes = PingNodeResults.Min
-        Task.Progress = 0.9
-        '与房主 Ping 一下看看
-        If Not IsServerSide Then
-            SetLoadDesc("正在连接到房主……", "检查房主")
-            HostPing = -1 '实际上不使用本次 Ping 的结果,因为它真的偏大……
-            Do
-                Dim PingHost = Ping(HostIp, 4000, False)
-                If PingHost = -1 Then
-                    Thread.Sleep(500)
-                Else
-                    Exit Do
-                End If
-            Loop Until Task.IsAborted
-            '在等待 2 秒后再次触发 Ping,以刷新显示的 Ping
-            RunInNewThread(Sub()
-                               Thread.Sleep(2000)
-                               TaskPingHost.Start(True, IsForceRestart:=True)
-                           End Sub, "HiPer Delayed Ping")
-        End If
-        '刷新完成页面
-        SetLoadDesc("正在加载完成页面……", "加载完成页面")
-        RunInUiWait(AddressOf FrmLinkHiper.WatcherTimer1)
     End Sub
     End Sub
     Private Shared PingNodes As Integer, AllNodes As List(Of String)
     Private Shared PingNodes As Integer, AllNodes As List(Of String)
 
 
@@ -389,40 +103,6 @@ FinishMcbFileCheck:
     ''' </summary>
     ''' </summary>
     Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean
     Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean
         Return False
         Return False
-        HiperStop = False
-        '修改凭证
-        Dim ConfigContent As String = ReadFile(PathHiper & "config.yml")
-        If Not ConfigContent.Contains("enable: false") Then
-            WriteFile(PathHiper & "config.yml", ConfigContent & vbCrLf & "enable: false")
-        End If
-        '关闭所有进程
-        For Each ProcessObject In Process.GetProcesses
-            Dim IsHiper As Boolean = ProcessObject.ProcessName = "HiPer 联机模块"
-            Dim IsMcb As Boolean = ProcessObject.ProcessName = "MCB 联机模块"
-            If Not IsHiper AndAlso Not IsMcb Then Continue For
-            HiperStop = True
-            Try
-                If IsMcb Then
-                    ProcessObject.Kill()
-                    Log("[HiPer] 已结束进程 PID " & ProcessObject.Id & ":" & ProcessObject.ProcessName)
-                    If HiperStop AndAlso SleepWhenKilled Then Thread.Sleep(1000) '等待 1 秒确认进程已退出
-                Else 'IsHiper
-                    If SleepWhenKilled Then
-                        Thread.Sleep(4000) '等待 4 秒确认进程已退出
-                        If Not ProcessObject.HasExited Then
-                            ProcessObject.Kill()
-                            Log("[HiPer] 已结束进程 PID " & ProcessObject.Id & ":" & ProcessObject.ProcessName)
-                        End If
-                    End If
-                End If
-            Catch ex As Exception
-                Log(ex, "结束进程失败(" & ProcessObject.Id & "," & ProcessObject.ProcessName & ")")
-            End Try
-        Next
-        '初始化
-        HiperState = LoadState.Waiting
-        HiperProcessId = -1 : McbProcessId = -1
-        HiperCertTime = Nothing
     End Function
     End Function
     ''' <summary>
     ''' <summary>
     ''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。
     ''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。
@@ -430,125 +110,10 @@ FinishMcbFileCheck:
     ''' 若程序正在运行,则会先停止其运行。
     ''' 若程序正在运行,则会先停止其运行。
     ''' </summary>
     ''' </summary>
     Public Shared Sub HiperStart(Task As LoaderTask(Of Integer, Integer))
     Public Shared Sub HiperStart(Task As LoaderTask(Of Integer, Integer))
-        Try
-            SetLoadDesc("正在启动联机模块……", "启动联机模块")
-            PossibleFailReason = Nothing
-            '启动 Hiper
-            Log("[Hiper] 启动 Hiper 进程")
-            DeleteDirectory(PathHiper & "logs") '清理日志
-            Dim HiperInfo = New ProcessStartInfo With {
-                .FileName = "wscript",
-                .Arguments = """" & PathHiper & "HiPer 联机模块.vbs""",
-                .Verb = "runas" '需要管理员权限
-            }
-            Dim HiperVbsProcess As New Process() With {.StartInfo = HiperInfo}
-            HiperVbsProcess.Start()
-            '查找真正的 Hiper 进程
-            SetLoadDesc("联机模块正在启动……", "联机模块加载")
-            Do
-                Dim GotProcesses = Process.GetProcesses.
-                    Where(Function(l) l.ProcessName = "HiPer 联机模块" AndAlso Math.Abs((l.StartTime - Date.Now).TotalMinutes) < 1)
-                If GotProcesses.Count > 0 Then
-                    HiperProcessId = GotProcesses.First.Id
-                    Log("[Hiper] 已发现 Hiper 进程,PID:" & HiperProcessId)
-                    Exit Do
-                End If
-                Thread.Sleep(50)
-            Loop While Task.State = LoadState.Loading AndAlso Not Task.IsAborted
-            If HiperProcessId = -1 Then Throw New Exception("联机模块未能成功启动!")
-            HiperState = LoadState.Loading
-            Task.Progress = 0.15
-            '抓取日志
-            Dim LogLine As Integer = 0
-            Do
-                Thread.Sleep(100)
-                If Not File.Exists(PathHiper & "logs\hiper.log") Then Continue Do
-                Dim LogLines As String() = ReadFile(PathHiper & "logs\hiper.log").TrimEnd(vbLf).Split(vbLf)
-                For i = LogLine To LogLines.Count - 1
-                    HiperLogLine(LogLines(i), Task)
-                Next
-                LogLine = LogLines.Count
-            Loop While HiperState = LoadState.Loading AndAlso Not Task.IsAborted AndAlso Process.GetProcesses.Any(Function(l) l.Id = HiperProcessId)
-            '输出
-            If HiperState = LoadState.Finished Then
-                Log("[Hiper] Hiper 启动完成")
-            ElseIf PossibleFailReason IsNot Nothing AndAlso PossibleFailReason.Contains("索引码已过期") Then
-                Throw New CertOutdatedException()
-            Else
-                Throw New Exception(If(PossibleFailReason, "联机模块因未知原因启动失败!"))
-            End If
-            '启动 MCB
-            Task.Progress = 0.25
-            If Not IsServerSide Then
-                Try
-                    Log("[Hiper] 启动 MCB 进程")
-                    Dim McbInfo = New ProcessStartInfo With {
-                        .FileName = PathHiper & "MCB 联机模块.exe", .WorkingDirectory = PathHiper,
-                        .UseShellExecute = False, .CreateNoWindow = True,
-                        .RedirectStandardError = True, .RedirectStandardOutput = True,
-                        .Arguments = "-addr " & HostIp & ":" & HostPort & " -motd ""PCL 联机房间"""
-                    }
-                    Dim McbProcess As New Process() With {.StartInfo = McbInfo}
-                    McbProcess.Start()
-                    McbProcessId = McbProcess.Id
-                Catch ex As Exception
-                    Throw New Exception("联机模块组件启动失败", ex)
-                End Try
-            End If
-        Catch ex As Exception
-            Try
-                HiperStop(True) '由于启动失败停止进程
-            Catch
-            End Try
-            HiperState = LoadState.Failed
-            Throw
-        End Try
     End Sub
     End Sub
 
 
     'Hiper 日志
     'Hiper 日志
     Private Shared Sub HiperLogLine(Content As String, Task As LoaderTask(Of Integer, Integer))
     Private Shared Sub HiperLogLine(Content As String, Task As LoaderTask(Of Integer, Integer))
-
-        '检查报错
-        Dim ContentTest As String = Content.ToLower
-        Dim ErrorMessage As String = Nothing
-        Dim ContentJson As JObject = Nothing
-        If Content.StartsWith("{""") AndAlso Content.EndsWith("}") Then
-            ContentJson = GetJson(Content)
-            ContentTest = If(ContentJson("error"), ContentJson("msg")).ToString.ToLower
-            ErrorMessage = ContentJson("error")
-        End If
-        If ContentTest.Contains("hiper certificate for this host is expired") Then
-            PossibleFailReason = "$你的索引码已过期,请更换新的索引码!"
-        ElseIf ContentTest.Contains("error creating interface: access is denied") Then
-            PossibleFailReason = "$没有获得管理员权限,无法创建网络通道!"
-        ElseIf ContentTest.Contains("cannot create a file when that file already exists") Then
-            PossibleFailReason = "$请不要重复开启多个联机模块!"
-        ElseIf Content.Contains("failed to load config") Then
-            PossibleFailReason = "$索引码文件内容存在错误!"
-        ElseIf Content.Contains("system cannot find the file specified") Then
-            PossibleFailReason = "$创建网络通道失败!" & vbCrLf & "如果你曾启动过 VPN 软件,请先打开那个软件,然后关掉它,最后再重试。虽然不知道为啥,但这样大概管用……"
-        End If
-        If PossibleFailReason Is Nothing AndAlso ErrorMessage IsNot Nothing Then PossibleFailReason = ErrorMessage
-
-        '检查结束
-        If ContentJson IsNot Nothing Then
-            Dim Message As String = ContentJson("msg")
-            If Message = "HiPer interface is active" Then
-                HiperIp = ContentJson("network").ToString.Split("/").First
-                If IsServerSide Then HostIp = HiperIp
-                Log("[Hiper] Hiper 启动完成,IP:" & HiperIp)
-                HiperState = LoadState.Finished
-            ElseIf Message = "Validity of client certificate" Then
-                Dim VaildTime As String = ContentJson("valid")
-                Date.TryParseExact(VaildTime, "yyyy-MM-dd HH:mm:ss", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.None, HiperCertTime)
-                Log("[Hiper] 索引码到期时间:" & HiperCertTime.ToString)
-                Setup.Set("LinkHiperCertTime", HiperCertTime.ToString)
-            End If
-        End If
-
-        '写入日志
-        If ModeDebug OrElse Content.Contains("""error""") Then Log("[Hiper] " & Content)
-
     End Sub
     End Sub
     Private Shared PossibleFailReason As String = Nothing
     Private Shared PossibleFailReason As String = Nothing
 
 
@@ -643,42 +208,6 @@ FinishMcbFileCheck:
     '每 15 秒执行的 Timer
     '每 15 秒执行的 Timer
     Private Shared HostPing As Integer = -1
     Private Shared HostPing As Integer = -1
     Private Sub WatcherTimer15()
     Private Sub WatcherTimer15()
-        If Not (HiperState = LoadState.Finished OrElse HiperState = LoadState.Loading) Then Exit Sub
-        '检查 HiPer 崩溃
-        Try
-            Process.GetProcessById(HiperProcessId)
-        Catch
-            Dim LogLines = ReadFile(PathHiper & "logs\hiper.log").TrimEnd(vbLf).Split(vbLf)
-            HiperExit(False)
-            MyMsgBox("由于联机模块异常退出,已退出联机房间。" & vbCrLf & vbCrLf &
-                     "联机模块最后的日志:" & vbCrLf & Join(LogLines.Skip(LogLines.Count - 3).ToList, vbCrLf), "联机断开")
-        End Try
-        '下面的部分需要完全完成加载
-        If HiperState <> LoadState.Finished Then Exit Sub
-        '检查 MCB 崩溃
-        Try
-            If Not IsServerSide Then Process.GetProcessById(McbProcessId)
-        Catch
-            HiperExit(False)
-            MyMsgBox("由于 MCB 联机模块异常退出,已退出联机房间。", "联机断开")
-        End Try
-        '重新检查 Ping
-        If Not IsServerSide Then
-            Dim PingHostFailedCount As Integer = 0
-            Do
-                TaskPingHost.WaitForExit(True, IsForceRestart:=True)
-                If TaskPingHost.Output = -1 Then
-                    PingHostFailedCount += 1
-                    Log("[HiPer] Ping 房主失败(第 " & PingHostFailedCount & " 次)")
-                    If PingHostFailedCount >= 3 Then
-                        HiperExit(False) : MyMsgBox("与房主断开连接,已退出联机房间。", "联机断开") : Exit Sub
-                    End If
-                Else
-                    If PingHostFailedCount > 0 Then Log("[HiPer] Ping 房主已恢复成功(" & HostPing & "ms)")
-                    Exit Do
-                End If
-            Loop While True
-        End If
     End Sub
     End Sub
 
 
 #End Region
 #End Region
@@ -711,14 +240,6 @@ FinishMcbFileCheck:
 
 
     '创建房间
     '创建房间
     Private Sub BtnSelectCreate_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectCreate.MouseLeftButtonUp
     Private Sub BtnSelectCreate_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectCreate.MouseLeftButtonUp
-        ''获取端口号
-        'Dim PortInput As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {
-        '                                           New ValidateInteger(2, 65535),
-        '                                           New ValidateExceptSame({"55555", "55557"}, "端口不能为 %!")
-        '                                       }, "在 MC 的暂停画面选择【对局域网开放】", "输入端口号", "确定", "取消")
-        'If PortInput Is Nothing Then Exit Sub
-        ''开始
-        'RoomCreate(Val(PortInput))
     End Sub
     End Sub
     Private Sub RoomCreate(Port As Integer)
     Private Sub RoomCreate(Port As Integer)
         '记录信息
         '记录信息
@@ -730,34 +251,6 @@ FinishMcbFileCheck:
 
 
     '加入房间
     '加入房间
     Private Sub BtnSelectJoin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectJoin.MouseLeftButtonUp
     Private Sub BtnSelectJoin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectJoin.MouseLeftButtonUp
-        '        '获取信息
-        '        Dim Code As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {New ValidateLength(8, 99)}, "",
-        '                        "输入联机码", "确定", "取消")
-        '        If Code Is Nothing Then Exit Sub
-        '        '记录信息
-        '        If Not Code.EndsWith(RequestVersion) OrElse Not Code.StartsWith("P") Then GoTo WrongCode
-        '        Dim Ip As String, Port As Integer
-        '        Try
-        '            Dim IpAndPort As Long = RadixConvert(Code.Substring(1, Code.Length - 2).ToUpper, 36, 10)
-        '            'IpAndPort = HostPort + IpParts(0) * 65536 + IpParts(1) * 65536 * 256 + IpParts(2) * 65536 * 256 * 256 + IpParts(3) * 65536 * 256 * 256 * 256
-        '            Ip = Math.Floor(IpAndPort / (65536L * 256 * 256 * 256))
-        '            IpAndPort = IpAndPort Mod (65536L * 256 * 256 * 256)
-        '            Ip = Math.Floor(IpAndPort / (65536L * 256 * 256)) & "." & Ip
-        '            IpAndPort = IpAndPort Mod (65536L * 256 * 256)
-        '            Ip = Math.Floor(IpAndPort / (65536L * 256)) & "." & Ip
-        '            IpAndPort = IpAndPort Mod (65536L * 256)
-        '            Ip = Math.Floor(IpAndPort / (65536L)) & "." & Ip
-        '            Port = IpAndPort Mod 65536
-        '        Catch
-        '            GoTo WrongCode
-        '        End Try
-        '        '启动
-        '        RoomJoin(Ip, Port)
-        '        Exit Sub
-        'WrongCode:
-        '        If Not Code.StartsWith("P") AndAlso Code.Length >= 49 Then Hint("你输入的可能是 IOI 的联机码,请在左侧的联机方式中选择 IOI!", HintType.Critical) : Exit Sub
-        '        If Code.StartsWith("P") AndAlso Not Code.EndsWith(RequestVersion) Then Hint("你的 PCL 版本与房主的 PCL 版本不一致!", HintType.Critical) : Exit Sub
-        '        Hint("你输入的联机码无效!", HintType.Critical)
     End Sub
     End Sub
     Private Sub RoomJoin(Ip As String, Port As Integer)
     Private Sub RoomJoin(Ip As String, Port As Integer)
         '记录信息
         '记录信息
@@ -773,54 +266,6 @@ FinishMcbFileCheck:
 
 
     '承接状态切换的 UI 改变
     '承接状态切换的 UI 改变
     Private Sub OnLoadStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState)
     Private Sub OnLoadStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState)
-        Select Case NewState
-            Case LoadState.Loading
-                UpdateProgress(0)
-                If IsServerSide Then
-                    LabLoadTitle.Text = "正在创建联机房间"
-                    Log("[Hiper] 正在创建联机房间,端口 " & HostPort)
-                Else
-                    LabLoadTitle.Text = "正在加入联机房间"
-                    Log("[Hiper] 正在加入联机房间,目标 IP " & HostIp & ":" & HostPort)
-                End If
-                LabLoadDesc.Text = "正在初始化……"
-                LoadStep = "准备初始化"
-            Case LoadState.Failed
-                UpdateProgress(1)
-                If IsServerSide Then
-                    LabLoadTitle.Text = "创建联机房间失败"
-                Else
-                    LabLoadTitle.Text = "加入联机房间失败"
-                End If
-                Dim RealException As Exception = If(Loader.Error.InnerException, Loader.Error)
-                If TypeOf RealException Is CertOutdatedException Then
-                    LabCertTitle.Text = "索引码无效"
-                    LabCertDesc.Text = "你的 HiPer 索引码无效或者已经过期!" & vbCrLf & "请重新输入索引码。"
-                    HiperExit(True)
-                ElseIf RealException.Message.StartsWith("$") Then
-                    LabLoadDesc.Text = RealException.Message.TrimStart("$") & vbCrLf &
-                                       "点击镐子重试,或者点击灰色的 × 取消。"
-                Else
-                    LabLoadDesc.Text = LoadStep & "失败:" & GetExceptionSummary(RealException) & vbCrLf &
-                                       "点击镐子重试,或者点击灰色的 × 取消。"
-                End If
-                Log(Loader.Error, "HiPer 联机尝试失败")
-            Case LoadState.Finished
-                UpdateProgress(1)
-                CurrentSubpage = Subpages.PanFinish
-                BtnFinishPing.Visibility = If(IsServerSide, Visibility.Collapsed, Visibility.Visible)
-                LineFinishPing.Visibility = If(IsServerSide, Visibility.Collapsed, Visibility.Visible)
-                BtnFinishCopy.Visibility = If(IsServerSide, Visibility.Visible, Visibility.Collapsed)
-                If IsServerSide Then
-                    LabFinishTitle.Text = "已创建联机房间"
-                    LabFinishDesc.Text = "已在端口 " & HostPort & " 创建了联机房间。" & vbCrLf & "点击下方的复制联机码按钮,然后把联机码发给朋友吧!"
-                Else
-                    LabFinishTitle.Text = "已加入联机房间"
-                    LabFinishDesc.Text = "启动游戏,进入多人游戏页面后稍等片刻,房间就会出现在服务器列表的最下方。" & vbCrLf & "如果提示无效会话,不要退出联机房间,重新启动游戏即可!"
-                End If
-                LabFinishIp.Text = HostIp & ":" & HostPort
-                Log("[Hiper] 已完成连接")
-        End Select
     End Sub
     End Sub
     Private Shared LoadStep As String = "准备初始化"
     Private Shared LoadStep As String = "准备初始化"
     Private Shared Sub SetLoadDesc(Intro As String, [Step] As String)
     Private Shared Sub SetLoadDesc(Intro As String, [Step] As String)
@@ -888,10 +333,6 @@ FinishMcbFileCheck:
 
 
     '复制联机码
     '复制联机码
     Private Sub BtnFinishCopy_Click(sender As Object, e As EventArgs) Handles BtnFinishCopy.Click
     Private Sub BtnFinishCopy_Click(sender As Object, e As EventArgs) Handles BtnFinishCopy.Click
-        Dim IpParts = HostIp.Split(".").Select(Function(l) Val(l)).ToList
-        Dim IpAndPort As Long = HostPort + IpParts(0) * 65536 + IpParts(1) * 65536 * 256 + IpParts(2) * 65536 * 256 * 256 + IpParts(3) * 65536 * 256 * 256 * 256
-        ClipboardSet("P" & RadixConvert(IpAndPort, 10, 36) & RequestVersion, False)
-        Hint("已复制联机码!", HintType.Finish)
     End Sub
     End Sub
 
 
     'Ping 房主
     'Ping 房主

+ 3 - 757
Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb

@@ -1,204 +1,10 @@
-Imports System.Net
-Imports System.Net.Sockets
+Imports System.Net.Sockets
 
 
 Public Class PageLinkIoi
 Public Class PageLinkIoi
     Public Const RequestVersion As Integer = 4
     Public Const RequestVersion As Integer = 4
     Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion
     Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion
     Public Shared PathIoi As String = PathAppdata & "联机模块\IOI 联机模块.exe"
     Public Shared PathIoi As String = PathAppdata & "联机模块\IOI 联机模块.exe"
 
 
-#Region "初始化"
-
-    '页面初始化
-    Private IsLoad As Boolean = False
-    Private Sub MeLoaded() Handles Me.Loaded
-        FormMain.EndProgramForce(Result.Aborted)
-
-        '重复加载部分
-        PanBack.ScrollToHome()
-        RefreshUi()
-
-        '非重复加载部分
-        If IsLoad Then Exit Sub
-        IsLoad = True
-
-        '更新线程
-        RunInNewThread(Sub()
-                           Do While True
-                               Thread.Sleep(200)
-                               If FrmMain.PageCurrent.Page = FormMain.PageType.Link AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.LinkIoi Then RunInUiWait(Sub() FrmLinkIoi.RefreshUi())
-                               RefreshWorker()
-                           Loop
-                       End Sub, "Link Timer")
-
-    End Sub
-
-    '加载器初始化与左边栏处理
-    Private Sub LoaderInit() Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanBack, PanAlways, InitLoader, Sub()
-                                                                      End Sub)
-    End Sub
-
-    '初始化加载器与步骤
-    Public Shared WithEvents InitLoader As New LoaderCombo(Of Integer)("联机模块初始化", {
-        New LoaderTask(Of Integer, Integer)("端口检查", AddressOf InitPortCheck) With {.ProgressWeight = 1},
-        New LoaderTask(Of Integer, Integer)("启动请求核心", AddressOf StartSocketListener) With {.ProgressWeight = 1},
-        New LoaderTask(Of Integer, List(Of NetFile))("初次启动尝试", AddressOf InitFirst) With {.ProgressWeight = 4},
-        New LoaderDownload("下载更新文件", New List(Of NetFile)) With {.ProgressWeight = 6},
-        New LoaderTask(Of List(Of NetFile), Boolean)("二次启动尝试", AddressOf InitSecond) With {.ProgressWeight = 3},
-        New LoaderTask(Of Boolean, Boolean)("创建请求核心房间", AddressOf InitRequest) With {.ProgressWeight = 2}
-    })
-    '判断端口是否被占用
-    Private Shared Sub InitPortCheck()
-        '检查协议
-        If Not Setup.Get("LinkEula") Then
-Reopen:
-            Select Case MyMsgBox("PCL 的联机服务由速聚授权提供。" & vbCrLf & "在使用前,你需要同意速聚的用户服务协议和隐私政策。", "协议授权", "同意", "拒绝", "查看用户服务协议和隐私政策")
-                Case 1
-                    Setup.Set("LinkEula", True)
-                Case 2
-                    Throw New Exception("你拒绝了用户服务协议……")
-                Case 3
-                    OpenWebsite("https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxMTMyODk3Mg==&action=getalbum&album_id=2585385685407514625&scene=173&from_msgid=2247483720&from_itemidx=1&count=3&nolastread=1#wechat_redirect")
-                    GoTo Reopen
-            End Select
-        End If
-        '先掐死
-        IoiStop(True)
-        '检查端口
-        Dim HasConflict As Boolean = False
-        For Each Port In Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
-            If Port.Port <> 55555 AndAlso Port.Port <> 55557 Then Continue For
-            Log("[IOI] 发现端口 " & Port.Port & " 被占用")
-            HasConflict = True
-        Next
-        If Not HasConflict Then Exit Sub
-        '对应的报错
-        For Each Line As String In ShellAndGetOutput("netstat", "-ano", 30000).Split({vbCrLf}, StringSplitOptions.RemoveEmptyEntries)
-            If Not (Line.Contains("127.0.0.1:55555") OrElse Line.Contains("127.0.0.1:55557")) Then Continue For
-            Dim ProcessName As String
-            Try
-                ProcessName = Process.GetProcessById(Line.Split(" ").Last).ProcessName
-            Catch ex As Exception
-                Log(ex, "获取占用端口的进程信息失败,假定进程已结束")
-                Continue For
-            End Try
-            If ProcessName = "联机模块" Then
-                Throw New Exception("由于一个已知问题,请在重启电脑后再尝试使用联机功能")
-            ElseIf ProcessName = "Idle" Then
-                '不知道为啥反正又没占用了
-                Log("[IOI] 未发现占用此端口的程序,继续执行")
-            Else
-                Throw New Exception("端口被程序 " & ProcessName & " 占用,无法启动联机模块,请在任务管理器关闭此程序后再试")
-            End If
-        Next
-    End Sub
-    '下载更新前尝试启动
-    Private Shared Sub InitFirst(Task As LoaderTask(Of Integer, List(Of NetFile)))
-        '初次启动尝试
-        If IoiVersion <> Setup.Get("LinkIoiVersion") Then
-            Log("[IOI] 设置版本强制要求联机模块更新")
-            Setup.Set("LinkIoiVersion", IoiVersion)
-        ElseIf File.Exists(PathIoi) Then
-            Task.Progress = 0.2
-            CheckFirewall()
-            If IoiStart() Then
-                '已完成启动与初始化
-                Task.Output = New List(Of NetFile)
-                Exit Sub
-            Else
-                '需要更新
-                IoiStop(True)
-                File.Delete(PathIoi)
-                GoTo StartDownload
-            End If
-            '如果抛出异常则直接使加载器失败
-        End If
-        '开始下载
-StartDownload:
-        Task.Progress = 0.8
-        Log("[IOI] 需要下载联机模块")
-        If File.Exists(PathTemp & "联机模块.zip") Then File.Delete(PathTemp & "联机模块.zip")
-        Task.Output = New List(Of NetFile) From {New NetFile(
-            {"https://gitcode.net/to/cato_bin/-/raw/master/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
-             "http://mirror.hiper.cn.s2.the.bb/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
-             "http://mirror.hiper.cn.s3.the.bb:175/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
-             "https://pcl2-server-1253424809.file.myqcloud.com/link/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip{CDN}"},
-            PathTemp & "联机模块.zip")}
-    End Sub
-    '下载更新后尝试启动
-    Private Shared Sub InitSecond(Task As LoaderTask(Of List(Of NetFile), Boolean))
-        '若首次尝试已经成功,则直接跳过
-        If IoiState = LoadState.Finished Then Exit Sub
-        '解压更新包
-        Log("[IOI] 解压联机模块以完成下载")
-        If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe")
-        If File.Exists(PathIoi) Then File.Delete(PathIoi)
-        ExtractFile(PathTemp & "联机模块.zip", PathTemp)
-        File.Delete(PathTemp & "联机模块.zip")
-        CopyFile(PathTemp & "ioi.exe", PathIoi)
-        File.Delete(PathTemp & "ioi.exe")
-        Task.Progress = 0.4
-        '再次尝试启动
-        CheckFirewall()
-        If IoiStart() Then Exit Sub
-        IoiStop(True)
-        Throw New Exception("联机模块初始化失败")
-    End Sub
-    '在 IOI 启动后建立 55557 房间
-    Private Shared Sub InitRequest()
-        Try
-            Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=55557" & "&password=" & IoiPassword, "PUT", "", "", 100000))
-            If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
-        Catch ex As InvalidOperationException
-            'API 返回的错误
-            Log("创建请求核心房间失败:" & ex.Message, LogLevel.Msgbox)
-        Catch ex As Exception
-            '常规错误
-            Log(ex, "创建请求核心房间失败", LogLevel.Msgbox)
-        End Try
-    End Sub
-
-    '检查防火墙权限,并添加对应的权限
-    Private Shared Sub CheckFirewall()
-        Try
-            Log("[IOI] Windows 防火墙:检测开始")
-            If Not PageLinkLeft.FirewallIsBlock(PathIoi) Then Exit Try
-            If PageLinkLeft.FirewallPolicy.CurrentProfile.ExceptionsNotAllowed Then
-                '禁止白名单
-                MyMsgBox("由于 Windows 防火墙阻止了所有传入连接,PCL 无法获取防火墙通行权限。" & vbCrLf &
-                         "联机会有很大概率失败,就算连上了,延迟也会变高……" & vbCrLf & vbCrLf &
-                         "请先关闭 Windows 防火墙中的 " & vbLQ & "阻止所有传入连接" & vbRQ & " 选项,然后重启 PCL。", "遭到防火墙拦截")
-            ElseIf IsAdmin() Then
-                '有管理员权限
-                Log("[IOI] Windows 防火墙:尝试添加防火墙通行权限")
-                Try
-                    PageLinkLeft.FirewallAddAuthorized("Plain Craft Launcher 启动器(IOI 联机模块)", PathIoi)
-                Catch ex As Exception
-                    Log(ex, "无法将联机模块添加至防火墙白名单,可能导致联机失败", LogLevel.Msgbox)
-                End Try
-            Else
-                '无管理员权限
-                If MyMsgBox("由于你开启了 Windows 防火墙,PCL 需要获取防火墙通行权限。" & vbCrLf & vbCrLf &
-                            "若继续,PCL 将尝试以管理员权限重新启动。" & vbCrLf &
-                            "若拒绝,联机模块可能会被防火墙拦截,联机会有很大概率失败。", "需要管理员权限", "继续", "拒绝") = 1 Then
-                    Log("[IOI] Windows 防火墙:尝试提升权限")
-                    If RunAsAdmin("--link ioi") Then
-                        FrmMain.EndProgram(False) '已重新运行
-                    Else
-                        Hint("获取管理员权限失败,请尝试右键 PCL,选择 " & vbLQ & "以管理员身份运行" & vbRQ & ",然后再进入联机页面!", HintType.Critical)
-                    End If
-                Else
-                    Hint("在没有防火墙权限的情况下尝试联机,很可能会导致联机失败!", HintType.Critical)
-                End If
-            End If
-            Log("[IOI] Windows 防火墙:无防火墙通行权限")
-        Catch ex As Exception
-            Log(ex, "无法检测防火墙状态,可能导致联机失败", LogLevel.Msgbox)
-        End Try
-    End Sub
-
-#End Region
-
 #Region "进程管理"
 #Region "进程管理"
 
 
     Private Shared IoiId As String, IoiPassword As String
     Private Shared IoiId As String, IoiPassword As String
@@ -209,38 +15,7 @@ StartDownload:
     ''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。
     ''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。
     ''' </summary>
     ''' </summary>
     Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean
     Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean
-        IoiStop = False
-        '发送断开信息
-        Try
-            If IoiProcess IsNot Nothing AndAlso Not IoiProcess.HasExited Then
-                For i = 0 To UserList.Count - 1
-                    If i > UserList.Count - 1 Then Exit For
-                    Dim User = UserList.Values(i)
-                    SendDisconnectRequest(User)
-                Next
-            End If
-        Catch ex As Exception
-            Log(ex, "结束 IOI 进程时发送 Disconnect 信息失败")
-        End Try
-        '关闭所有 Ioi 进程
-        For Each ProcessObject In Process.GetProcesses
-            If ProcessObject.ProcessName <> "IOI 联机模块" Then Continue For
-            IoiStop = True
-            Try
-                ProcessObject.Kill()
-                Log("[IOI] 已关闭联机模块:" & ProcessObject.Id)
-                If SleepWhenKilled Then Thread.Sleep(3000) '等待 3 秒确认进程已退出
-            Catch ex As Exception
-                Log(ex, "关闭联机模块失败(" & ProcessObject.Id & ")")
-            End Try
-        Next
-        '初始化
-        IoiProcess = Nothing
-        IoiId = Nothing
-        IoiPassword = Nothing
-        IoiState = LoadState.Waiting
-        UserList = New Dictionary(Of String, LinkUserIoi)
-        RoomListForMe = New List(Of RoomEntry)
+        Return False
     End Function
     End Function
     ''' <summary>
     ''' <summary>
     ''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。
     ''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。
@@ -248,130 +23,11 @@ StartDownload:
     ''' 若 Ioi 正在运行,则会先停止其运行。
     ''' 若 Ioi 正在运行,则会先停止其运行。
     ''' </summary>
     ''' </summary>
     Public Shared Function IoiStart() As Boolean
     Public Shared Function IoiStart() As Boolean
-        IoiStop(True)
-        Log("[IOI] 启动联机模块进程")
-        Dim Info = New ProcessStartInfo With {
-            .FileName = PathIoi,
-            .UseShellExecute = False,
-            .CreateNoWindow = True,
-            .RedirectStandardError = True,
-            .RedirectStandardOutput = True,
-            .WorkingDirectory = PathTemp
-        }
-        IoiProcess = New Process() With {.StartInfo = Info}
-        Using outputWaitHandle As New AutoResetEvent(False)
-            Using errorWaitHandle As New AutoResetEvent(False)
-                AddHandler IoiProcess.OutputDataReceived, Function(sender, e)
-                                                              Try
-                                                                  If e.Data Is Nothing Then
-                                                                      outputWaitHandle.[Set]()
-                                                                  Else
-                                                                      IoiLogLine(e.Data)
-                                                                  End If
-                                                              Catch ex As ObjectDisposedException
-                                                              Catch ex As Exception
-                                                                  Log(ex, "读取联机模块信息失败")
-                                                                  IoiState = LoadState.Failed
-                                                              End Try
-                                                              Return Nothing
-                                                          End Function
-                AddHandler IoiProcess.ErrorDataReceived, Function(sender, e)
-                                                             Try
-                                                                 If e.Data Is Nothing Then
-                                                                     errorWaitHandle.[Set]()
-                                                                 Else
-                                                                     IoiLogLine(e.Data)
-                                                                 End If
-                                                             Catch ex As ObjectDisposedException
-                                                             Catch ex As Exception
-                                                                 Log(ex, "读取联机模块错误信息失败")
-                                                                 IoiState = LoadState.Failed
-                                                             End Try
-                                                             Return Nothing
-                                                         End Function
-                IoiProcess.Start()
-                IoiState = LoadState.Loading
-                IoiProcess.BeginOutputReadLine()
-                IoiProcess.BeginErrorReadLine()
-                '等待
-                Do Until IoiProcess.HasExited OrElse IoiState <> LoadState.Loading
-                    Thread.Sleep(10)
-                Loop
-                '输出
-                If IoiState = LoadState.Finished Then
-                    Log("[IOI] 联机模块启动成功")
-                    Return True
-                Else
-                    Throw New Exception("联机模块启动失败")
-                End If
-                'Select Case IoiState
-                '    Case LoadState.Finished
-                '        Log("[IOI] 联机模块启动成功")
-                '        Return True
-                '    Case LoadState.Aborted
-                '        Log("[IOI] 联机模块要求更新")
-                '        Return False
-                '    Case LoadState.Failed
-                '        Log("[IOI] 联机模块启动出现异常")
-                '        Return False
-                '    Case Else 'LoadState.Loading
-                '        Throw New Exception("联机模块启动失败,请检查你的网络连接")
-                'End Select
-            End Using
-        End Using
+        Return False
     End Function
     End Function
 
 
     'Ioi 日志
     'Ioi 日志
     Private Shared Sub IoiLogLine(Content As String)
     Private Shared Sub IoiLogLine(Content As String)
-
-        '初始化
-        If Content.Contains(" > http://127.0.0.1:55555/") Then Exit Sub
-        If Content.Contains("All done") Then
-            If IoiId IsNot Nothing AndAlso IoiPassword IsNot Nothing Then
-                IoiState = LoadState.Finished
-            Else
-                Log("[IOI] 联机模块汇报初始化完成,但未提供账户信息")
-                IoiState = LoadState.Failed
-            End If
-            Exit Sub
-        End If
-        If Content.Contains("Password :: ") Then
-            IoiPassword = Content.Split({"Password :: "}, StringSplitOptions.None)(1)
-            Exit Sub '为保证安全不记录密码到 Log
-        End If
-
-        '初始化
-        If Content.Contains("ID :: ") Then IoiId = Content.Split({"ID :: "}, StringSplitOptions.None)(1)
-        If Content.Contains("Initialization failed") OrElse Content.Contains("The version is ") Then IoiState = LoadState.Aborted
-
-        '系统回应
-        If Content.Contains("'portssub' from ") Then LastPortsId = Content.Split(" ").Last
-        If Content.Contains("Listening tcp ") Then
-            If UserList.ContainsKey(LastPortsId) Then
-                UserList(LastPortsId).Ports(Content.Split(" ").Last) = RegexSeek(Content, "(?<=Listening tcp )[^:]+")
-            Else
-                Log("[IOI] 未在列表中的用户出现意料外的连接信息")
-                NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & LastPortsId & "&password=" & IoiPassword, "DELETE", "", "", 500)
-            End If
-        End If
-
-        '断开连接
-        If Content.Contains("Closed tcp connection from ") AndAlso Content.Contains(":55557") Then
-            For i = 0 To UserList.Count - 1
-                Dim User = UserList.Values(i)
-                If Not Content.Contains(User.Ports(55557)) OrElse User.IsDisposed Then Continue For
-                Log("[IOI] 检测到 55557 端口断开(" & User.DisplayName & ")")
-                UserRemove(User, True)
-                Exit For
-            Next
-        End If
-
-        '写入日志
-        If Not Content.Contains("read /dev/stdin: The handle is invalid.") AndAlso LogLinesCount < If(ModeDebug, 10000, 1000) Then
-            LogLinesCount += 1
-            Log("[IOI] " & Content)
-        End If
-
     End Sub
     End Sub
     Private Shared LogLinesCount As Integer = 0
     Private Shared LogLinesCount As Integer = 0
     Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口
     Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口
@@ -384,94 +40,10 @@ StartDownload:
     Private Shared UserListIdentifyCache As String = ""
     Private Shared UserListIdentifyCache As String = ""
     Private Shared RoomListIdentifyCache As String = ""
     Private Shared RoomListIdentifyCache As String = ""
     Public Sub RefreshUi()
     Public Sub RefreshUi()
-        Try
-            '确认用户列表缓存
-            Dim UserListIdentify As String = Join(UserList, vbCrLf)
-            If UserListIdentify <> UserListIdentifyCache Then
-                UserListIdentifyCache = UserListIdentify
-                '刷新列表项
-                PanUserList.Children.Clear()
-                For i = 0 To UserList.Count - 1
-                    If i > UserList.Count - 1 Then Exit For
-                    PanUserList.Children.Add(UserList.Values(i).ToListItem)
-                Next
-                '刷新列表标题
-                CardUser.Title = "已连接的玩家 (" & PanUserList.Children.Count & ")"
-            End If
-            '刷新用户列表项
-            For Each UserItem As MyListItem In PanUserList.Children
-                CType(UserItem.Tag, LinkUserIoi).RefreshUi(UserItem)
-            Next
-            '确认房间列表缓存
-            Dim RoomListIdentify As String = Join(GetRoomList(), vbCrLf)
-            If RoomListIdentify <> RoomListIdentifyCache Then
-                RoomListIdentifyCache = RoomListIdentify
-                '刷新列表项
-                PanRoom.Children.Clear()
-                For Each Room In GetRoomList()
-                    PanRoom.Children.Add(Room.ToListItem)
-                Next
-            End If
-            '刷新房间列表项
-            For Each RoomItem As MyListItem In PanRoom.Children
-                CType(RoomItem.Tag, RoomEntry).RefreshUi(RoomItem)
-            Next
-            '刷新操作提示
-            If UserList.Count = 0 Then
-                If RoomListForMe.Count = 0 Then
-                    LabHint.Text = "若想创建房间,请点击创建房间按钮,并按说明进行操作。" & vbCrLf & "若想加入他人的房间,请点击建立连接,然后输入对方的联机码。"
-                Else
-                    LabHint.Text = "若想让其他人加入你的房间,请点击复制联机码,然后让你的朋友输入你的联机码以建立连接。"
-                End If
-            Else
-                If GetRoomList.Count = 0 Then
-                    LabHint.Text = "若想创建房间,请点击创建房间按钮,然后输入对局域网开放后 MC 显示的端口号,或本地服务端的端口号。"
-                ElseIf RoomListForMe.Count = 0 Then
-                    '已有连接,本人没有房间,对方有房间
-                    LabHint.Text = "若想加入某个房间,直接点击该房间即可获取说明。"
-                Else
-                    '已有连接,且本人有房间
-                    LabHint.Text = "指向你所创建的房间,能在右侧找到修改房间名称、关闭房间等选项。" &
-                                   If(RoomListForMe.Count <> GetRoomList.Count, vbCrLf & "若想加入其他人的房间,直接点击该房间即可获取说明。", "")
-                End If
-            End If
-        Catch ex As Exception
-            Log(ex, "联机模块 UI 时钟运行失败", LogLevel.Feedback)
-        End Try
     End Sub
     End Sub
 
 
     '工作线程刷新
     '工作线程刷新
     Public Sub RefreshWorker()
     Public Sub RefreshWorker()
-        Try
-            '检测退出
-            If InitLoader.State = LoadState.Finished AndAlso IoiProcess.HasExited Then
-                Log("[IOI] 联机模块出现异常!", LogLevel.Hint)
-                ModuleStopManually()
-            End If
-            '检测用户
-            For i = 0 To UserList.Values.Count - 1
-                If i > UserList.Values.Count - 1 Then Exit For
-                Dim User = UserList.Values(i)
-                If User.Progress < 1 Then Continue For '跳过连接中的用户
-                '发送心跳包
-                If Date.Now - User.LastSend > New TimeSpan(0, 0, RandomInteger(50, 30)) Then
-                    RunInNewThread(Sub()
-                                       Try
-                                           SendUpdateRequest(User, 1)
-                                       Catch ex As Exception
-                                           Log(ex, "心跳包发送失败(" & User.DisplayName & ")", LogLevel.Normal)
-                                       End Try
-                                   End Sub, "Link Heartbeat " & User.Id)
-                End If
-                '检测被动离线
-                If Date.Now - User.LastReceive > New TimeSpan(0, 2, 0) Then
-                    SendDisconnectRequest(User)
-                    Hint("与 " & User.DisplayName & " 的连接已中断!", HintType.Critical)
-                End If
-            Next
-        Catch ex As Exception
-            Log(ex, "联机模块工作时钟运行失败", LogLevel.Feedback)
-        End Try
     End Sub
     End Sub
 
 
 #End Region
 #End Region
@@ -482,65 +54,6 @@ StartDownload:
     ''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。
     ''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。
     ''' </summary>
     ''' </summary>
     Private Shared Sub SendPortsubRequest(User As LinkUserIoi)
     Private Shared Sub SendPortsubRequest(User As LinkUserIoi)
-        Log("[IOI] 尝试建立连接:" & User.Id)
-        '向对方发送 portsub 请求
-        Dim Retry As Integer = 0
-RetryLink:
-        Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "PUT", "", "", 100000))
-        '失败与重试
-        If Result("msg") IsNot Nothing Then
-            Dim ErrorMessage As String = Result("msg").ToString
-            Select Case ErrorMessage
-                Case "failed to find any peer in table"
-                    Retry += 1
-                    ErrorMessage = "我方网络环境不佳,连接失败。" '初始化对等机列表超时
-                    Log("[IOI] 尝试建立连接结果:未找到对等机,第 " & Retry & " 级重试")
-                Case "routing: not found"
-                    Retry += 4
-                    ErrorMessage = "我方或对方网络环境不佳,或对方已关闭联机模块,未找到路由。" '未知情况
-                    Log("[IOI] 尝试建立连接结果:无法连接到路由,第 " & Retry & " 级重试")
-                Case "you are already connected to specified host"
-                    If User.IsDisposed Then Throw New ThreadInterruptedException("用户对象已被释放")
-                    Log("[IOI] 尝试建立连接结果:已与对方连接")
-                    GoTo Done
-                    'Retry += 1
-                    'NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 100000)
-                    'ErrorMessage = "已与对方连接。" '双向连接偶尔导致
-                    'Log("[IOI] 已与对方连接,尝试中断现有连接,第 " & Retry & " 级重试")
-                Case "dial backoff"
-                    Retry += 20
-                    ErrorMessage = "对方网络环境不佳,或对方已关闭联机模块。请尝试让对方主动连接你,而不是你去连接对方。" 'NAT2 连 NAT4 导致
-                    Log("[IOI] 尝试建立连接结果:NAT 异常,第 " & Retry & " 级重试")
-                Case Else
-                    If ErrorMessage.Contains("all dials failed") Then
-                        Retry += 8
-                        ErrorMessage = "我方或对方网络环境不佳,或对方已关闭联机模块,连接失败。" '网络不佳,或对方已关闭模块
-                        Log("[IOI] 尝试建立连接结果:连接失败,第 " & Retry & " 级重试")
-                    Else
-                        Retry += 8
-                        Log("[IOI] 尝试建立连接结果:未知错误(" & ErrorMessage & "),第 " & Retry & " 级重试")
-                    End If
-            End Select
-            If User.IsDisposed Then Throw New ThreadInterruptedException("用户对象已被释放")
-            If Retry <= 64 Then
-                User.Progress = Retry * 0.01 + 0.05
-                Thread.Sleep(3000)
-                GoTo RetryLink
-            Else
-                Throw New InvalidOperationException(ErrorMessage)
-            End If
-        End If
-        Log("[IOI] 尝试建立连接结果:成功")
-        '等待获取对方端口
-Done:
-        Dim WaitCount As Integer = 0
-        Do Until User.Ports.ContainsKey(55555) AndAlso User.Ports.ContainsKey(55557)
-            WaitCount += 1
-            User.Progress = 0.7 + WaitCount / 200 * 0.1 '70% ~ 80%
-            If WaitCount = 100 Then Throw New Exception("连接超时,请尝试重新连接(未收到端口回报)!")
-            Thread.Sleep(150)
-        Loop
-        User.Progress = 0.8
     End Sub
     End Sub
 
 
     ''' <summary>
     ''' <summary>
@@ -584,20 +97,6 @@ Done:
     ''' 尝试发送断开请求,并将其从用户列表中移除。
     ''' 尝试发送断开请求,并将其从用户列表中移除。
     ''' </summary>
     ''' </summary>
     Private Shared Sub SendDisconnectRequest(User As LinkUserIoi, Optional Message As String = Nothing, Optional IsError As Boolean = False)
     Private Shared Sub SendDisconnectRequest(User As LinkUserIoi, Optional Message As String = Nothing, Optional IsError As Boolean = False)
-        Dim RawJson As New JObject()
-        RawJson("id") = IoiId
-        RawJson("type") = "disconnect"
-        If Message IsNot Nothing Then
-            RawJson("message") = Message
-            RawJson("isError") = IsError
-        End If
-        Try
-            User.Send(RawJson)
-            Thread.Sleep(50)
-        Catch
-        End Try
-        NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-        User.Dispose()
     End Sub
     End Sub
 
 
 #End Region
 #End Region
@@ -606,29 +105,9 @@ Done:
 
 
     '刷新连接
     '刷新连接
     Private Shared Sub BtnListRefresh_Click(sender As MyIconButton, e As EventArgs)
     Private Shared Sub BtnListRefresh_Click(sender As MyIconButton, e As EventArgs)
-        Dim User As LinkUserIoi = sender.Tag
-        User.PingRecord.Clear()
-        FrmLinkIoi.RefreshUi()
-        RunInThread(Sub()
-                        Try
-                            SendUpdateRequest(User, 1)
-                        Catch ex As Exception
-                            If InitLoader.State = LoadState.Finished Then Log(ex, "刷新与 " & User.DisplayName & " 的连接失败", LogLevel.Hint)
-                        End Try
-                    End Sub)
     End Sub
     End Sub
     '断开连接
     '断开连接
     Private Shared Sub BtnListDisconnect_Click(sender As MyIconButton, e As EventArgs)
     Private Shared Sub BtnListDisconnect_Click(sender As MyIconButton, e As EventArgs)
-        Dim User As LinkUserIoi = sender.Tag
-        sender.IsEnabled = False
-        RunInThread(Sub()
-                        If User.Progress < 1 AndAlso User.RelativeThread IsNot Nothing AndAlso User.RelativeThread.IsAlive Then
-                            NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-                            User.Dispose()
-                        Else
-                            SendDisconnectRequest(User, GetPlayerName() & " 主动断开了连接!")
-                        End If
-                    End Sub)
     End Sub
     End Sub
     '复制联机码
     '复制联机码
     Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click
     Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click
@@ -680,46 +159,10 @@ Done:
 
 
 #Region "请求核心"
 #Region "请求核心"
 
 
-    Private Shared Listener As Socket = Nothing
     ''' <summary>
     ''' <summary>
     ''' 启动 Socket 监听核心。
     ''' 启动 Socket 监听核心。
     ''' </summary>
     ''' </summary>
     Public Shared Sub StartSocketListener()
     Public Shared Sub StartSocketListener()
-        If Listener IsNot Nothing Then Exit Sub '已经启动过了
-        Listener = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) With {.ReceiveTimeout = 1000000000, .SendTimeout = 1000000000}
-        Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, True)
-        Try
-            Listener.Bind(New IPEndPoint(IPAddress.Any, 55557))
-        Catch ex As Exception
-            Log(ex, "初次启动 Socket 监听核心失败,开始重试")
-            Thread.Sleep(1000)
-            Listener.Bind(New IPEndPoint(IPAddress.Any, 55557))
-        End Try
-        Listener.Listen(100)
-        Log("[IOI] 已启动 Socket 监听核心")
-        RunInNewThread(Sub()
-                           While True
-                               Try
-                                   '获取新的 Socket
-                                   Dim bytes(1024) As Byte
-                                   Dim ClientSocket As Socket = Listener.Accept()
-                                   '获取初始输入信息
-                                   Dim RequestInput As String = Encoding.UTF8.GetString(bytes, 0, ClientSocket.Receive(bytes))
-                                   If Not RequestInput.StartsWith("PCL - ") Then Exit Sub
-                                   Dim RawData As String
-                                   Try
-                                       RawData = SecretDecrypt(WebUtility.UrlDecode(RequestInput.Substring(6)))
-                                       Log("[IOI] 接收到新的 Socket:" & RawData)
-                                       PageLinkIoi.ReceiveJson(GetJson(RawData), ClientSocket)
-                                   Catch ex As Exception
-                                       Log(ex, "新的 Socket 处理出错(内容:" & RequestInput & ")")
-                                   End Try
-                               Catch ex As Exception
-                                   Log(ex, "Socket 监听出错")
-                                   Thread.Sleep(3000) '防止死循环出错
-                               End Try
-                           End While
-                       End Sub, "Socket Listener")
     End Sub
     End Sub
 
 
 #End Region
 #End Region
@@ -738,40 +181,9 @@ Done:
         '请求管理
         '请求管理
         Public Socket As Socket = Nothing
         Public Socket As Socket = Nothing
         Public Sub Send(Request As JObject)
         Public Sub Send(Request As JObject)
-            If Socket Is Nothing Then Throw New Exception("该用户尚未绑定 Socket")
-            Dim Data As String = Request.ToString(Newtonsoft.Json.Formatting.None)
-            If ModeDebug Then Log("[IOI] 发送联机数据包(" & DisplayName & "):" & Data)
-            Data = "PCL - " & WebUtility.UrlEncode(SecretEncrypt(Data))
-            Me.LastSend = Date.Now
-            Socket.Send(Encoding.UTF8.GetBytes(Data))
         End Sub
         End Sub
         Public ListenerThread As Thread = Nothing
         Public ListenerThread As Thread = Nothing
         Public Sub StartListener()
         Public Sub StartListener()
-            If ListenerThread IsNot Nothing Then Exit Sub
-            '启动监听
-            ListenerThread = RunInNewThread(Sub()
-                                                Try
-                                                    Dim bytes(1024) As Byte
-                                                    While PageLinkIoi.UserList.ContainsValue(Me)
-                                                        Dim RequestInput As String = Encoding.UTF8.GetString(bytes, 0, Socket.Receive(bytes))
-                                                        If Not RequestInput.StartsWith("PCL - ") Then Exit Sub
-                                                        Dim RawData As String = SecretDecrypt(WebUtility.UrlDecode(RequestInput.Substring(6)))
-                                                        If ModeDebug Then Log("[IOI] 接收联机数据包(" & DisplayName & "):" & RawData)
-                                                        PageLinkIoi.ReceiveJson(GetJson(RawData)) '调用去实际的联机模块
-                                                    End While
-                                                Catch ex As ThreadInterruptedException
-                                                    Log("[IOI] 用户监听已中断(" & DisplayName & ")")
-                                                Catch ex As Exception
-                                                    If IsDisposed Then Exit Sub
-                                                    If ex.GetType.Equals(GetType(SocketException)) AndAlso CType(ex, SocketException).SocketErrorCode = 10053 Then
-                                                        Log("[IOI] 客户端已关闭(" & DisplayName & ")")
-                                                        PageLinkIoi.UserRemove(CType(Me, PageLinkIoi.LinkUserIoi), True)
-                                                        Exit Sub
-                                                    End If
-                                                    Log(ex, "用户监听出错(" & DisplayName & ")")
-                                                    PageLinkIoi.UserRemove(CType(Me, PageLinkIoi.LinkUserIoi), True)
-                                                End Try
-                                            End Sub, "Link Listener " & DisplayName)
         End Sub
         End Sub
         Public Sub BindSocket(Socket As Socket)
         Public Sub BindSocket(Socket As Socket)
             If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket")
             If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket")
@@ -975,135 +387,12 @@ Done:
 
 
     '正向与反向连接
     '正向与反向连接
     Public Shared Sub BtnLeftCreate_Click() Handles BtnLeftCreate.Click
     Public Shared Sub BtnLeftCreate_Click() Handles BtnLeftCreate.Click
-        ''获取信息
-        'Dim Code As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {New ValidateLength(9, 99999)}, "", "输入对方的联机码", "确定", "取消")
-        'If Code Is Nothing Then Exit Sub
-        ''检查
-        'If Code.StartsWith("P") AndAlso Code.Length < 48 Then
-        '    Hint("你输入的可能是 HiPer 的联机码,请在左侧的联机方式中选择 HiPer!", HintType.Critical) : Exit Sub
-        'End If
-        'Dim Id As String, DisplayName As String
-        'Try '解密失败检查
-        '    Id = "12D3" & Code.Substring(0, 48)
-        '    DisplayName = SecretDecrypt(Code.Substring(48))
-        'Catch
-        '    Hint("你输入的联机码有误!", HintType.Critical)
-        '    Exit Sub
-        'End Try
-        'If Id = IoiId Then '自我连接检查
-        '    Hint("我连我自己?搁这卡 Bug 呢?", HintType.Critical)
-        '    Exit Sub
-        'End If
-        ''开始
-        'Dim User As New LinkUserIoi(Id, DisplayName)
-        'User.RelativeThread = RunInNewThread(Sub()
-        '                                         Dim WaitCount As Integer = 0
-        '                                         Try
-        '                                             '加入列表
-        '                                             If UserList.ContainsKey(Id) Then
-        '                                                 Hint(UserList(Id).DisplayName & " 已在列表中,无需再次连接!")
-        '                                                 Exit Sub
-        '                                             Else
-        '                                                 UserList.Add(Id, User)
-        '                                             End If
-        '                                             '发送 portsub 请求(0% -> 80%)
-        '                                             SendPortsubRequest(User)
-        '                                             '构建 Socket(81% -> 82%)
-        '                                             Dim ClientSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) With {.ReceiveTimeout = 1000000000, .SendTimeout = 1000000000}
-        '                                             ClientSocket.Connect(New IPEndPoint(IPAddress.Parse(User.Ports(55557)), 55557))
-        '                                             User.BindSocket(ClientSocket)
-        '                                             User.Progress = 0.82
-        '                                             '发送 connect 请求(83% -> 85%)
-        '                                             SendConnectRequest(User)
-        '                                             User.Progress = 0.85
-        '                                             '等待对方向自己请求
-        '                                             Log("[IOI] 加入成功,等待反向请求")
-        '                                             Do While User.Progress < 0.9999
-        '                                                 User.Progress += 0.0002
-        '                                                 If User.Progress > 0.98 AndAlso User.Progress < 0.9999 Then Throw New Exception("对方未回应连接请求!")
-        '                                                 Thread.Sleep(100)
-        '                                             Loop
-        '                                             Hint("已连接到 " & User.DisplayName & "!", HintType.Finish)
-        '                                         Catch ex As ThreadInterruptedException
-        '                                             Log("[IOI] 已中断主动发起的连接(" & User.DisplayName & ")")
-        '                                         Catch ex As InvalidOperationException
-        '                                             'API 返回的错误
-        '                                             NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-        '                                             User.Dispose()
-        '                                             If InitLoader.State = LoadState.Finished Then Log("与 " & DisplayName & " 建立连接失败:" & ex.Message, LogLevel.Msgbox)
-        '                                         Catch ex As Exception
-        '                                             '常规错误
-        '                                             NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-        '                                             User.Dispose()
-        '                                             If ex.InnerException IsNot Nothing AndAlso TypeOf ex.InnerException Is ThreadInterruptedException Then Exit Sub
-        '                                             If InitLoader.State = LoadState.Finished Then Log(ex, "与 " & DisplayName & " 建立连接失败", LogLevel.Msgbox)
-        '                                         End Try
-        '                                     End Sub, "Link Create " & DisplayName)
     End Sub
     End Sub
     Private Shared Sub SendPortsubBack(User As LinkUserIoi, TargetVersion As Integer)
     Private Shared Sub SendPortsubBack(User As LinkUserIoi, TargetVersion As Integer)
-        Try
-            SendPortsubRequest(User)
-            User.Progress = 0.9
-            If TargetVersion > RequestVersion Then
-                SendDisconnectRequest(User, "无法连接到 " & GetPlayerName() & ":对方的 PCL 版本过低!", True)
-                Throw New InvalidOperationException("你的 PCL 版本过低!")
-            ElseIf TargetVersion < RequestVersion Then
-                SendDisconnectRequest(User, "无法连接到 " & GetPlayerName() & ":你的 PCL 版本过低!", True)
-                Throw New InvalidOperationException("对方的 PCL 版本过低!")
-            Else
-                SendUpdateRequest(User, 1)
-                User.Progress = 1
-                Hint(User.DisplayName & " 已与你建立连接!")
-            End If
-        Catch ex As ThreadInterruptedException
-            Log("[IOI] 已中断被动建立的连接(" & User.DisplayName & ")")
-        Catch ex As InvalidOperationException
-            'API 返回的错误
-            NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-            User.Dispose()
-            If InitLoader.State = LoadState.Finished Then Log("与 " & User.DisplayName & " 建立连接失败:" & ex.Message, LogLevel.Hint)
-        Catch ex As Exception
-            '常规错误
-            NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-            User.Dispose()
-            If InitLoader.State = LoadState.Finished Then Log(ex, "与 " & User.DisplayName & " 建立连接失败", LogLevel.Hint)
-        End Try
     End Sub
     End Sub
 
 
     '创建房间
     '创建房间
     Private Sub LinkCreate() Handles BtnCreate.Click
     Private Sub LinkCreate() Handles BtnCreate.Click
-        'If MyMsgBox("请先进入 MC 并暂停游戏,在暂停页面选择对局域网开放,然后在下一个窗口输入 MC 显示的端口号。" & vbCrLf & "若使用服务端开服,则直接在下一个窗口输入服务器配置中的端口号即可。", "提示", "继续", "取消") = 2 Then Exit Sub
-        ''获取端口号
-        'Dim Port As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {
-        '                                           New ValidateInteger(0, 65535),
-        '                                           New ValidateExceptSame({"55555", "55557"}, "端口不能为 %!"),
-        '                                           New ValidateExceptSame(RoomListForMe.Select(AddressOf RoomEntry.SelectPort), "端口 % 已创建过房间,请在删除该房间后继续!")
-        '                                       }, "", "输入端口号", "确定", "取消")
-        'If Port Is Nothing Then Exit Sub
-        ''获取显示名称
-        'Dim DisplayName As String = MyMsgBoxInput(GetPlayerName() & " 的房间 - " & Port, New ObjectModel.Collection(Of Validate) From {
-        '                                           New ValidateNullOrWhiteSpace(), New ValidateLength(1, 40), New ValidateFilter()
-        '                                       }, "", "输入房间名(建议包含游戏版本等信息)", "确定", "取消")
-        'If DisplayName Is Nothing Then Exit Sub
-        'DisplayName = DisplayName.Trim
-        ''开始
-        'RunInThread(Sub()
-        '                Try
-        '                    '请求
-        '                    Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=" & Port & "&password=" & IoiPassword, "PUT", "", "", 100000))
-        '                    If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
-        '                    '成功
-        '                    RoomListForMe.Add(New RoomEntry(Port, DisplayName))
-        '                    Hint("房间 " & DisplayName & " 已创建!", HintType.Finish)
-        '                    SendUpdateRequestToAllUsers()
-        '                Catch ex As InvalidOperationException
-        '                    'API 返回的错误
-        '                    Log("创建房间失败:" & ex.Message, LogLevel.Msgbox)
-        '                Catch ex As Exception
-        '                    '常规错误
-        '                    Log(ex, "创建房间失败", LogLevel.Msgbox)
-        '                End Try
-        '            End Sub)
     End Sub
     End Sub
     Private Shared Sub SendUpdateRequestToAllUsers()
     Private Shared Sub SendUpdateRequestToAllUsers()
         For i = 0 To UserList.Count - 1
         For i = 0 To UserList.Count - 1
@@ -1119,18 +408,6 @@ Done:
     End Sub
     End Sub
     '修改房间名称
     '修改房间名称
     Private Shared Sub BtnRoomEdit_Click(sender As MyIconButton, e As EventArgs)
     Private Shared Sub BtnRoomEdit_Click(sender As MyIconButton, e As EventArgs)
-        'Dim Room As RoomEntry = sender.Tag
-        ''获取房间名
-        'Dim DisplayName As String = MyMsgBoxInput(Room.DisplayName, New ObjectModel.Collection(Of Validate) From {
-        '                                           New ValidateNullOrWhiteSpace(),
-        '                                           New ValidateLength(1, 40)
-        '                                       }, "", "输入房间名(建议包含游戏版本等信息)", "确定", "取消")
-        'If DisplayName Is Nothing Then Exit Sub
-        'DisplayName = DisplayName.Trim
-        ''修改
-        'Room.DisplayName = DisplayName
-        'FrmLinkIoi.RefreshUi()
-        'SendUpdateRequestToAllUsers()
     End Sub
     End Sub
     '加入房间
     '加入房间
     Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs)
     Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs)
@@ -1141,25 +418,6 @@ Done:
     End Sub
     End Sub
     '关闭房间
     '关闭房间
     Private Shared Sub BtnRoomClose_Click(sender As MyIconButton, e As EventArgs)
     Private Shared Sub BtnRoomClose_Click(sender As MyIconButton, e As EventArgs)
-        Dim Room As RoomEntry = sender.Tag
-        RunInThread(Sub()
-                        Try
-                            '远程移除
-                            Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=" & Room.Port & "&password=" & IoiPassword, "DELETE", "", "", 100000))
-                            If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
-                            '本地移除
-                            RoomListForMe.Remove(Room)
-                            '成功
-                            RunInUi(Sub() FrmLinkIoi.RefreshUi())
-                            SendUpdateRequestToAllUsers()
-                        Catch ex As InvalidOperationException
-                            'API 返回的错误
-                            If InitLoader.State = LoadState.Finished Then Log("移除房间失败:" & ex.Message, LogLevel.Msgbox)
-                        Catch ex As Exception
-                            '常规错误
-                            If InitLoader.State = LoadState.Finished Then Log(ex, "移除房间失败", LogLevel.Msgbox)
-                        End Try
-                    End Sub)
     End Sub
     End Sub
 
 
     '获取数据包
     '获取数据包
@@ -1238,21 +496,9 @@ Done:
     ''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。
     ''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。
     ''' </summary>
     ''' </summary>
     Public Shared Sub UserRemove(User As LinkUserIoi, ShowLeaveMessage As Boolean)
     Public Shared Sub UserRemove(User As LinkUserIoi, ShowLeaveMessage As Boolean)
-        If Not UserList.ContainsKey(User.Id) Then Exit Sub
-        If ShowLeaveMessage Then Hint(User.DisplayName & " 已离开!")
-        NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
-        User.Dispose()
     End Sub
     End Sub
 
 
-    '关闭联机模块按钮
-    Private Shared Sub ModuleStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState) Handles InitLoader.OnStateChangedUi
-        If FrmLinkLeft IsNot Nothing Then CType(FrmLinkLeft.ItemIoi.Buttons(0), MyIconButton).Visibility = If(NewState = LoadState.Finished, Visibility.Visible, Visibility.Collapsed)
-    End Sub
     Public Shared Sub ModuleStopManually()
     Public Shared Sub ModuleStopManually()
-        IoiStop(False)
-        InitLoader.Error = New Exception("联机模块已关闭,点击以重新启动")
-        InitLoader.State = LoadState.Failed
-        IoiState = LoadState.Failed
     End Sub
     End Sub
 
 
 End Class
 End Class

+ 1 - 82
Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb

@@ -1,6 +1,4 @@
-Imports NetFwTypeLib
-
-Public Class PageLinkLeft
+Public Class PageLinkLeft
 
 
     Private IsLoad As Boolean = False
     Private IsLoad As Boolean = False
     Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次
     Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次
@@ -15,84 +13,6 @@ Public Class PageLinkLeft
         IsPageSwitched = False
         IsPageSwitched = False
     End Sub
     End Sub
 
 
-#Region "Windows 防火墙"
-
-    '为联机模块添加防火墙通行权限的函数库
-    '不加防火墙权限你要我怎么办,这联机根本连都连不上,球球你们别看到防火墙就说是后门了,真的很恼火……
-
-    ''' <summary>
-    ''' 获取某个文件是否被 Windows 防火墙阻止。
-    ''' </summary>
-    Public Shared Function FirewallIsBlock(FilePath As String) As Boolean
-        '获取配置
-        If FirewallPolicy Is Nothing Then
-            Try
-                FirewallPolicy = FirewallPolicyGet()
-            Catch ex As Exception
-                Log(ex, "Windows 防火墙:可能关闭得太死了,根本检测不到,笑死", LogLevel.Normal)
-                Return False
-            End Try
-        End If
-        '检查是否开启
-        If Not FirewallPolicy.CurrentProfile.FirewallEnabled Then
-            Log("[Link] Windows 防火墙:防火墙关闭")
-            Return False
-        End If
-        '检查是否允许例外
-        If FirewallPolicy.CurrentProfile.ExceptionsNotAllowed Then
-            Log("[Link] Windows 防火墙:防火墙开启,不允许例外")
-            Return True
-        End If
-        '检查白名单
-        Dim Target As String = FilePath.Replace(PathTemp.Split("\").First, "").ToLower
-        For Each App As INetFwAuthorizedApplication In FirewallPolicy.CurrentProfile.AuthorizedApplications
-            If ModeDebug Then Log("[Link] 防火墙白名单(" & App.Enabled & "):" & App.ProcessImageFileName)
-            If Not App.Enabled Then Continue For
-            '信标的奇妙盘符 E:0/xxxxx/xxx 导致误判……
-            Dim FileNameTrimmed As String = App.ProcessImageFileName.Replace(App.ProcessImageFileName.Split("\").First, "")
-            If App.ProcessImageFileName.ToLower.Contains(FilePath) Then
-                Log("[Link] Windows 防火墙:开启,已在白名单中(" & FilePath & ")")
-                Return False
-            End If
-        Next
-        Log("[Link] Windows 防火墙:开启,未在白名单中(" & FilePath & ")")
-        Return True
-    End Function
-    ''' <summary>
-    ''' 添加防火墙通行权限。需要管理员权限。
-    ''' </summary>
-    Public Shared Sub FirewallAddAuthorized(DisplayName As String, FilePath As String)
-        Log("[Link] Windows 防火墙:添加通行权限(" & FilePath & ")")
-        '获取配置
-        If FirewallPolicy Is Nothing Then FirewallPolicy = FirewallPolicyGet()
-        '添加项目
-        FirewallPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_DOMAIN).AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
-        FirewallPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD).AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
-        FirewallPolicy.CurrentProfile.AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
-    End Sub
-    Private Class FirewallApp
-        Implements INetFwAuthorizedApplication
-        Public Property Name As String Implements INetFwAuthorizedApplication.Name
-        Public Property ProcessImageFileName As String Implements INetFwAuthorizedApplication.ProcessImageFileName
-        Public Property IpVersion As NET_FW_IP_VERSION_ = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY Implements INetFwAuthorizedApplication.IpVersion
-        Public Property Scope As NET_FW_SCOPE_ = NET_FW_SCOPE_.NET_FW_SCOPE_ALL Implements INetFwAuthorizedApplication.Scope
-        Public Property RemoteAddresses As String = "*" Implements INetFwAuthorizedApplication.RemoteAddresses
-        Public Property Enabled As Boolean = True Implements INetFwAuthorizedApplication.Enabled
-        Public Sub New(DisplayName As String, FilePath As String)
-            Name = DisplayName
-            ProcessImageFileName = FilePath
-        End Sub
-    End Class
-    '获取防火墙配置
-    Public Shared FirewallPolicy As INetFwPolicy = Nothing
-    Public Shared Function FirewallPolicyGet() As INetFwPolicy
-        Const CLSID_FIREWALL_MANAGER As String = "{304CE942-6E39-40D8-943A-B913C40C9CD4}"
-        Dim objType As Type = Type.GetTypeFromCLSID(New Guid(CLSID_FIREWALL_MANAGER))
-        Return TryCast(Activator.CreateInstance(objType), INetFwMgr).LocalPolicy
-    End Function
-
-#End Region
-
 #Region "页面切换"
 #Region "页面切换"
 
 
     ''' <summary>
     ''' <summary>
@@ -184,7 +104,6 @@ Public Class PageLinkLeft
         PageLinkHiper.ModuleStopManually()
         PageLinkHiper.ModuleStopManually()
     End Sub
     End Sub
     Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs)
     Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs)
-        sender.Visibility = If(PageLinkIoi.InitLoader.State = LoadState.Finished, Visibility.Visible, Visibility.Collapsed)
     End Sub
     End Sub
     Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs)
     Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs)
         PageLinkIoi.ModuleStopManually()
         PageLinkIoi.ModuleStopManually()

+ 2 - 2
Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml

@@ -79,7 +79,7 @@
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                         </Grid.RowDefinitions>
                         </Grid.RowDefinitions>
                         <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
                         <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
-                        <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="LaunchArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则不进行更改。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="默认" MaxLength="100" />
+                        <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="LaunchArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则不进行更改。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="默认" MaxLength="100" />
                         <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
                         <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
                         <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchArgumentInfo" Text="Plain Craft Launcher 2" Grid.Column="1" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角">
                         <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchArgumentInfo" Text="Plain Craft Launcher 2" Grid.Column="1" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角">
                             <local:MyTextBox.ValidateRules>
                             <local:MyTextBox.ValidateRules>
@@ -235,7 +235,7 @@
                     <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。" />
                     <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" />
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText=""
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText=""
-                                     ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" />
+                                     ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
                     <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="LaunchAdvanceRunWait" Visibility="Collapsed"
                     <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="LaunchAdvanceRunWait" Visibility="Collapsed"
                                       Text="等待命令执行完成后再继续启动" Margin="0,8,0,7" />
                                       Text="等待命令执行完成后再继续启动" Margin="0,8,0,7" />
                     <StackPanel Margin="0,12,0,4" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
                     <StackPanel Margin="0,12,0,4" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">

+ 4 - 0
Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml

@@ -26,6 +26,7 @@
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
+                            <RowDefinition Height="Auto" />
                         </Grid.RowDefinitions>
                         </Grid.RowDefinitions>
                         <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表获取" Margin="0,0,25,0" />
                         <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表获取" Margin="0,0,25,0" />
                         <local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1">
                         <local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1">
@@ -55,6 +56,9 @@
                         <local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器显示" Grid.Row="8" Height="22" Grid.ColumnSpan="2"
                         <local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器显示" Grid.Row="8" Height="22" Grid.ColumnSpan="2"
                                           x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt" 
                                           x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt" 
                                           ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。&#xa;例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" />
                                           ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。&#xa;例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" />
+                        <local:MyCheckBox Margin="0,2,0,4" Text="验证 SSL 证书" Grid.Row="9" Height="22" Grid.ColumnSpan="2"
+                                          x:Name="CheckDownloadCert" Tag="ToolDownloadCert" 
+                                          ToolTip="在进行连接时,检查 SSL 证书是否正常。&#xa;进行验证会提高安全性,但疑似会导致少部分玩家的微软登录失败。" />
                     </Grid>
                     </Grid>
                 </StackPanel>
                 </StackPanel>
             </local:MyCard>
             </local:MyCard>

+ 3 - 1
Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb

@@ -58,6 +58,7 @@
         ComboDownloadTranslate.SelectedIndex = Setup.Get("ToolDownloadTranslate")
         ComboDownloadTranslate.SelectedIndex = Setup.Get("ToolDownloadTranslate")
         CheckDownloadKeepModpack.Checked = Setup.Get("ToolDownloadKeepModpack")
         CheckDownloadKeepModpack.Checked = Setup.Get("ToolDownloadKeepModpack")
         CheckDownloadIgnoreQuilt.Checked = Setup.Get("ToolDownloadIgnoreQuilt")
         CheckDownloadIgnoreQuilt.Checked = Setup.Get("ToolDownloadIgnoreQuilt")
+        CheckDownloadCert.Checked = Setup.Get("ToolDownloadCert")
 
 
         'Minecraft 更新提示
         'Minecraft 更新提示
         CheckUpdateRelease.Checked = Setup.Get("ToolUpdateRelease")
         CheckUpdateRelease.Checked = Setup.Get("ToolUpdateRelease")
@@ -88,6 +89,7 @@
             Setup.Reset("ToolDownloadTranslate")
             Setup.Reset("ToolDownloadTranslate")
             Setup.Reset("ToolDownloadKeepModpack")
             Setup.Reset("ToolDownloadKeepModpack")
             Setup.Reset("ToolDownloadIgnoreQuilt")
             Setup.Reset("ToolDownloadIgnoreQuilt")
+            Setup.Reset("ToolDownloadCert")
             Setup.Reset("ToolUpdateRelease")
             Setup.Reset("ToolUpdateRelease")
             Setup.Reset("ToolUpdateSnapshot")
             Setup.Reset("ToolUpdateSnapshot")
             Setup.Reset("ToolHelpChinese")
             Setup.Reset("ToolHelpChinese")
@@ -109,7 +111,7 @@
     End Sub
     End Sub
 
 
     '将控件改变路由到设置改变
     '将控件改变路由到设置改变
-    Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckDebugMode.Change, CheckDebugDelay.Change, CheckDebugSkipCopy.Change, CheckUpdateRelease.Change, CheckUpdateSnapshot.Change, CheckHelpChinese.Change, CheckDownloadKeepModpack.Change, CheckDownloadIgnoreQuilt.Change
+    Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckDebugMode.Change, CheckDebugDelay.Change, CheckDebugSkipCopy.Change, CheckUpdateRelease.Change, CheckUpdateSnapshot.Change, CheckHelpChinese.Change, CheckDownloadKeepModpack.Change, CheckDownloadIgnoreQuilt.Change, CheckDownloadCert.Change
         If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked)
         If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked)
     End Sub
     End Sub
     Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change
     Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change

+ 33 - 8
Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb

@@ -1,4 +1,6 @@
-Public Class PageVersionMod
+Imports System.Security.Principal
+
+Public Class PageVersionMod
 
 
 #Region "初始化"
 #Region "初始化"
 
 
@@ -65,12 +67,12 @@
                 PanBack.Visibility = Visibility.Collapsed
                 PanBack.Visibility = Visibility.Collapsed
                 Exit Sub
                 Exit Sub
             End If
             End If
-            SearchBox.Text = ""
             '输出结果
             '输出结果
             ModItems.Clear()
             ModItems.Clear()
             For Each ModEntity As McMod In Mods
             For Each ModEntity As McMod In Mods
                 ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
                 ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
             Next
             Next
+            SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
             RefreshResult(Mods)
             RefreshResult(Mods)
         Catch ex As Exception
         Catch ex As Exception
             Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
             Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
@@ -333,20 +335,33 @@
     End Sub
     End Sub
     Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
     Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
         Dim IsSuccessful As Boolean = True
         Dim IsSuccessful As Boolean = True
-        For Each ModEntity In ModList.ToList
+        For Each ModE In ModList.ToList
+            Dim ModEntity = ModE '仅用于去除迭代变量无法修改的限制
             Dim NewPath As String = Nothing
             Dim NewPath As String = Nothing
             If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
             If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
                 '禁用
                 '禁用
-                NewPath = ModEntity.Path & ".disabled"
+                NewPath = ModEntity.Path & If(File.Exists(ModEntity.Path & ".old"), ".old", ".disabled")
             ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then
             ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then
                 '启用
                 '启用
-                NewPath = ModEntity.Path.Replace(".disabled", "").Replace(".old", "")
+                NewPath = ModEntity.RawPath
             Else
             Else
                 Continue For
                 Continue For
             End If
             End If
             '重命名
             '重命名
             Try
             Try
-                If File.Exists(NewPath) AndAlso Not File.Exists(ModEntity.Path) Then Continue For '因为未知原因 Mod 的状态已经切换完了
+                If File.Exists(NewPath) Then
+                    If File.Exists(ModEntity.Path) Then
+                        '同时存在两个名称的 Mod
+                        If GetFileMD5(ModEntity.Path) <> GetFileMD5(NewPath) Then
+                            MyMsgBox($"目前同时存在启用和禁用的两个 Mod 文件:{vbCrLf} - {NewPath}{vbCrLf} - {ModEntity.Path}{vbCrLf}{vbCrLf}注意,这两个文件的内容并不相同。{vbCrLf}在手动删除或重命名其中一个文件后,才能继续操作。", "存在文件冲突")
+                            Continue For
+                        End If
+                    Else
+                        '已经重命名过了
+                        Log("[Mod] Mod 的状态已被切换", LogLevel.Debug)
+                        Continue For
+                    End If
+                End If
                 File.Delete(NewPath)
                 File.Delete(NewPath)
                 FileSystem.Rename(ModEntity.Path, NewPath)
                 FileSystem.Rename(ModEntity.Path, NewPath)
             Catch ex As FileNotFoundException
             Catch ex As FileNotFoundException
@@ -389,6 +404,16 @@
         Try
         Try
             Dim IsSuccessful As Boolean = True
             Dim IsSuccessful As Boolean = True
             Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
             Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
+            '确认需要删除的文件
+            ModList = ModList.SelectMany(
+            Function(Target As McMod)
+                If Target.State = McMod.McModState.Fine Then
+                    Return {Target.Path, Target.Path & If(File.Exists(Target.Path & ".old"), ".old", ".disabled")}
+                Else
+                    Return {Target.Path, Target.RawPath}
+                End If
+            End Function).Distinct.Where(Function(m) File.Exists(m)).Select(Function(m) New McMod(m)).ToList()
+            '实际删除文件
             For Each ModEntity In ModList
             For Each ModEntity In ModList
                 '删除
                 '删除
                 Try
                 Try
@@ -410,8 +435,8 @@
                 '更改 Loader 和 UI 中的列表
                 '更改 Loader 和 UI 中的列表
                 McModLoader.Output.Remove(ModEntity)
                 McModLoader.Output.Remove(ModEntity)
                 ModItems.Remove(ModEntity.RawFileName)
                 ModItems.Remove(ModEntity.RawFileName)
-                Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).First(Function(i) i.Entry Is ModEntity))
-                PanList.Children.RemoveAt(IndexOfUi)
+                Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
+                If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
             Next
             Next
             RefreshTitle()
             RefreshTitle()
             If Not IsSuccessful Then
             If Not IsSuccessful Then

+ 2 - 4
Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml

@@ -26,7 +26,7 @@
                             <RowDefinition Height="28" />
                             <RowDefinition Height="28" />
                         </Grid.RowDefinitions>
                         </Grid.RowDefinitions>
                         <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
                         <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
-                        <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则使用全局设置的值。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="使用全局设置" MaxLength="100" />
+                        <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则使用全局设置的值。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="使用全局设置" MaxLength="100" />
                         <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
                         <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
                         <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionArgumentInfo" Grid.Column="1" HintText="使用全局设置" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角。&#xa;若留空,则使用全局设置的值。">
                         <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionArgumentInfo" Grid.Column="1" HintText="使用全局设置" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角。&#xa;若留空,则使用全局设置的值。">
                             <local:MyTextBox.ValidateRules>
                             <local:MyTextBox.ValidateRules>
@@ -168,10 +168,8 @@
                     <Grid Height="35" Name="PanServerNide" Margin="0,12,0,0">
                     <Grid Height="35" Name="PanServerNide" Margin="0,12,0,0">
                         <Grid.ColumnDefinitions>
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
                             <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
                         </Grid.ColumnDefinitions>
                         </Grid.ColumnDefinitions>
                         <local:MyButton x:Name="BtnServerNideWeb" Grid.Column="0" MinWidth="140" Text="服主后台" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
                         <local:MyButton x:Name="BtnServerNideWeb" Grid.Column="0" MinWidth="140" Text="服主后台" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnServerNideBbs" Grid.Column="1" MinWidth="140" Text="查看简介" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
                     </Grid>
                     </Grid>
                     <local:MyButton x:Name="BtnServerAuthLittle" Height="35" MinWidth="150" Margin="0,12,0,0" Text="设置为 LittleSkin" HorizontalAlignment="Left" ColorType="Highlight" />
                     <local:MyButton x:Name="BtnServerAuthLittle" Height="35" MinWidth="150" Margin="0,12,0,0" Text="设置为 LittleSkin" HorizontalAlignment="Left" ColorType="Highlight" />
                 </StackPanel>
                 </StackPanel>
@@ -199,7 +197,7 @@
                     <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。&#xa;若留空,则使用全局设置的值。" HintText="使用全局设置" />
                     <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。&#xa;若留空,则使用全局设置的值。" HintText="使用全局设置" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
-                                     ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" />
+                                     ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
                     <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
                     <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
                                       Text="等待命令执行完成后再继续启动" Margin="0,5,0,0" />
                                       Text="等待命令执行完成后再继续启动" Margin="0,5,0,0" />
                     <StackPanel Margin="0,10,0,0" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
                     <StackPanel Margin="0,10,0,0" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">

+ 6 - 7
Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb

@@ -121,9 +121,10 @@
     End Sub
     End Sub
     Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerNide.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.ValidatedTextChanged, TextAdvanceRun.ValidatedTextChanged
     Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerNide.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.ValidatedTextChanged, TextAdvanceRun.ValidatedTextChanged
         If AniControlEnabled = 0 Then
         If AniControlEnabled = 0 Then
-            Dim HandledText As String = sender.Text
-            If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/")
-            Setup.Set(sender.Tag, HandledText, Version:=PageVersionLeft.Version)
+            '#3194,不能删减 /
+            'Dim HandledText As String = sender.Text
+            'If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/")
+            Setup.Set(sender.Tag, sender.Text, Version:=PageVersionLeft.Version)
         End If
         End If
     End Sub
     End Sub
     Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderRamCustom.Change
     Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderRamCustom.Change
@@ -398,14 +399,12 @@ PreFin:
     Private Sub BtnServerNideWeb_Click(sender As Object, e As EventArgs) Handles BtnServerNideWeb.Click
     Private Sub BtnServerNideWeb_Click(sender As Object, e As EventArgs) Handles BtnServerNideWeb.Click
         OpenWebsite("https://login.mc-user.com:233/server/intro")
         OpenWebsite("https://login.mc-user.com:233/server/intro")
     End Sub
     End Sub
-    Private Sub BtnServerNideBbs_Click(sender As Object, e As EventArgs) Handles BtnServerNideBbs.Click
-        OpenWebsite("https://www.mcbbs.net/thread-729821-1-1.html")
-    End Sub
 
 
     'LittleSkin
     'LittleSkin
     Private Sub BtnServerAuthLittle_Click(sender As Object, e As EventArgs) Handles BtnServerAuthLittle.Click
     Private Sub BtnServerAuthLittle_Click(sender As Object, e As EventArgs) Handles BtnServerAuthLittle.Click
         If TextServerAuthServer.Text <> "" AndAlso TextServerAuthServer.Text <> "https://littleskin.cn/api/yggdrasil" AndAlso
         If TextServerAuthServer.Text <> "" AndAlso TextServerAuthServer.Text <> "https://littleskin.cn/api/yggdrasil" AndAlso
-            MyMsgBox("即将把第三方登录设置覆盖为自动使用 LittleSkin 登录,是否继续?", "覆盖确认", "确定", "取消") = 2 Then Exit Sub
+            MyMsgBox("即将把第三方登录设置覆盖为 LittleSkin 登录。" & vbCrLf & "除非你是服主,或者服主要求你这样做,否则请不要继续。" & vbCrLf & vbCrLf & "是否确实需要覆盖当前设置?",
+                     "设置覆盖确认", "继续", "取消") = 2 Then Exit Sub
         TextServerAuthServer.Text = "https://littleskin.cn/api/yggdrasil"
         TextServerAuthServer.Text = "https://littleskin.cn/api/yggdrasil"
         TextServerAuthRegister.Text = "https://littleskin.cn/auth/register"
         TextServerAuthRegister.Text = "https://littleskin.cn/auth/register"
         TextServerAuthName.Text = "LittleSkin 登录"
         TextServerAuthName.Text = "LittleSkin 登录"

+ 0 - 3
Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj

@@ -838,9 +838,6 @@
   <ItemGroup>
   <ItemGroup>
     <Resource Include="Images\Blocks\Fabric.png" />
     <Resource Include="Images\Blocks\Fabric.png" />
   </ItemGroup>
   </ItemGroup>
-  <ItemGroup>
-    <Resource Include="Images\Heads\MCBBS.png" />
-  </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="Resources\forge-installer.jar" />
     <None Include="Resources\forge-installer.jar" />
   </ItemGroup>
   </ItemGroup>

+ 5 - 5
Plain Craft Launcher 2/Resources/Custom.xaml

@@ -30,7 +30,7 @@
                     Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
                     Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
         <TextBlock TextWrapping="Wrap" Margin="0,0,0,6"
         <TextBlock TextWrapping="Wrap" Margin="0,0,0,6"
                     Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" />
                     Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" />
-		<Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
+		<Image Height="50" HorizontalAlignment="Center" Source="https://zh.minecraft.wiki/images/Grass_Block_JE7_BE6.png?bf67e" />
 	</StackPanel>
 	</StackPanel>
 </local:MyCard>
 </local:MyCard>
 
 
@@ -58,7 +58,7 @@
         <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
         <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
                     Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" />
                     Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" />
 		<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
 		<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
-                    Text="打开 MCBBS" EventType="打开网页" EventData="https://www.mcbbs.net/" />
+                    Text="打开 Minecraft Wiki" EventType="打开网页" EventData="https://zh.minecraft.wiki/" />
         <TextBlock TextWrapping="Wrap" Margin="0,10,0,10"
         <TextBlock TextWrapping="Wrap" Margin="0,10,0,10"
                     Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" />
                     Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" />
 		<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
 		<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
@@ -119,7 +119,7 @@
 		<TextBlock Margin="0,0,0,4"
 		<TextBlock Margin="0,0,0,4"
                     Text="· 下载文件:在点击时要求选择文件保存位置,并且下载 EventData 的链接中的文件" />
                     Text="· 下载文件:在点击时要求选择文件保存位置,并且下载 EventData 的链接中的文件" />
 		<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
 		<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
-                    Text="下载 MCBBS 的 Logo" EventType="下载文件" EventData="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
+                    Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
 		<TextBlock Margin="0,4,0,4"
 		<TextBlock Margin="0,4,0,4"
                     Text="· 打开帮助:跳转到 EventData 对应的帮助文件(输入相对 Help 文件夹的路径)"/>
                     Text="· 打开帮助:跳转到 EventData 对应的帮助文件(输入相对 Help 文件夹的路径)"/>
 		<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
 		<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
@@ -153,7 +153,7 @@
 			<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
 			<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
                         Text="内存优化" EventType="内存优化" />
                         Text="内存优化" EventType="内存优化" />
 			<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
 			<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
-                        Text="下载作者的头像" EventType="下载文件" EventData="https://www.mcbbs.net/uc_server/data/avatar/000/14/51/06_avatar_small.jpg?ts=1471402592" />
+                        Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
 		</StackPanel>
 		</StackPanel>
 	</StackPanel>
 	</StackPanel>
 </local:MyCard>
 </local:MyCard>
@@ -227,7 +227,7 @@
             <TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" />
             <TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" />
             <TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" />
             <TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" />
 		</StackPanel>
 		</StackPanel>
-		<TextBlock Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
+		<TextBlock TextWrapping="Wrap" Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
                     Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" />
                     Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" />
 	</StackPanel>
 	</StackPanel>
 </local:MyCard>
 </local:MyCard>

BIN
Plain Craft Launcher 2/Resources/Help.zip


Разница между файлами не показана из-за своего большого размера
+ 243 - 243
Plain Craft Launcher 2/Resources/ModData.txt


BIN
Plain Craft Launcher 2/Resources/java-wrapper.jar


Некоторые файлы не были показаны из-за большого количества измененных файлов