├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── build-and-package.yml ├── .gitignore ├── LICENSE ├── README.md ├── UMCLauncher.sln ├── UMCLauncher ├── UMCLauncher.Core │ ├── Helpers │ │ ├── DataHelper.cs │ │ ├── DateHelper.cs │ │ ├── Extensions.cs │ │ └── OSVersionHelper.cs │ ├── Models │ │ └── JavaVersion.cs │ ├── UMCLauncher.Core.csproj │ └── WeakEvent.cs └── UMCLauncher │ ├── App.xaml │ ├── App.xaml.cs │ ├── Assets │ ├── LockScreenLogo.scale-200.png │ ├── SplashScreen.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── StoreLogo.png │ └── Wide310x150Logo.scale-200.png │ ├── Control │ ├── LoginDialog.xaml │ ├── LoginDialog.xaml.cs │ ├── PageHeader.xaml │ └── PageHeader.xaml.cs │ ├── Helpers │ ├── BackdropHelper.cs │ ├── DataHelper │ │ ├── DataSourceBase.cs │ │ └── IncrementalLoadingBase.cs │ ├── DispatcherQueueHelper.cs │ ├── DownloadHelper.cs │ ├── Exceptions │ │ └── ExceptionHandling.cs │ ├── LaunchHelper.cs │ ├── SettingsHelper.cs │ ├── ThemeHelper.cs │ ├── TitleBarHelper.cs │ ├── Trigger │ │ └── DeviceFamilyTrigger.cs │ ├── UIHelper.cs │ ├── ValueConverters │ │ └── TimeToReadableConverter.cs │ └── WindowHelper.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Package.appxmanifest │ ├── Pages │ ├── BrowserPage.xaml │ ├── BrowserPage.xaml.cs │ ├── DownloadPage.xaml │ ├── DownloadPage.xaml.cs │ ├── HomePage.xaml │ ├── HomePage.xaml.cs │ ├── ListPage.xaml │ ├── ListPage.xaml.cs │ ├── MainPage.xaml │ ├── MainPage.xaml.cs │ ├── MyPage.xaml │ ├── MyPage.xaml.cs │ └── SettingPages │ │ ├── SettingPage.xaml │ │ ├── SettingPage.xaml.cs │ │ ├── TestPage.xaml │ │ └── TestPage.xaml.cs │ ├── Properties │ ├── PublishProfiles │ │ ├── win10-arm64.pubxml │ │ ├── win10-x64.pubxml │ │ └── win10-x86.pubxml │ └── launchSettings.json │ ├── Strings │ └── zh-CN │ │ └── Resources.resw │ ├── UMCLauncher.csproj │ └── app.manifest └── _config.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: wherewhere 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['afdian.net/@wherewhere'] 14 | -------------------------------------------------------------------------------- /.github/workflows/build-and-package.yml: -------------------------------------------------------------------------------- 1 | name: build and package 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [main] 7 | paths: 8 | - 'UMCLauncher/**' 9 | workflow_dispatch: 10 | 11 | env: 12 | DOTNET_VERSION: '6.0.x' # The .NET SDK version to use 13 | 14 | jobs: 15 | build-and-test: 16 | 17 | name: build-and-package 18 | runs-on: windows-latest 19 | 20 | env: 21 | Solution_Name: UMCLauncher.sln 22 | Project_Directory: UMCLauncher/UMCLauncher 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | 30 | # Install the .NET Core workload 31 | - name: Install .NET Core 32 | uses: actions/setup-dotnet@v3 33 | with: 34 | dotnet-version: ${{env.DOTNET_VERSION}} 35 | 36 | # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild 37 | - name: Setup MSBuild.exe 38 | uses: microsoft/setup-msbuild@v1 39 | 40 | # Restore the application to populate the obj folder with RuntimeIdentifiers 41 | - name: Restore the application 42 | run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration 43 | env: 44 | Configuration: Release 45 | 46 | # Create the app package by building and packaging the Windows Application Packaging project 47 | - name: Create the app package 48 | run: msbuild $env:Solution_Name /p:AppxBundlePlatforms="$env:Appx_Bundle_Platforms" /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:AppxPackageDir="$env:Appx_Package_Dir" /p:GenerateAppxPackageOnBuild=true /p:AppxPackageSigningEnabled=false 49 | env: 50 | Appx_Bundle: Always 51 | Appx_Bundle_Platforms: x86|x64|ARM64 52 | Appx_Package_Build_Mode: SideloadOnly 53 | Appx_Package_Dir: AppxPackages\ 54 | Configuration: Release 55 | 56 | # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact 57 | - name: Upload build artifacts 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: MSIX Package 61 | path: UMCLauncher/UMCLauncher/AppxPackages/** 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | # *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minecraft-Launcher 2 | 一个基于 WinUI 3 平台的 Minecraft Java 启动器 3 | 4 | GitHub 5 | GitHub 6 | GitHub 7 | 8 | GitHub All Releases 9 | Baidu Netdisk 10 | 11 | ## 目录 12 | - [Minecraft-Launcher](#Minecraft-Launcher) 13 | - [目录](#目录) 14 | - [使用到的模块](#使用到的模块) 15 | 16 | ## 使用到的模块 17 | - [Win UI](https://github.com/microsoft/microsoft-ui-xaml "Win UI") 18 | - [KMCCC](https://github.com/MineStudio/KMCCC "KMCCC") 19 | - [ModuleLauncher.Re](https://github.com/AHpxChina/ModuleLauncher.Re "ModuleLauncher.Re") 20 | 21 | ## Star 数量统计 22 | [![Star 数量统计](https://starchart.cc/wherewhere/Minecraft-Launcher.svg)](https://starchart.cc/wherewhere/Minecraft-Launcher "Star 数量统计") -------------------------------------------------------------------------------- /UMCLauncher.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31825.309 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UMCLauncher", "UMCLauncher\UMCLauncher\UMCLauncher.csproj", "{A6B5A692-8FB5-41CE-B479-05F161BC54A5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UMCLauncher.Core", "UMCLauncher\UMCLauncher.Core\UMCLauncher.Core.csproj", "{1A845D38-CC34-45FF-9A9B-16706D05371E}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|arm64 = Debug|arm64 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|Any CPU = Release|Any CPU 17 | Release|arm64 = Release|arm64 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|Any CPU.ActiveCfg = Debug|x64 23 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|Any CPU.Build.0 = Debug|x64 24 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|Any CPU.Deploy.0 = Debug|x64 25 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|arm64.ActiveCfg = Debug|arm64 26 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|arm64.Build.0 = Debug|arm64 27 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|arm64.Deploy.0 = Debug|arm64 28 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x64.ActiveCfg = Debug|x64 29 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x64.Build.0 = Debug|x64 30 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x64.Deploy.0 = Debug|x64 31 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x86.ActiveCfg = Debug|x86 32 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x86.Build.0 = Debug|x86 33 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Debug|x86.Deploy.0 = Debug|x86 34 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|Any CPU.ActiveCfg = Release|x64 35 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|Any CPU.Build.0 = Release|x64 36 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|Any CPU.Deploy.0 = Release|x64 37 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|arm64.ActiveCfg = Release|arm64 38 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|arm64.Build.0 = Release|arm64 39 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|arm64.Deploy.0 = Release|arm64 40 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x64.ActiveCfg = Release|x64 41 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x64.Build.0 = Release|x64 42 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x64.Deploy.0 = Release|x64 43 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x86.ActiveCfg = Release|x86 44 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x86.Build.0 = Release|x86 45 | {A6B5A692-8FB5-41CE-B479-05F161BC54A5}.Release|x86.Deploy.0 = Release|x86 46 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|arm64.ActiveCfg = Debug|Any CPU 49 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|arm64.Build.0 = Debug|Any CPU 50 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|x64.ActiveCfg = Debug|Any CPU 51 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|x64.Build.0 = Debug|Any CPU 52 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|x86.ActiveCfg = Debug|Any CPU 53 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Debug|x86.Build.0 = Debug|Any CPU 54 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|arm64.ActiveCfg = Release|Any CPU 57 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|arm64.Build.0 = Release|Any CPU 58 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|x64.ActiveCfg = Release|Any CPU 59 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|x64.Build.0 = Release|Any CPU 60 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|x86.ActiveCfg = Release|Any CPU 61 | {1A845D38-CC34-45FF-9A9B-16706D05371E}.Release|x86.Build.0 = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(ExtensibilityGlobals) = postSolution 67 | SolutionGuid = {C1551682-C34D-44C7-8E90-FB003EB56823} 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/Helpers/DataHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | using System; 4 | 5 | namespace UMCLauncher.Core.Helpers 6 | { 7 | public static class DataHelper 8 | { 9 | public static string GetMD5(this string input) 10 | { 11 | // Create a new instance of the MD5CryptoServiceProvider object. 12 | // Convert the input string to a byte array and compute the hash. 13 | byte[] data = MD5.HashData(Encoding.UTF8.GetBytes(input)); 14 | string results = BitConverter.ToString(data).ToLowerInvariant(); 15 | return results.Replace("-", ""); 16 | } 17 | 18 | public static string GetBase64(this string input, bool israw = false) 19 | { 20 | byte[] bytes = Encoding.UTF8.GetBytes(input); 21 | string result = Convert.ToBase64String(bytes); 22 | if (israw) { result = result.Replace("=", ""); } 23 | return result; 24 | } 25 | 26 | public static string Reverse(this string text) 27 | { 28 | char[] charArray = text.ToCharArray(); 29 | Array.Reverse(charArray); 30 | return new string(charArray); 31 | } 32 | 33 | public static string GetSizeString(this double size) 34 | { 35 | int index = 0; 36 | while (index <= 11) 37 | { 38 | index++; 39 | size /= 1024; 40 | if (size > 0.7 && size < 716.8) { break; } 41 | else if (size >= 716.8) { continue; } 42 | else if (size <= 0.7) 43 | { 44 | size *= 1024; 45 | index--; 46 | break; 47 | } 48 | } 49 | string str = string.Empty; 50 | switch (index) 51 | { 52 | case 0: str = "B"; break; 53 | case 1: str = "KB"; break; 54 | case 2: str = "MB"; break; 55 | case 3: str = "GB"; break; 56 | case 4: str = "TB"; break; 57 | case 5: str = "PB"; break; 58 | case 6: str = "EB"; break; 59 | case 7: str = "ZB"; break; 60 | case 8: str = "YB"; break; 61 | case 9: str = "BB"; break; 62 | case 10: str = "NB"; break; 63 | case 11: str = "DB"; break; 64 | default: 65 | break; 66 | } 67 | return $"{size:0.##}{str}"; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/Helpers/DateHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UMCLauncher.Core.Helpers 4 | { 5 | public static class DateHelper 6 | { 7 | public enum TimeIntervalType 8 | { 9 | MonthsAgo, 10 | DaysAgo, 11 | HoursAgo, 12 | MinutesAgo, 13 | JustNow, 14 | } 15 | 16 | private static readonly DateTime UnixDateBase = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 17 | 18 | public static string ConvertUnixTimeStampToReadable(double time, DateTime baseTime) 19 | { 20 | TimeIntervalType type; 21 | object obj; 22 | 23 | TimeSpan ttime = new((long)time * 1000_0000); 24 | DateTime tdate = UnixDateBase.Add(ttime); 25 | TimeSpan temp = baseTime.ToUniversalTime() 26 | .Subtract(tdate); 27 | 28 | if (temp.TotalDays > 30) 29 | { 30 | type = TimeIntervalType.MonthsAgo; 31 | obj = tdate; 32 | } 33 | else 34 | { 35 | type = temp.Days > 0 36 | ? TimeIntervalType.DaysAgo 37 | : temp.Hours > 0 38 | ? TimeIntervalType.HoursAgo 39 | : temp.Minutes > 0 40 | ? TimeIntervalType.MinutesAgo 41 | : TimeIntervalType.JustNow; 42 | obj = temp; 43 | } 44 | 45 | return type switch 46 | { 47 | TimeIntervalType.MonthsAgo => ((DateTime)obj).ToLongDateString(), 48 | TimeIntervalType.DaysAgo => $"{((TimeSpan)obj).Days}天前", 49 | TimeIntervalType.HoursAgo => $"{((TimeSpan)obj).Hours}小时前", 50 | TimeIntervalType.MinutesAgo => $"{((TimeSpan)obj).Minutes}分钟前", 51 | TimeIntervalType.JustNow => "刚刚", 52 | _ => string.Empty, 53 | }; 54 | } 55 | 56 | public static double ConvertDateTimeToUnixTimeStamp(DateTime time) 57 | { 58 | return Math.Round( 59 | time.ToUniversalTime() 60 | .Subtract(UnixDateBase) 61 | .TotalSeconds); 62 | } 63 | 64 | public static string ConvertDateTimeToReadable(DateTime time) 65 | { 66 | return ConvertUnixTimeStampToReadable(ConvertDateTimeToUnixTimeStamp(time)); 67 | } 68 | 69 | public static string ConvertUnixTimeStampToReadable(double time) 70 | { 71 | return ConvertUnixTimeStampToReadable(time, DateTime.Now); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/Helpers/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.Versioning; 7 | using System.Text; 8 | using UMCLauncher.Models; 9 | 10 | namespace UMCLauncher.Core.Helpers 11 | { 12 | public static class Extensions 13 | { 14 | public static IEnumerable Add(this IEnumerable e, T value) 15 | { 16 | foreach (T cur in e) 17 | { 18 | yield return cur; 19 | } 20 | yield return value; 21 | } 22 | 23 | [SupportedOSPlatform("windows")] 24 | public static List GetJavaInstallationPath() 25 | { 26 | List list = new(); 27 | 28 | string environmentPath = Environment.GetEnvironmentVariable("JAVA_HOME"); 29 | 30 | if (!string.IsNullOrEmpty(environmentPath) && File.Exists(Path.Combine(environmentPath, @"bin\javaw.exe"))) 31 | { 32 | list.Add(new JavaVersion(environmentPath)); 33 | } 34 | 35 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\JavaSoft\JDK\")) 36 | { 37 | if (rk != null) 38 | { 39 | string currentVersion = rk.GetValue("CurrentVersion").ToString(); 40 | 41 | using RegistryKey key = rk.OpenSubKey(currentVersion); 42 | string path = key.GetValue("JavaHome").ToString(); 43 | 44 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 45 | { 46 | list.Add(new JavaVersion(path)); 47 | } 48 | } 49 | } 50 | 51 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\JavaSoft\Java Runtime Environment\")) 52 | { 53 | if (rk != null) 54 | { 55 | string currentVersion = rk.GetValue("CurrentVersion").ToString(); 56 | 57 | using RegistryKey key = rk.OpenSubKey(currentVersion); 58 | string path = key.GetValue("JavaHome").ToString(); 59 | 60 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 61 | { 62 | list.Add(new JavaVersion(path)); 63 | } 64 | } 65 | } 66 | 67 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\JDK\")) 68 | { 69 | if (rk != null) 70 | { 71 | foreach (string currentVersion in rk.GetSubKeyNames()) 72 | { 73 | using RegistryKey key = rk.OpenSubKey(currentVersion + @"\hotspot\MSI"); 74 | string path = key.GetValue("Path").ToString(); 75 | 76 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 77 | { 78 | list.Add(new JavaVersion(path)); 79 | } 80 | } 81 | } 82 | } 83 | 84 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Mojang\InstalledProducts\Minecraft Launcher\")) 85 | { 86 | if (rk != null) 87 | { 88 | string path = rk.GetValue("InstallLocation").ToString(); 89 | 90 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x64\java-runtime-alpha\bin\javaw.exe"))) 91 | { 92 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x64\java-runtime-alpha"))); 93 | } 94 | 95 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-beta\windows-x64\java-runtime-beta\bin\javaw.exe"))) 96 | { 97 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-beta\windows-x64\java-runtime-beta"))); 98 | } 99 | 100 | if (File.Exists(Path.Combine(path, @"runtime\jre-legacy\windows-x64\jre-legacy\bin\javaw.exe"))) 101 | { 102 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-legacy\windows-x64\jre-legacy"))); 103 | } 104 | 105 | if (File.Exists(Path.Combine(path, @"runtime\jre-x64\bin\javaw.exe"))) 106 | { 107 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-x64"))); 108 | } 109 | } 110 | } 111 | 112 | if (OSVersionHelper.IsWindows8OrGreater) 113 | { 114 | string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local"); 115 | 116 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x64\java-runtime-alpha\bin\javaw.exe"))) 117 | { 118 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x64\java-runtime-alpha"))); 119 | } 120 | 121 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-beta\windows-x64\java-runtime-beta\bin\javaw.exe"))) 122 | { 123 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-beta\windows-x64\java-runtime-beta"))); 124 | } 125 | 126 | if (File.Exists(Path.Combine(path, @"runtime\jre-legacy\windows-x64\jre-legacy\bin\javaw.exe"))) 127 | { 128 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-legacy\windows-x64\jre-legacy"))); 129 | } 130 | 131 | if (File.Exists(Path.Combine(path, @"runtime\jre-x64\bin\javaw.exe"))) 132 | { 133 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-x64"))); 134 | } 135 | } 136 | 137 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\JavaSoft\JDK\")) 138 | { 139 | if (rk != null) 140 | { 141 | string currentVersion = rk.GetValue("CurrentVersion").ToString(); 142 | 143 | using RegistryKey key = rk.OpenSubKey(currentVersion); 144 | string path = key.GetValue("JavaHome").ToString(); 145 | 146 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 147 | { 148 | list.Add(new JavaVersion(path, true)); 149 | } 150 | } 151 | } 152 | 153 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\JavaSoft\Java Runtime Environment\")) 154 | { 155 | if (rk != null) 156 | { 157 | string currentVersion = rk.GetValue("CurrentVersion").ToString(); 158 | 159 | using RegistryKey key = rk.OpenSubKey(currentVersion); 160 | string path = key.GetValue("JavaHome").ToString(); 161 | 162 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 163 | { 164 | list.Add(new JavaVersion(path, true)); 165 | } 166 | } 167 | } 168 | 169 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\JDK\")) 170 | { 171 | if (rk != null) 172 | { 173 | foreach (string currentVersion in rk.GetSubKeyNames()) 174 | { 175 | using RegistryKey key = rk.OpenSubKey(currentVersion + @"\hotspot\MSI"); 176 | string path = key.GetValue("Path").ToString(); 177 | 178 | if (File.Exists(Path.Combine(path, @"bin\javaw.exe"))) 179 | { 180 | list.Add(new JavaVersion(path, true)); 181 | } 182 | } 183 | } 184 | } 185 | 186 | using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Mojang\InstalledProducts\Minecraft Launcher\")) 187 | { 188 | if (rk != null) 189 | { 190 | string path = rk.GetValue("InstallLocation").ToString(); 191 | 192 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x86\java-runtime-alpha\bin\javaw.exe"))) 193 | { 194 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-alpha\windows-x86\java-runtime-alpha"), true)); 195 | } 196 | 197 | if (File.Exists(Path.Combine(path, @"runtime\java-runtime-beta\windows-x86\java-runtime-beta\bin\javaw.exe"))) 198 | { 199 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\java-runtime-beta\windows-x86\java-runtime-beta"), true)); 200 | } 201 | 202 | if (File.Exists(Path.Combine(path, @"runtime\jre-legacy\windows-x86\jre-legacy\bin\javaw.exe"))) 203 | { 204 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-legacy\windows-x86\jre-legacy"), true)); 205 | } 206 | 207 | if (File.Exists(Path.Combine(path, @"runtime\jre-x86\bin\javaw.exe"))) 208 | { 209 | list.Add(new JavaVersion(Path.Combine(path, @"runtime\jre-x86"), true)); 210 | } 211 | } 212 | } 213 | 214 | list = list.GroupBy(x => x.JavaPath).Select(y => y.First()).ToList(); 215 | list.Sort(); 216 | return list; 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/Helpers/OSVersionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace UMCLauncher.Core.Helpers 6 | { 7 | public static class OSVersionHelper 8 | { 9 | public static readonly Version OSVersion = GetOSVersion(); 10 | 11 | /// 12 | /// Whether the operating system is NT. 13 | /// 14 | public static bool IsWindowsNT { get; } = Environment.OSVersion.Platform == PlatformID.Win32NT; 15 | 16 | /// 17 | /// Whether the operating system version is greater than or equal to 6.0. 18 | /// 19 | public static bool IsWindowsVistaOrGreater { get; } = IsWindowsNT && OSVersion >= new Version(6, 0); 20 | 21 | /// 22 | /// Whether the operating system version is greater than or equal to 6.1. 23 | /// 24 | public static bool IsWindows7OrGreater { get; } = IsWindowsNT && OSVersion >= new Version(6, 1); 25 | 26 | /// 27 | /// Whether the operating system version is greater than or equal to 6.2. 28 | /// 29 | public static bool IsWindows8OrGreater { get; } = IsWindowsNT && OSVersion >= new Version(6, 2); 30 | 31 | /// 32 | /// Whether the operating system version is greater than or equal to 10.0. 33 | /// 34 | public static bool IsWindows10OrGreater { get; } = IsWindowsNT && OSVersion >= new Version(10, 0); 35 | 36 | /// 37 | /// Whether the operating system version is greater than or equal to 10.0* (build 21996). 38 | /// 39 | public static bool IsWindows11OrGreater { get; } = IsWindowsNT && OSVersion >= new Version(10, 0, 21996); 40 | 41 | /// 42 | /// Whether the operating system version build is greater than the value. 43 | /// 44 | /// Build verson 45 | public static bool IsOSVersonGreater(this int Build) => OSVersion.Build >= Build; 46 | 47 | private static Version GetOSVersion() 48 | { 49 | var osv = new RTL_OSVERSIONINFOEX(); 50 | osv.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osv); 51 | int ret = RtlGetVersion(out osv); 52 | Debug.Assert(ret == 0); 53 | return new Version((int)osv.dwMajorVersion, (int)osv.dwMinorVersion, (int)osv.dwBuildNumber); 54 | } 55 | 56 | [DllImport("ntdll.dll")] 57 | private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation); 58 | 59 | [StructLayout(LayoutKind.Sequential)] 60 | private struct RTL_OSVERSIONINFOEX 61 | { 62 | internal uint dwOSVersionInfoSize; 63 | internal uint dwMajorVersion; 64 | internal uint dwMinorVersion; 65 | internal uint dwBuildNumber; 66 | internal uint dwPlatformId; 67 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 68 | internal string szCSDVersion; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/Models/JavaVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace UMCLauncher.Models 8 | { 9 | public class JavaVersion : IComparable 10 | { 11 | /// 12 | /// 是否为 x86 13 | /// 14 | public bool ISWOW6432 { get; set; } 15 | 16 | /// 17 | /// 环境根目录 18 | /// 19 | public string RootPath { get; set; } 20 | 21 | /// 22 | /// java.exe 目录 23 | /// 24 | public string JavaPath => Path.Combine(RootPath, @"bin\javaw.exe"); 25 | 26 | /// 27 | /// Java 环境版本信息 28 | /// 29 | public FileVersionInfo Version => FileVersionInfo.GetVersionInfo(JavaPath); 30 | 31 | public JavaVersion(string path, bool x86 = false) 32 | { 33 | RootPath = path; 34 | ISWOW6432 = x86; 35 | } 36 | 37 | public int CompareTo(object obj) 38 | { 39 | if (obj is JavaVersion another) 40 | { 41 | int result = another.ISWOW6432.CompareTo(ISWOW6432); 42 | if (result == 0) 43 | { 44 | result = GetAsVersionInfo(Version.ProductVersion).CompareTo(GetAsVersionInfo(another.Version.ProductVersion)); 45 | if (result == 0) 46 | { 47 | JavaPath.CompareTo(another.JavaPath); 48 | } 49 | } 50 | return result; 51 | } 52 | throw new ArgumentException(null, nameof(obj)); 53 | } 54 | 55 | public static bool operator <(JavaVersion left, JavaVersion right) => left.CompareTo(right) < 0; 56 | 57 | public static bool operator <=(JavaVersion left, JavaVersion right) => left.CompareTo(right) <= 0; 58 | 59 | public static bool operator >(JavaVersion left, JavaVersion right) => left.CompareTo(right) > 0; 60 | 61 | public static bool operator >=(JavaVersion left, JavaVersion right) => left.CompareTo(right) >= 0; 62 | 63 | private static Version GetAsVersionInfo(string version) 64 | { 65 | List nums = GetVersionNumbers(version).Split('.').Select(int.Parse).ToList(); 66 | 67 | return nums.Count <= 1 68 | ? new Version(nums[0], 0, 0, 0) 69 | : nums.Count <= 2 70 | ? new Version(nums[0], nums[1], 0, 0) 71 | : nums.Count <= 3 72 | ? new Version(nums[0], nums[1], nums[2], 0) 73 | : new Version(nums[0], nums[1], nums[2], nums[3]); 74 | } 75 | 76 | private static string GetVersionNumbers(string version) 77 | { 78 | string allowedChars = "01234567890."; 79 | return new string(version.Where(allowedChars.Contains).ToArray()); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/UMCLauncher.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | preview 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher.Core/WeakEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace UMCLauncher.Core 6 | { 7 | public class WeakEvent 8 | { 9 | private class Method 10 | { 11 | private readonly bool _isStatic; 12 | private readonly WeakReference _reference; 13 | private readonly MethodInfo _method; 14 | 15 | public bool IsDead => !(_isStatic || _reference.IsAlive); 16 | 17 | public Method(Action callback) 18 | { 19 | _isStatic = callback.Target == null; 20 | _reference = new WeakReference(callback.Target); 21 | _method = callback.Method; 22 | } 23 | 24 | public bool Equals(Action callback) => _reference.Target == callback.Target && _method == callback.Method; 25 | 26 | public void Invoke(object arg) => _method.Invoke(_reference.Target, new object[] { arg }); 27 | } 28 | 29 | private readonly List _list = new List(); 30 | 31 | public int Count => _list.Count; 32 | 33 | public void Add(Action callback) => _list.Add(new Method(callback)); 34 | 35 | public void Remove(Action callback) 36 | { 37 | for (int i = _list.Count - 1; i > -1; i--) 38 | { 39 | if (_list[i].Equals(callback)) 40 | { 41 | _list.RemoveAt(i); 42 | } 43 | } 44 | } 45 | 46 | public void Invoke(TEventArgs arg) 47 | { 48 | for (int i = _list.Count - 1; i > -1; i--) 49 | { 50 | if (_list[i].IsDead) 51 | { 52 | _list.RemoveAt(i); 53 | } 54 | else 55 | { 56 | _list[i].Invoke(arg); 57 | } 58 | } 59 | } 60 | 61 | public void Clear() => _list.Clear(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 641 15 | 16 | 0 17 | -4,0,12,0 18 | 19 | 20 | 0,48,0,0 21 | 1,1,0,0 22 | 8,0,0,0 23 | 56,34,0,0 24 | 25 | Transparent 26 | Transparent 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | using Microsoft.UI.Xaml.Controls; 3 | using System; 4 | using System.Text; 5 | using UMCLauncher.Helpers; 6 | using UMCLauncher.Helpers.Exceptions; 7 | using UMCLauncher.Pages; 8 | 9 | // To learn more about WinUI, the WinUI project structure, 10 | // and more about our project templates, see: http://aka.ms/winui-project-info. 11 | 12 | namespace UMCLauncher 13 | { 14 | /// 15 | /// Provides application-specific behavior to supplement the default Application class. 16 | /// 17 | public partial class App : Application 18 | { 19 | /// 20 | /// Initializes the singleton application object. This is the first line of authored code 21 | /// executed, and as such is the logical equivalent of main() or WinMain(). 22 | /// 23 | public App() 24 | { 25 | InitializeComponent(); 26 | UnhandledException += Application_UnhandledException; 27 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 28 | } 29 | 30 | /// 31 | /// Invoked when the application is launched normally by the end user. Other entry points 32 | /// will be used such as when the application is launched to open a specific file. 33 | /// 34 | /// Details about the launch request and process. 35 | private MainWindow m_window; 36 | 37 | protected override void OnLaunched(LaunchActivatedEventArgs args) 38 | { 39 | RegisterExceptionHandlingSynchronizationContext(); 40 | m_window = new(); 41 | m_window.TrackWindow(); 42 | ThemeHelper.Initialize(); 43 | m_window.Title = "Universal-like Minecraft Launcher"; 44 | m_window.Activate(); 45 | } 46 | 47 | private void Application_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) 48 | { 49 | SettingsHelper.LogManager.GetLogger("Unhandled Exception - Application").Error(ExceptionToMessage(e.Exception), e.Exception); 50 | e.Handled = true; 51 | } 52 | 53 | private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) 54 | { 55 | if (e.ExceptionObject is Exception Exception) 56 | { 57 | SettingsHelper.LogManager.GetLogger("Unhandled Exception - CurrentDomain").Error(ExceptionToMessage(Exception), Exception); 58 | } 59 | } 60 | 61 | /// 62 | /// Should be called from OnActivated and OnLaunched 63 | /// 64 | private void RegisterExceptionHandlingSynchronizationContext() 65 | { 66 | ExceptionHandlingSynchronizationContext 67 | .Register() 68 | .UnhandledException += SynchronizationContext_UnhandledException; 69 | } 70 | 71 | private void SynchronizationContext_UnhandledException(object sender, Helpers.Exceptions.UnhandledExceptionEventArgs e) 72 | { 73 | SettingsHelper.LogManager.GetLogger("Unhandled Exception - SynchronizationContext").Error(ExceptionToMessage(e.Exception), e.Exception); 74 | e.Handled = true; 75 | } 76 | 77 | private string ExceptionToMessage(Exception ex) 78 | { 79 | StringBuilder builder = new StringBuilder(); 80 | builder.Append('\n'); 81 | if (!string.IsNullOrWhiteSpace(ex.Message)) { builder.AppendLine($"Message: {ex.Message}"); } 82 | builder.AppendLine($"HResult: {ex.HResult} (0x{Convert.ToString(ex.HResult, 16)})"); 83 | if (!string.IsNullOrWhiteSpace(ex.StackTrace)) { builder.AppendLine(ex.StackTrace); } 84 | if (!string.IsNullOrWhiteSpace(ex.HelpLink)) { builder.Append($"HelperLink: {ex.HelpLink}"); } 85 | return builder.ToString(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/StoreLogo.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wherewhere/Minecraft-Launcher/93560514855da585812c3343e50e81f141db5959/UMCLauncher/UMCLauncher/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Control/LoginDialog.xaml: -------------------------------------------------------------------------------- 1 |  18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 43 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Control/LoginDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | using Microsoft.UI.Xaml.Input; 3 | using UMCLauncher.Helpers; 4 | using UMCLauncher.Pages; 5 | 6 | // To learn more about WinUI, the WinUI project structure, 7 | // and more about our project templates, see: http://aka.ms/winui-project-info. 8 | 9 | namespace UMCLauncher.Control 10 | { 11 | public sealed partial class LoginDialog : ContentDialog 12 | { 13 | public LoginDialog() 14 | { 15 | InitializeComponent(); 16 | } 17 | 18 | private void TextBox_KeyDown(object sender, KeyRoutedEventArgs e) 19 | { 20 | if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrEmpty(Username.Text) && ((bool)IsOffline.IsChecked || !string.IsNullOrEmpty(Password.Password))) 21 | { 22 | ContentDialog_PrimaryButtonClick(sender as ContentDialog, null); 23 | } 24 | } 25 | 26 | private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) 27 | { 28 | UIHelper.Navigate(typeof(BrowserPage), new object[] { true }); 29 | } 30 | 31 | private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) 32 | { 33 | CheckLogin(); 34 | } 35 | 36 | private async void CheckLogin() 37 | { 38 | UIHelper.MainPage.AppTitle.Text = "正在登录..."; 39 | if ((bool)IsOffline.IsChecked ? await SettingsHelper.CheckLogin(AuthenticatorType.OfflineAuthenticator, new object[] { Username.Text }) : await SettingsHelper.CheckLogin(AuthenticatorType.MojangAuthenticator, new object[] { Username.Text, Password.Password })) 40 | { 41 | UIHelper.ShowMessage("登录成功", InfoBarSeverity.Success); 42 | UIHelper.MainPage.HelloWorld(); 43 | UIHelper.HideProgressBar(); 44 | } 45 | else 46 | { 47 | UIHelper.ShowMessage("登录失败", InfoBarSeverity.Error); 48 | UIHelper.ErrorProgressBar(); 49 | UIHelper.MainPage.AppTitle.Text = UIHelper.AppTitle; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Control/PageHeader.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 43 | 47 | 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 75 | 79 | 80 | 81 | 82 | 83 | 84 | 92 | 93 | 94 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Control/PageHeader.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Composition; 2 | using Microsoft.UI.Xaml; 3 | using Microsoft.UI.Xaml.Controls; 4 | using Microsoft.UI.Xaml.Hosting; 5 | using System; 6 | using Windows.UI; 7 | 8 | // To learn more about WinUI, the WinUI project structure, 9 | // and more about our project templates, see: http://aka.ms/winui-project-info. 10 | 11 | namespace UMCLauncher.Control 12 | { 13 | public sealed partial class PageHeader : UserControl 14 | { 15 | public Action CopyLinkAction { get; set; } 16 | public Action ToggleThemeAction { get; set; } 17 | 18 | public object Title 19 | { 20 | get => GetValue(TitleProperty); 21 | set => SetValue(TitleProperty, value); 22 | } 23 | 24 | public static readonly DependencyProperty TitleProperty = 25 | DependencyProperty.Register("Title", typeof(object), typeof(PageHeader), new PropertyMetadata(null)); 26 | 27 | 28 | public object Subtitle 29 | { 30 | get => GetValue(SubtitleProperty); 31 | set => SetValue(SubtitleProperty, value); 32 | } 33 | 34 | public static readonly DependencyProperty SubtitleProperty = 35 | DependencyProperty.Register("Subtitle", typeof(object), typeof(PageHeader), new PropertyMetadata(null)); 36 | 37 | 38 | 39 | public Thickness HeaderPadding 40 | { 41 | get => (Thickness)GetValue(HeaderPaddingProperty); 42 | set => SetValue(HeaderPaddingProperty, value); 43 | } 44 | 45 | // Using a DependencyProperty as the backing store for BackgroundColorOpacity. This enables animation, styling, binding, etc... 46 | public static readonly DependencyProperty HeaderPaddingProperty = 47 | DependencyProperty.Register("HeaderPadding", typeof(Thickness), typeof(PageHeader), new PropertyMetadata((Thickness)App.Current.Resources["PageHeaderDefaultPadding"])); 48 | 49 | 50 | public double BackgroundColorOpacity 51 | { 52 | get => (double)GetValue(BackgroundColorOpacityProperty); 53 | set => SetValue(BackgroundColorOpacityProperty, value); 54 | } 55 | 56 | // Using a DependencyProperty as the backing store for BackgroundColorOpacity. This enables animation, styling, binding, etc... 57 | public static readonly DependencyProperty BackgroundColorOpacityProperty = 58 | DependencyProperty.Register("BackgroundColorOpacity", typeof(double), typeof(PageHeader), new PropertyMetadata(0.0)); 59 | 60 | 61 | public double AcrylicOpacity 62 | { 63 | get => (double)GetValue(AcrylicOpacityProperty); 64 | set => SetValue(AcrylicOpacityProperty, value); 65 | } 66 | 67 | // Using a DependencyProperty as the backing store for BackgroundColorOpacity. This enables animation, styling, binding, etc... 68 | public static readonly DependencyProperty AcrylicOpacityProperty = 69 | DependencyProperty.Register("AcrylicOpacity", typeof(double), typeof(PageHeader), new PropertyMetadata(0.3)); 70 | 71 | public double ShadowOpacity 72 | { 73 | get => (double)GetValue(ShadowOpacityProperty); 74 | set => SetValue(ShadowOpacityProperty, value); 75 | } 76 | 77 | // Using a DependencyProperty as the backing store for BackgroundColorOpacity. This enables animation, styling, binding, etc... 78 | public static readonly DependencyProperty ShadowOpacityProperty = 79 | DependencyProperty.Register("ShadowOpacity", typeof(double), typeof(PageHeader), new PropertyMetadata(0.0)); 80 | 81 | public UIElement TitlePanel => PageTitle; 82 | 83 | public PageHeader() 84 | { 85 | this.InitializeComponent(); 86 | this.InitializeDropShadow(ShadowHost, TitleTextBlock.GetAlphaMask()); 87 | RectanglePointerExited(); 88 | } 89 | 90 | /// 91 | /// This method will be called when a gets unloaded. 92 | /// Put any code in here that should be done when a gets unloaded. 93 | /// 94 | /// The sender (the ItemPage) 95 | /// The of the ItemPage that was unloaded. 96 | public void Event_ItemPage_Unloaded(object sender, RoutedEventArgs e) 97 | { 98 | 99 | } 100 | 101 | private void InitializeDropShadow(UIElement shadowHost, CompositionBrush shadowTargetBrush) 102 | { 103 | Visual hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost); 104 | Compositor compositor = hostVisual.Compositor; 105 | 106 | // Create a drop shadow 107 | DropShadow dropShadow = compositor.CreateDropShadow(); 108 | dropShadow.Color = Color.FromArgb(102, 0, 0, 0); 109 | dropShadow.BlurRadius = 4.0f; 110 | // Associate the shape of the shadow with the shape of the target element 111 | dropShadow.Mask = shadowTargetBrush; 112 | 113 | // Create a Visual to hold the shadow 114 | SpriteVisual shadowVisual = compositor.CreateSpriteVisual(); 115 | shadowVisual.Shadow = dropShadow; 116 | 117 | // Add the shadow as a child of the host in the visual tree 118 | ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual); 119 | 120 | // Make sure size of shadow host and shadow visual always stay in sync 121 | ExpressionAnimation bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size"); 122 | bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual); 123 | 124 | shadowVisual.StartAnimation("Size", bindSizeAnimation); 125 | } 126 | 127 | public void RectanglePointerEntered() => EnterStoryboard.Begin(); 128 | 129 | public void RectanglePointerExited() => ExitStoryboard.Begin(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/BackdropHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Composition; 2 | using Microsoft.UI.Composition.SystemBackdrops; 3 | using Microsoft.UI.Xaml; 4 | using Windows.Foundation; 5 | using Windows.UI; 6 | using WinRT; // required to support Window.As() 7 | 8 | namespace UMCLauncher.Helpers 9 | { 10 | public enum BackdropType 11 | { 12 | Mica, 13 | MicaAlt, 14 | DesktopAcrylic, 15 | DefaultColor, 16 | } 17 | 18 | public class BackdropHelper 19 | { 20 | private readonly Window window; 21 | private readonly WindowsSystemDispatcherQueueHelper m_wsdqHelper; 22 | private MicaController m_micaController; 23 | private DesktopAcrylicController m_acrylicController; 24 | private SystemBackdropConfiguration m_configurationSource; 25 | 26 | public BackdropType? Backdrop { get; private set; } = null; 27 | public event TypedEventHandler BackdropTypeChanged; 28 | 29 | public BackdropHelper(Window window) 30 | { 31 | this.window = window; 32 | m_wsdqHelper = new WindowsSystemDispatcherQueueHelper(); 33 | m_wsdqHelper.EnsureWindowsSystemDispatcherQueueController(); 34 | } 35 | 36 | public void SetBackdrop(BackdropType type) 37 | { 38 | if (type == Backdrop) { return; } 39 | 40 | // Reset to default color. If the requested type is supported, we'll update to that. 41 | // Note: This sample completely removes any previous controller to reset to the default 42 | // state. This is done so this sample can show what is expected to be the most 43 | // common pattern of an app simply choosing one controller type which it sets at 44 | // startup. If an app wants to toggle between Mica and Acrylic it could simply 45 | // call RemoveSystemBackdropTarget() on the old controller and then setup the new 46 | // controller, reusing any existing m_configurationSource and Activated/Closed 47 | // event handlers. 48 | Backdrop = BackdropType.DefaultColor; 49 | if (m_micaController != null) 50 | { 51 | m_micaController.Dispose(); 52 | m_micaController = null; 53 | } 54 | if (m_acrylicController != null) 55 | { 56 | m_acrylicController.Dispose(); 57 | m_acrylicController = null; 58 | } 59 | window.Closed -= Window_Closed; 60 | window.Activated -= Window_Activated; 61 | ((FrameworkElement)window.Content).ActualThemeChanged -= Window_ThemeChanged; 62 | m_configurationSource = null; 63 | 64 | if (type is BackdropType.Mica or BackdropType.MicaAlt) 65 | { 66 | if (TrySetMicaBackdrop(type == BackdropType.MicaAlt ? MicaKind.BaseAlt : MicaKind.Base)) 67 | { 68 | Backdrop = type; 69 | } 70 | } 71 | if (type == BackdropType.DesktopAcrylic) 72 | { 73 | if (TrySetAcrylicBackdrop()) 74 | { 75 | Backdrop = type; 76 | } 77 | } 78 | 79 | BackdropTypeChanged?.Invoke(this, Backdrop); 80 | } 81 | 82 | private bool TrySetMicaBackdrop(MicaKind kind = MicaKind.Base) 83 | { 84 | if (MicaController.IsSupported()) 85 | { 86 | // Hooking up the policy object 87 | m_configurationSource = new SystemBackdropConfiguration(); 88 | 89 | window.Closed += Window_Closed; 90 | window.Activated += Window_Activated; 91 | ((FrameworkElement)window.Content).ActualThemeChanged += Window_ThemeChanged; 92 | 93 | // Initial configuration state. 94 | m_configurationSource.IsInputActive = true; 95 | SetConfigurationSourceTheme(); 96 | 97 | m_micaController = new MicaController { Kind = kind }; 98 | 99 | // Enable the system backdrop. 100 | // Note: Be sure to have "using WinRT;" to support the Window.As<...>() call. 101 | m_micaController.AddSystemBackdropTarget(window.As()); 102 | m_micaController.SetSystemBackdropConfiguration(m_configurationSource); 103 | return true; // succeeded 104 | } 105 | 106 | return false; // Mica is not supported on this system 107 | } 108 | 109 | private bool TrySetAcrylicBackdrop() 110 | { 111 | if (DesktopAcrylicController.IsSupported()) 112 | { 113 | // Hooking up the policy object 114 | m_configurationSource = new SystemBackdropConfiguration(); 115 | 116 | window.Closed += Window_Closed; 117 | window.Activated += Window_Activated; 118 | ((FrameworkElement)window.Content).ActualThemeChanged += Window_ThemeChanged; 119 | 120 | // Initial configuration state. 121 | m_configurationSource.IsInputActive = true; 122 | SetConfigurationSourceTheme(); 123 | 124 | Color BackgroundColor = ThemeHelper.IsDarkTheme() ? Color.FromArgb(255, 32, 32, 32) : Color.FromArgb(255, 243, 243, 243); 125 | m_acrylicController = new DesktopAcrylicController { TintColor = BackgroundColor, FallbackColor = BackgroundColor }; 126 | 127 | // Enable the system backdrop. 128 | // Note: Be sure to have "using WinRT;" to support the Window.As<...>() call. 129 | m_acrylicController.AddSystemBackdropTarget(window.As()); 130 | m_acrylicController.SetSystemBackdropConfiguration(m_configurationSource); 131 | return true; // succeeded 132 | } 133 | 134 | return false; // Acrylic is not supported on this system 135 | } 136 | 137 | private void Window_Activated(object sender, WindowActivatedEventArgs args) 138 | { 139 | m_configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated; 140 | } 141 | 142 | private void Window_Closed(object sender, WindowEventArgs args) 143 | { 144 | // Make sure any Mica/Acrylic controller is disposed so it doesn't try to 145 | // use this closed window. 146 | if (m_micaController != null) 147 | { 148 | m_micaController.Dispose(); 149 | m_micaController = null; 150 | } 151 | if (m_acrylicController != null) 152 | { 153 | m_acrylicController.Dispose(); 154 | m_acrylicController = null; 155 | } 156 | ((FrameworkElement)window.Content).ActualThemeChanged -= Window_ThemeChanged; 157 | window.Activated -= Window_Activated; 158 | m_configurationSource = null; 159 | } 160 | 161 | private void Window_ThemeChanged(FrameworkElement sender, object args) 162 | { 163 | if (m_configurationSource != null) 164 | { 165 | SetConfigurationSourceTheme(); 166 | } 167 | if (m_acrylicController != null) 168 | { 169 | Color BackgroundColor = ThemeHelper.IsDarkTheme(sender.ActualTheme) ? Color.FromArgb(255, 32, 32, 32) : Color.FromArgb(255, 243, 243, 243); 170 | m_acrylicController.TintColor = m_acrylicController.FallbackColor = BackgroundColor; 171 | } 172 | } 173 | 174 | private void SetConfigurationSourceTheme() 175 | { 176 | switch (((FrameworkElement)window.Content).ActualTheme) 177 | { 178 | case ElementTheme.Dark: m_configurationSource.Theme = SystemBackdropTheme.Dark; break; 179 | case ElementTheme.Light: m_configurationSource.Theme = SystemBackdropTheme.Light; break; 180 | case ElementTheme.Default: m_configurationSource.Theme = SystemBackdropTheme.Default; break; 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/DataHelper/DataSourceBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace UMCLauncher.Helpers.DataHelper 7 | { 8 | public delegate void OnDataRequestError(int code); 9 | 10 | /// 11 | /// Datasource base for UMCL that enabled incremental loading (page based).
12 | /// Clone from 13 | ///
14 | public abstract class DataSourceBase : IncrementalLoadingBase 15 | { 16 | /// 17 | /// The refresh will clear current items, and re-fetch from beginning, so that we will keep a correct page number. 18 | /// 19 | public virtual async Task Refresh() 20 | { 21 | //reset 22 | _currentPage = 1; 23 | _hasMoreItems = true; 24 | 25 | Clear(); 26 | _ = await LoadMoreItemsAsync(20); 27 | } 28 | 29 | protected DateTime _lastTime = DateTime.MinValue; 30 | 31 | protected virtual bool IsInTime() 32 | { 33 | TimeSpan delta = DateTime.Now - _lastTime; 34 | _lastTime = DateTime.Now; 35 | return delta.TotalMilliseconds < 500; 36 | } 37 | 38 | /// 39 | /// Special for UMCL, as their items are paged. 40 | /// 41 | protected override async Task> LoadMoreItemsOverrideAsync(CancellationToken c, uint count) 42 | { 43 | if (IsInTime()) 44 | { 45 | return null; 46 | } 47 | 48 | IList newItems = await LoadItemsAsync(count); 49 | 50 | // Update page state. 51 | if (newItems != null) 52 | { 53 | _currentPage++; 54 | } 55 | 56 | _hasMoreItems = newItems != null && newItems.Count > 0; 57 | 58 | return newItems; 59 | } 60 | 61 | protected void FireErrorEvent(int code) 62 | { 63 | DataRequestError?.Invoke(code); 64 | } 65 | 66 | public event OnDataRequestError DataRequestError; 67 | 68 | protected override bool HasMoreItemsOverride() => _hasMoreItems; 69 | 70 | protected abstract Task> LoadItemsAsync(uint count); 71 | 72 | protected int _currentPage = 1; 73 | protected bool _hasMoreItems = true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/DataHelper/IncrementalLoadingBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Windows.Foundation; 9 | 10 | namespace UMCLauncher.Helpers.DataHelper 11 | { 12 | /// 13 | /// A incremental loading class base on the data binding sample on 14 | /// 15 | /// , but using ObservableCollection to contain data and notify changes.
16 | /// If you want to use incremental loading in MVVM pattern, you can use this as a collection, 17 | /// and add a constructor with a delegate to load data, 18 | /// so that you can load different data in your view model, refer this blog for detail 19 | /// 20 | ///
21 | public abstract class IncrementalLoadingBase : ObservableCollection, ISupportIncrementalLoading 22 | { 23 | #region ISupportIncrementalLoading 24 | 25 | public bool HasMoreItems => HasMoreItemsOverride(); 26 | 27 | /// 28 | /// Load more items, this is invoked by Controls like ListView. 29 | /// 30 | /// How many new items want to load. 31 | /// Item count actually loaded. 32 | public IAsyncOperation LoadMoreItemsAsync(uint count) 33 | { 34 | if (_busy) 35 | { 36 | return Task.Run(() => new LoadMoreItemsResult { Count = 0 }).AsAsyncOperation(); 37 | } 38 | 39 | _busy = true; 40 | 41 | // We need to use AsyncInfo.Run to invoke async operation, as this method cannot return a Task. 42 | return AsyncInfo.Run((c) => LoadMoreItemsAsync(c, count)); 43 | } 44 | 45 | #endregion 46 | 47 | /// 48 | /// We use this method to load data and add to self. 49 | /// 50 | /// Cancellation Token 51 | /// How many want to load. 52 | /// Item count actually loaded. 53 | protected async Task LoadMoreItemsAsync(CancellationToken c, uint count) 54 | { 55 | try 56 | { 57 | // We are going to load more. 58 | OnLoadMoreStarted?.Invoke(count); 59 | 60 | // Data loading will different for sub-class. 61 | IList items = await LoadMoreItemsOverrideAsync(c, count); 62 | 63 | AddItems(items); 64 | 65 | // We finished loading operation. 66 | OnLoadMoreCompleted?.Invoke(items == null ? 0 : items.Count); 67 | 68 | return new LoadMoreItemsResult { Count = items == null ? 0 : (uint)items.Count }; 69 | } 70 | finally 71 | { 72 | _busy = false; 73 | } 74 | } 75 | 76 | 77 | 78 | public delegate void LoadMoreStarted(uint count); 79 | public delegate void LoadMoreCompleted(int count); 80 | 81 | public event LoadMoreStarted OnLoadMoreStarted; 82 | public event LoadMoreCompleted OnLoadMoreCompleted; 83 | 84 | #region Overridable methods 85 | /// 86 | /// Append items to list. 87 | /// 88 | protected virtual void AddItems(IList items) 89 | { 90 | if (items != null) 91 | { 92 | foreach (T item in items) 93 | { 94 | Add(item); 95 | } 96 | } 97 | } 98 | 99 | protected abstract Task> LoadMoreItemsOverrideAsync(CancellationToken c, uint count); 100 | 101 | protected abstract bool HasMoreItemsOverride(); 102 | 103 | #endregion 104 | 105 | protected bool _busy = false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/DispatcherQueueHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; // For DllImport 3 | using Windows.System; 4 | 5 | namespace UMCLauncher.Helpers 6 | { 7 | public class WindowsSystemDispatcherQueueHelper 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | private struct DispatcherQueueOptions 11 | { 12 | internal int DWSize; 13 | internal int ThreadType; 14 | internal int ApartmentType; 15 | } 16 | 17 | [DllImport("CoreMessaging.dll")] 18 | private static unsafe extern int CreateDispatcherQueueController(DispatcherQueueOptions options, IntPtr* instance); 19 | 20 | IntPtr m_dispatcherQueueController = IntPtr.Zero; 21 | public void EnsureWindowsSystemDispatcherQueueController() 22 | { 23 | if (DispatcherQueue.GetForCurrentThread() != null) 24 | { 25 | // one already exists, so we'll just use it. 26 | return; 27 | } 28 | 29 | if (m_dispatcherQueueController == IntPtr.Zero) 30 | { 31 | DispatcherQueueOptions options; 32 | options.DWSize = Marshal.SizeOf(typeof(DispatcherQueueOptions)); 33 | options.ThreadType = 2; // DQTYPE_THREAD_CURRENT 34 | options.ApartmentType = 2; // DQTAT_COM_STA 35 | 36 | unsafe 37 | { 38 | IntPtr dispatcherQueueController; 39 | _ = CreateDispatcherQueueController(options, &dispatcherQueueController); 40 | m_dispatcherQueueController = dispatcherQueueController; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/DownloadHelper.cs: -------------------------------------------------------------------------------- 1 | using ModuleLauncher.Re.Downloaders.Concrete; 2 | using ModuleLauncher.Re.Models.Downloaders; 3 | using System.Threading.Tasks; 4 | 5 | namespace UMCLauncher.Helpers 6 | { 7 | internal class DownloadHelper 8 | { 9 | public static MinecraftDownloader MinecraftDownload(DownloaderSource source) 10 | { 11 | MinecraftDownloader Downloader = new(SettingsHelper.Get(SettingsHelper.MinecraftRoot)) 12 | { 13 | Source = source 14 | }; 15 | return Downloader; 16 | } 17 | 18 | public static async Task DownloadDependencies(string Version, bool ignoreExist = false, int maxParallel = 5) 19 | { 20 | UIHelper.MainPage.AppTitle.Text = "正在下载 Assets 文件..."; 21 | await new AssetsDownloader(SettingsHelper.Get(SettingsHelper.MinecraftRoot)).DownloadParallel(Version, ignoreExist, maxParallel); 22 | UIHelper.MainPage.AppTitle.Text = "正在下载 Libraries 文件..."; 23 | await new LibrariesDownloader(SettingsHelper.Get(SettingsHelper.MinecraftRoot)).DownloadParallel(Version, ignoreExist, maxParallel); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/Exceptions/ExceptionHandling.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | using System; 3 | using System.Threading; 4 | 5 | namespace UMCLauncher.Helpers.Exceptions 6 | { 7 | /// 8 | /// Wrapper around a standard synchronization context, that catches any unhandled exceptions. 9 | /// Acts as a facade passing calls to the original SynchronizationContext 10 | /// 11 | /// 12 | /// Set this up inside your App.xaml.cs file as follows: 13 | /// 14 | /// protected override void OnActivated(IActivatedEventArgs args) 15 | /// { 16 | /// EnsureSyncContext(); 17 | /// ... 18 | /// } 19 | /// 20 | /// protected override void OnLaunched(LaunchActivatedEventArgs args) 21 | /// { 22 | /// EnsureSyncContext(); 23 | /// ... 24 | /// } 25 | /// 26 | /// private void EnsureSyncContext() 27 | /// { 28 | /// var exceptionHandlingSynchronizationContext = ExceptionHandlingSynchronizationContext.Register(); 29 | /// exceptionHandlingSynchronizationContext.UnhandledException += OnSynchronizationContextUnhandledException; 30 | /// } 31 | /// 32 | /// private void OnSynchronizationContextUnhandledException(object sender, UnhandledExceptionEventArgs args) 33 | /// { 34 | /// args.Handled = true; 35 | /// } 36 | /// 37 | /// 38 | public class ExceptionHandlingSynchronizationContext : SynchronizationContext 39 | { 40 | /// 41 | /// Registration method. Call this from OnLaunched and OnActivated inside the App.xaml.cs 42 | /// 43 | /// 44 | public static ExceptionHandlingSynchronizationContext Register() 45 | { 46 | SynchronizationContext syncContext = Current; 47 | if (syncContext == null) 48 | { 49 | throw new InvalidOperationException("Ensure a synchronization context exists before calling this method."); 50 | } 51 | 52 | if (syncContext is not ExceptionHandlingSynchronizationContext customSynchronizationContext) 53 | { 54 | customSynchronizationContext = new ExceptionHandlingSynchronizationContext(syncContext); 55 | SetSynchronizationContext(customSynchronizationContext); 56 | } 57 | 58 | return customSynchronizationContext; 59 | } 60 | 61 | /// 62 | /// Links the synchronization context to the specified frame 63 | /// and ensures that it is still in use after each navigation event 64 | /// 65 | /// 66 | /// 67 | public static ExceptionHandlingSynchronizationContext RegisterForFrame(Frame rootFrame) 68 | { 69 | if (rootFrame == null) { throw new ArgumentNullException(nameof(rootFrame)); } 70 | 71 | ExceptionHandlingSynchronizationContext synchronizationContext = Register(); 72 | 73 | rootFrame.Navigating += (sender, args) => EnsureContext(synchronizationContext); 74 | rootFrame.Loaded += (sender, args) => EnsureContext(synchronizationContext); 75 | 76 | return synchronizationContext; 77 | } 78 | 79 | private static void EnsureContext(SynchronizationContext context) 80 | { 81 | if (Current != context) { SetSynchronizationContext(context); } 82 | } 83 | 84 | 85 | private readonly SynchronizationContext _syncContext; 86 | 87 | 88 | public ExceptionHandlingSynchronizationContext(SynchronizationContext syncContext) 89 | { 90 | _syncContext = syncContext; 91 | } 92 | 93 | 94 | public override SynchronizationContext CreateCopy() 95 | { 96 | return new ExceptionHandlingSynchronizationContext(_syncContext.CreateCopy()); 97 | } 98 | 99 | 100 | public override void OperationCompleted() 101 | { 102 | _syncContext.OperationCompleted(); 103 | } 104 | 105 | 106 | public override void OperationStarted() 107 | { 108 | _syncContext.OperationStarted(); 109 | } 110 | 111 | 112 | public override void Post(SendOrPostCallback d, object state) 113 | { 114 | _syncContext.Post(WrapCallback(d), state); 115 | } 116 | 117 | 118 | public override void Send(SendOrPostCallback d, object state) 119 | { 120 | _syncContext.Send(d, state); 121 | } 122 | 123 | 124 | private SendOrPostCallback WrapCallback(SendOrPostCallback sendOrPostCallback) 125 | { 126 | return state => 127 | { 128 | try 129 | { 130 | sendOrPostCallback(state); 131 | } 132 | catch (Exception ex) 133 | { 134 | if (!HandleException(ex)) { throw; } 135 | } 136 | }; 137 | } 138 | 139 | private bool HandleException(Exception exception) 140 | { 141 | if (UnhandledException == null) { return false; } 142 | 143 | UnhandledExceptionEventArgs exWrapper = new() 144 | { 145 | Exception = exception 146 | }; 147 | 148 | UnhandledException(this, exWrapper); 149 | 150 | #if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION 151 | if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } 152 | #endif 153 | 154 | return exWrapper.Handled; 155 | } 156 | 157 | 158 | /// 159 | /// Listen to this event to catch any unhandled exceptions and allow for handling them 160 | /// so they don't crash your application 161 | /// 162 | public event EventHandler UnhandledException; 163 | } 164 | 165 | public class UnhandledExceptionEventArgs : EventArgs 166 | { 167 | public bool Handled { get; set; } 168 | public Exception Exception { get; set; } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/LaunchHelper.cs: -------------------------------------------------------------------------------- 1 | using ModuleLauncher.Re.Launcher; 2 | using ModuleLauncher.Re.Locators; 3 | using ModuleLauncher.Re.Locators.Concretes; 4 | using ModuleLauncher.Re.Models.Locators.Minecraft; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | using UMCLauncher.Core.Helpers; 8 | using UMCLauncher.Models; 9 | 10 | namespace UMCLauncher.Helpers 11 | { 12 | internal class LaunchHelper 13 | { 14 | /// 15 | /// Java 环境列表 16 | /// 17 | public static List Javas; 18 | 19 | /// 20 | /// Minecraft 版本列表 21 | /// 22 | public static IEnumerable Minecrafts; 23 | 24 | /// 25 | /// 生成启动参数 26 | /// 27 | /// 是否为1.17以下 28 | /// 是否全屏 29 | /// 启动参数 30 | public static Launcher Launch(bool IsOld = false, bool Fullscreen = false) 31 | { 32 | SettingsHelper.GetCapacity(); 33 | Launcher launcher = new(SettingsHelper.Get(SettingsHelper.MinecraftRoot)) 34 | { 35 | Java = IsOld ? SettingsHelper.Get(SettingsHelper.Java8Root) : SettingsHelper.Get(SettingsHelper.Java16Root), 36 | Authentication = string.IsNullOrEmpty(SettingsHelper.Authentication.Name) ? "Steve" : SettingsHelper.Authentication, 37 | LauncherName = "UMCL", //optianal 38 | MaximumMemorySize = (int)(SettingsHelper.Available * 0.9 / 1048576), //optional 39 | MinimumMemorySize = null, //optional 40 | WindowHeight = (int?)(480 * MainWindow.ScalingFactor), //optional 41 | WindowWidth = (int?)(854 * MainWindow.ScalingFactor), //optional 42 | Fullscreen = Fullscreen //optional 43 | }; 44 | return launcher; 45 | } 46 | 47 | /// 48 | /// 刷新已有 Minecraft 版本 49 | /// 50 | /// 51 | public static async Task GetMinecrafts() 52 | { 53 | MinecraftLocator Locator = new(new LocalityLocator(SettingsHelper.Get(SettingsHelper.MinecraftRoot))); 54 | Minecrafts = await Locator.GetLocalMinecrafts(); 55 | } 56 | 57 | /// 58 | /// 刷新已有 Java 环境 59 | /// 60 | public static void GetJavas() 61 | { 62 | Javas = Extensions.GetJavaInstallationPath(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/SettingsHelper.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.WinUI.Helpers; 2 | using LiteDB; 3 | using MetroLog; 4 | using MetroLog.Targets; 5 | using Microsoft.UI.Xaml; 6 | using Microsoft.UI.Xaml.Media.Imaging; 7 | using ModuleLauncher.Re.Authenticators; 8 | using ModuleLauncher.Re.Models.Authenticators; 9 | using System; 10 | using System.IO; 11 | using System.Management; 12 | using System.Threading.Tasks; 13 | using Windows.Storage; 14 | using Windows.System.Profile; 15 | using IObjectSerializer = CommunityToolkit.Common.Helpers.IObjectSerializer; 16 | using JsonSerializer = System.Text.Json.JsonSerializer; 17 | 18 | namespace UMCLauncher.Helpers 19 | { 20 | internal static partial class SettingsHelper 21 | { 22 | public const string UserUID = "UserUID"; 23 | public const string Java8Root = "Java8Root"; 24 | public const string Java16Root = "Java16Root"; 25 | public const string ChooseVersion = "ChooseVersion"; 26 | public const string MinecraftRoot = "MinecraftRoot"; 27 | public const string SelectedAppTheme = "SelectedAppTheme"; 28 | public const string SelectedBackdrop = "SelectedBackdrop"; 29 | public const string ShowOtherException = "ShowOtherException"; 30 | 31 | public static Type Get(string key) => LocalObject.Read(key); 32 | public static void Set(string key, Type value) => LocalObject.Save(key, value); 33 | public static void SetFile(string key, Type value) => LocalObject.CreateFileAsync(key, value); 34 | public static async Task GetFile(string key) => await LocalObject.ReadFileAsync(key); 35 | 36 | public static void SetDefaultSettings() 37 | { 38 | if (!LocalObject.KeyExists(UserUID)) 39 | { LocalObject.Save(UserUID, string.Empty); } 40 | if (!LocalObject.KeyExists(Java8Root)) 41 | { LocalObject.Save(Java8Root, @"C:\Program Files (x86)\Minecraft Launcher\runtime\jre-x64\bin\javaw.exe"); } 42 | if (!LocalObject.KeyExists(Java16Root)) 43 | { LocalObject.Save(Java16Root, @"C:\Program Files (x86)\Minecraft Launcher\runtime\java-runtime-alpha\windows-x64\java-runtime-alpha\bin\javaw.exe"); } 44 | if (!LocalObject.KeyExists(ChooseVersion)) 45 | { LocalObject.Save(ChooseVersion, string.Empty); } 46 | if (!LocalObject.KeyExists(MinecraftRoot)) 47 | { LocalObject.Save(MinecraftRoot, @$"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\.minecraft"); } 48 | if (!LocalObject.KeyExists(SelectedAppTheme)) 49 | { LocalObject.Save(SelectedAppTheme, ElementTheme.Default); } 50 | if (!LocalObject.KeyExists(SelectedBackdrop)) 51 | { LocalObject.Save(SelectedBackdrop, BackdropType.Mica); } 52 | if (!LocalObject.KeyExists(ShowOtherException)) 53 | { LocalObject.Save(ShowOtherException, true); } 54 | } 55 | } 56 | 57 | internal enum AuthenticatorType 58 | { 59 | MicrosoftAuthenticator, 60 | OfflineAuthenticator, 61 | MojangAuthenticator, 62 | AuthenticateResult 63 | } 64 | 65 | internal static partial class SettingsHelper 66 | { 67 | public static double Capacity, Available; 68 | public static ulong version = ulong.Parse(AnalyticsInfo.VersionInfo.DeviceFamilyVersion); 69 | public static string AccountPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, "Accounts.db"); 70 | public static readonly ILogManager LogManager = LogManagerFactory.CreateLogManager(GetDefaultReleaseConfiguration()); 71 | private static readonly ApplicationDataStorageHelper LocalObject = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonObjectSerializer()); 72 | public static double WindowsVersion = double.Parse($"{(ushort)((version & 0x00000000FFFF0000L) >> 16)}.{(ushort)(version & 0x000000000000FFFFL)}"); 73 | 74 | private static AuthenticateResult authentication; 75 | public static AuthenticateResult Authentication 76 | { 77 | get => authentication; 78 | set 79 | { 80 | UIHelper.MainPage.UserNames = value.Name; 81 | UIHelper.MainPage.UserAvatar = new BitmapImage(new Uri("https://crafatar.com/renders/head/" + value.Uuid)); 82 | authentication = value; 83 | } 84 | } 85 | 86 | static SettingsHelper() 87 | { 88 | SetDefaultSettings(); 89 | } 90 | 91 | private static LoggingConfiguration GetDefaultReleaseConfiguration() 92 | { 93 | string path = Path.Combine(ApplicationData.Current.LocalFolder.Path, "MetroLogs"); 94 | if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } 95 | LoggingConfiguration loggingConfiguration = new(); 96 | loggingConfiguration.AddTarget(LogLevel.Info, LogLevel.Fatal, new StreamingFileTarget(path, 7)); 97 | return loggingConfiguration; 98 | } 99 | 100 | public static async Task CheckLogin(AuthenticatorType Type = AuthenticatorType.AuthenticateResult, object[] vs = null) 101 | { 102 | switch (Type) 103 | { 104 | case AuthenticatorType.MojangAuthenticator: 105 | if (vs != null && vs[0] is string && vs[1] is string) 106 | { 107 | #pragma warning disable CS0618 // 类型或成员已过时 108 | MojangAuthenticator Authenticator = new(vs[0] as string, vs[1] as string); 109 | #pragma warning restore CS0618 // 类型或成员已过时 110 | Authentication = await Authenticator.Authenticate(); 111 | if (!string.IsNullOrEmpty(Authentication.Uuid)) 112 | { 113 | using (LiteDatabase db = new(AccountPath)) 114 | { 115 | ILiteCollection AuthenticateResults = db.GetCollection(); 116 | _ = AuthenticateResults.Upsert(Authentication.Uuid, Authentication); 117 | Set(UserUID, Authentication.Uuid); 118 | } 119 | return true; 120 | } 121 | } 122 | break; 123 | 124 | case AuthenticatorType.OfflineAuthenticator: 125 | if (vs != null && vs[0] is string) 126 | { 127 | OfflineAuthenticator Authenticator = new(vs[0] as string); 128 | Authentication = await Authenticator.Authenticate(); 129 | if (!string.IsNullOrEmpty(Authentication.Uuid)) 130 | { 131 | using (LiteDatabase db = new(AccountPath)) 132 | { 133 | ILiteCollection AuthenticateResults = db.GetCollection(); 134 | _ = AuthenticateResults.Upsert(Authentication.Uuid, Authentication); 135 | Set(UserUID, Authentication.Uuid); 136 | } 137 | return true; 138 | } 139 | } 140 | break; 141 | 142 | case AuthenticatorType.MicrosoftAuthenticator: 143 | if (vs != null && vs[0] is string) 144 | { 145 | MicrosoftAuthenticator Authenticator = new(vs[0] as string); 146 | Authentication = await Authenticator.Authenticate(); 147 | if (!string.IsNullOrEmpty(Authentication.Uuid)) 148 | { 149 | using (LiteDatabase db = new(AccountPath)) 150 | { 151 | ILiteCollection AuthenticateResults = db.GetCollection(); 152 | _ = AuthenticateResults.Upsert(Authentication.Uuid, Authentication); 153 | Set(UserUID, Authentication.Uuid); 154 | } 155 | return true; 156 | } 157 | } 158 | break; 159 | 160 | default: 161 | if (!string.IsNullOrEmpty(Get(UserUID))) 162 | { 163 | using LiteDatabase db = new(AccountPath); 164 | ILiteCollection AuthenticateResults = db.GetCollection(); 165 | Authentication = AuthenticateResults.FindById(Get(UserUID)); 166 | return true; 167 | } 168 | break; 169 | } 170 | UIHelper.MainPage.UserNames = "登录"; 171 | return false; 172 | } 173 | 174 | public static void GetCapacity() 175 | { 176 | try 177 | { 178 | ManagementClass cimobject1 = new("Win32_ComputerSystem"); 179 | ManagementObjectCollection moc1 = cimobject1.GetInstances(); 180 | Capacity = 0; 181 | foreach (ManagementBaseObject mo1 in moc1) 182 | { 183 | Capacity += long.Parse(mo1.Properties["TotalPhysicalMemory"].Value.ToString()); 184 | } 185 | moc1.Dispose(); 186 | cimobject1.Dispose(); 187 | } 188 | catch { } 189 | } 190 | 191 | public static void GetAvailable() 192 | { 193 | try 194 | { 195 | ManagementClass cimobject2 = new("Win32_OperatingSystem"); 196 | ManagementObjectCollection moc2 = cimobject2.GetInstances(); 197 | Available = 0; 198 | foreach (ManagementBaseObject mo2 in moc2) 199 | { 200 | Available += long.Parse(mo2.Properties["FreePhysicalMemory"].Value.ToString()); 201 | } 202 | Available *= 1024; 203 | moc2.Dispose(); 204 | cimobject2.Dispose(); 205 | } 206 | catch { } 207 | } 208 | } 209 | 210 | public class SystemTextJsonObjectSerializer : IObjectSerializer 211 | { 212 | string IObjectSerializer.Serialize(T value) => JsonSerializer.Serialize(value); 213 | 214 | public T Deserialize(string value) => JsonSerializer.Deserialize(value); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/ThemeHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI; 2 | using Microsoft.UI.Windowing; 3 | using Microsoft.UI.Xaml; 4 | using Windows.UI; 5 | using Windows.UI.ViewManagement; 6 | 7 | namespace UMCLauncher.Helpers 8 | { 9 | /// 10 | /// Class providing functionality around switching and restoring theme settings 11 | /// 12 | public static class ThemeHelper 13 | { 14 | private static Window CurrentApplicationWindow; 15 | 16 | /// 17 | /// Gets the current actual theme of the app based on the requested theme of the 18 | /// root element, or if that value is Default, the requested theme of the Application. 19 | /// 20 | public static ElementTheme ActualTheme 21 | { 22 | get 23 | { 24 | foreach (Window window in WindowHelper.ActiveWindows) 25 | { 26 | if (window.Content is FrameworkElement rootElement) 27 | { 28 | if (rootElement.RequestedTheme != ElementTheme.Default) 29 | { 30 | return rootElement.RequestedTheme; 31 | } 32 | } 33 | } 34 | 35 | return SettingsHelper.Get(SettingsHelper.SelectedAppTheme); 36 | } 37 | } 38 | 39 | /// 40 | /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. 41 | /// 42 | public static ElementTheme RootTheme 43 | { 44 | get 45 | { 46 | foreach (Window window in WindowHelper.ActiveWindows) 47 | { 48 | if (window.Content is FrameworkElement rootElement) 49 | { 50 | return rootElement.RequestedTheme; 51 | } 52 | } 53 | 54 | return ElementTheme.Default; 55 | } 56 | set 57 | { 58 | foreach (Window window in WindowHelper.ActiveWindows) 59 | { 60 | if (window.Content is FrameworkElement rootElement) 61 | { 62 | rootElement.RequestedTheme = value; 63 | } 64 | } 65 | 66 | SettingsHelper.Set(SettingsHelper.SelectedAppTheme, value); 67 | UpdateSystemCaptionButtonColors(); 68 | } 69 | } 70 | 71 | public static void Initialize() 72 | { 73 | // Save reference as this might be null when the user is in another app 74 | CurrentApplicationWindow = UIHelper.MainWindow; 75 | RootTheme = SettingsHelper.Get(SettingsHelper.SelectedAppTheme); 76 | } 77 | 78 | public static bool IsDarkTheme() 79 | { 80 | return RootTheme == ElementTheme.Default 81 | ? Application.Current.RequestedTheme == ApplicationTheme.Dark 82 | : RootTheme == ElementTheme.Dark; 83 | } 84 | 85 | public static bool IsDarkTheme(ElementTheme ElementTheme) 86 | { 87 | return ElementTheme == ElementTheme.Default 88 | ? Application.Current.RequestedTheme == ApplicationTheme.Dark 89 | : ElementTheme == ElementTheme.Dark; 90 | } 91 | 92 | public static void UpdateSystemCaptionButtonColors() 93 | { 94 | if (!UIHelper.HasTitleBar) 95 | { 96 | bool IsHighContrast = new AccessibilitySettings().HighContrast; 97 | AppWindowTitleBar TitleBar = WindowHelper.GetAppWindowForCurrentWindow().TitleBar; 98 | 99 | Color ForegroundColor = IsDarkTheme() || IsHighContrast ? Colors.White : Colors.Black; 100 | Color BackgroundColor = IsHighContrast ? Color.FromArgb(255, 0, 0, 0) : IsDarkTheme() ? Color.FromArgb(255, 32, 32, 32) : Color.FromArgb(255, 243, 243, 243); 101 | 102 | TitleBar.ForegroundColor = TitleBar.ButtonForegroundColor = ForegroundColor; 103 | TitleBar.BackgroundColor = TitleBar.InactiveBackgroundColor = BackgroundColor; 104 | TitleBar.ButtonBackgroundColor = TitleBar.ButtonInactiveBackgroundColor = UIHelper.TitleBarExtended ? Colors.Transparent : BackgroundColor; 105 | } 106 | 107 | ResourceDictionary resources = Application.Current.Resources; 108 | resources["WindowCaptionForeground"] = IsDarkTheme() ? Colors.White : Colors.Black; 109 | 110 | if (UIHelper.HasTitleBar && (UIHelper.MainPage?.IsLoaded).Equals(true)) 111 | { 112 | TitleBarHelper.TriggerTitleBarRepaint(); 113 | } 114 | 115 | if (IsDarkTheme()) 116 | { 117 | foreach (Window window in WindowHelper.ActiveWindows) 118 | { 119 | window?.ApplyWindowDarkMode(); 120 | } 121 | } 122 | else 123 | { 124 | foreach (Window window in WindowHelper.ActiveWindows) 125 | { 126 | window?.RemoveWindowDarkMode(); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/TitleBarHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Input; 2 | using Microsoft.UI.Xaml; 3 | using Microsoft.UI.Xaml.Input; 4 | using NativeMethods.Interop; 5 | using System; 6 | using System.Runtime.InteropServices; 7 | using WinRT.Interop; 8 | 9 | namespace UMCLauncher.Helpers 10 | { 11 | public static class TitleBarHelper 12 | { 13 | public const int WA_ACTIVE = 0x01; 14 | public const int WA_INACTIVE = 0x00; 15 | 16 | public static void TriggerTitleBarRepaint() 17 | { 18 | // to trigger repaint tracking task id 38044406 19 | IntPtr hwnd = WindowNative.GetWindowHandle(UIHelper.MainWindow); 20 | IntPtr activeWindow = PInvoke.User32.GetActiveWindow(); 21 | if (hwnd == activeWindow) 22 | { 23 | _ = User32.SendMessage(hwnd, User32.WM.ACTIVATE, (IntPtr)WA_INACTIVE, IntPtr.Zero); 24 | _ = User32.SendMessage(hwnd, User32.WM.ACTIVATE, (IntPtr)WA_ACTIVE, IntPtr.Zero); 25 | } 26 | else 27 | { 28 | _ = User32.SendMessage(hwnd, User32.WM.ACTIVATE, (IntPtr)WA_ACTIVE, IntPtr.Zero); 29 | _ = User32.SendMessage(hwnd, User32.WM.ACTIVATE, (IntPtr)WA_INACTIVE, IntPtr.Zero); 30 | } 31 | 32 | } 33 | 34 | [DllImport("user32.dll")] 35 | public static extern bool ReleaseCapture(); 36 | 37 | public static bool SetTitleBar(Window window, FrameworkElement element) 38 | => WindowHelper.GetHandle(window, out IntPtr windowHandle) && SetTitleBar(windowHandle, element); 39 | 40 | public static bool SetTitleBar(IntPtr handle, FrameworkElement element) 41 | { 42 | void On_PointerReleased(object sender, PointerRoutedEventArgs e) 43 | { 44 | ReleaseCapture(); 45 | PointerPoint Point = e.GetCurrentPoint(sender as FrameworkElement); 46 | switch (Point.Properties.PointerUpdateKind) 47 | { 48 | case PointerUpdateKind.RightButtonReleased: 49 | _ = User32.SendMessage(handle, User32.WM.NCRBUTTONUP, (IntPtr)User32.WM_NCHITTEST.CAPTION, (IntPtr)0); 50 | break; 51 | } 52 | } 53 | 54 | void On_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) 55 | { 56 | ReleaseCapture(); 57 | _ = User32.SendMessage(handle, User32.WM.NCLBUTTONDBLCLK, (IntPtr)User32.WM_NCHITTEST.CAPTION, (IntPtr)0); 58 | } 59 | 60 | element.PointerReleased += On_PointerReleased; 61 | element.DoubleTapped += On_DoubleTapped; 62 | return true; 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/Trigger/DeviceFamilyTrigger.cs: -------------------------------------------------------------------------------- 1 | using Windows.System.Profile; 2 | using Microsoft.UI.Xaml; 3 | 4 | namespace UMCLauncher.Helpers.Trigger 5 | { 6 | // https://docs.microsoft.com/windows/apps/design/devices/designing-for-tv#custom-visual-state-trigger-for-xbox 7 | public class DeviceFamilyTrigger : StateTriggerBase 8 | { 9 | private bool _isNegation = false; 10 | private string _actualDeviceFamily; 11 | private string _triggerDeviceFamily; 12 | 13 | public string DeviceFamily 14 | { 15 | get => _triggerDeviceFamily; 16 | set 17 | { 18 | if (_triggerDeviceFamily != value) 19 | { 20 | _triggerDeviceFamily = value; 21 | PropertyChanged(_triggerDeviceFamily, _isNegation); 22 | } 23 | } 24 | } 25 | 26 | public bool IsNegation 27 | { 28 | get => _isNegation; 29 | set 30 | { 31 | if (_isNegation != value) 32 | { 33 | _isNegation = value; 34 | PropertyChanged(_triggerDeviceFamily, _isNegation); 35 | } 36 | } 37 | } 38 | 39 | private void PropertyChanged(string _triggerDeviceFamily, bool _isNegation) 40 | { 41 | _actualDeviceFamily = AnalyticsInfo.VersionInfo.DeviceFamily; 42 | bool istrue = _actualDeviceFamily == _triggerDeviceFamily; 43 | SetActive(_isNegation ? !istrue : istrue); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/UIHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Windowing; 2 | using Microsoft.UI.Xaml.Controls; 3 | using Microsoft.UI.Xaml.Media.Animation; 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Threading.Tasks; 7 | using UMCLauncher.Pages; 8 | 9 | namespace UMCLauncher.Helpers 10 | { 11 | internal static class UIHelper 12 | { 13 | public const string AppTitle = "Universal-like Minecraft Launcher"; 14 | 15 | public static bool HasTitleBar = !AppWindowTitleBar.IsCustomizationSupported(); 16 | public static bool TitleBarExtended => HasTitleBar ? MainWindow.ExtendsContentIntoTitleBar : WindowHelper.GetAppWindowForCurrentWindow().TitleBar.ExtendsContentIntoTitleBar; 17 | 18 | public static MainPage MainPage; 19 | public static MainWindow MainWindow; 20 | public static bool IsShowingProgressRing, IsShowingProgressBar, IsShowingMessage; 21 | private static readonly ObservableCollection<(string Message, InfoBarSeverity Severity)> MessageList = new(); 22 | 23 | public enum NavigationThemeTransition 24 | { 25 | Default, 26 | Entrance, 27 | DrillIn, 28 | Suppress 29 | } 30 | 31 | public static void Navigate(Type pageType, object e = null, NavigationThemeTransition Type = NavigationThemeTransition.Default) 32 | { 33 | _ = Type switch 34 | { 35 | NavigationThemeTransition.DrillIn => MainPage?.NavigationViewFrame.Navigate(pageType, e, new DrillInNavigationTransitionInfo()), 36 | NavigationThemeTransition.Entrance => MainPage?.NavigationViewFrame.Navigate(pageType, e, new EntranceNavigationTransitionInfo()), 37 | NavigationThemeTransition.Suppress => MainPage?.NavigationViewFrame.Navigate(pageType, e, new SuppressNavigationTransitionInfo()), 38 | NavigationThemeTransition.Default => MainPage?.NavigationViewFrame.Navigate(pageType, e), 39 | _ => MainPage?.NavigationViewFrame.Navigate(pageType, e), 40 | }; 41 | } 42 | 43 | public static void ShowProgressRing() 44 | { 45 | IsShowingProgressRing = true; 46 | MainPage.ShowProgressRing(); 47 | } 48 | 49 | public static void HideProgressRing() 50 | { 51 | IsShowingProgressRing = false; 52 | MainPage.HideProgressRing(); 53 | } 54 | 55 | public static void ShowProgressBar() 56 | { 57 | IsShowingProgressBar = true; 58 | MainPage.ShowProgressBar(); 59 | } 60 | 61 | public static void PausedProgressBar() 62 | { 63 | IsShowingProgressBar = true; 64 | MainPage.PausedProgressBar(); 65 | } 66 | 67 | public static void ErrorProgressBar() 68 | { 69 | IsShowingProgressBar = true; 70 | MainPage.ErrorProgressBar(); 71 | } 72 | 73 | public static void HideProgressBar() 74 | { 75 | IsShowingProgressBar = false; 76 | MainPage.HideProgressBar(); 77 | } 78 | 79 | public static async void ShowMessage(string message, InfoBarSeverity severity = InfoBarSeverity.Warning) 80 | { 81 | MessageList.Add((message, severity)); 82 | if (!IsShowingMessage) 83 | { 84 | IsShowingMessage = true; 85 | while (MessageList.Count > 0) 86 | { 87 | if (!string.IsNullOrEmpty(MessageList[0].Message)) 88 | { 89 | string messages = $"{MessageList[0].Message.Replace("\n", " ")}"; 90 | MainPage?.ShowMessage(messages, MessageList.Count, MessageList[0].Severity); 91 | await Task.Delay(3000); 92 | } 93 | MessageList.RemoveAt(0); 94 | if (MessageList.Count == 0) 95 | { 96 | MainPage?.PageHeader?.RectanglePointerExited(); 97 | } 98 | } 99 | IsShowingMessage = false; 100 | } 101 | } 102 | 103 | public static string GetGreetings() 104 | { 105 | string str = ""; 106 | DateTime now = DateTime.Now; 107 | int times = now.Hour; 108 | if (times is >= 0 and < 6) { str = "熬夜对身体不好哦"; } 109 | if (times is >= 6 and < 9) { str = "早安"; } 110 | if (times is >= 9 and < 11) { str = "上午好"; } 111 | if (times is >= 11 and < 13) { str = "中午好"; } 112 | if (times is >= 13 and < 17) { str = "下午好"; } 113 | if (times is >= 17 and < 19) { str = "吃过晚饭了吗"; } 114 | if (times is >= 19 and < 24) { str = "晚安"; } 115 | return str; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/ValueConverters/TimeToReadableConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | using Microsoft.UI.Xaml.Data; 3 | using System; 4 | using UMCLauncher.Core.Helpers; 5 | 6 | namespace UMCLauncher.Helpers.ValueConverters 7 | { 8 | public class TimeToReadableConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, string language) 11 | { 12 | return value is DateTime time 13 | ? DateHelper.ConvertDateTimeToReadable(time) 14 | : value is double @double 15 | ? DateHelper.ConvertUnixTimeStampToReadable(@double) 16 | : value; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Helpers/WindowHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI; 2 | using Microsoft.UI.Windowing; 3 | using Microsoft.UI.Xaml; 4 | using NativeMethods.Interop; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Runtime.InteropServices; 8 | using UMCLauncher.Core.Helpers; 9 | using WinRT.Interop; 10 | 11 | namespace UMCLauncher.Helpers 12 | { 13 | // Helpers class to allow the app to find the Window that contains an 14 | // arbitrary UIElement (GetWindowForElement). To do this, we keep track 15 | // of all active Windows. The app code must call WindowHelper.CreateWindow 16 | // rather than "new Window" so we can keep track of all the relevant 17 | // windows. In the future, we would like to support this in platform APIs. 18 | public static class WindowHelper 19 | { 20 | public static Window CreateWindow() 21 | { 22 | Window newWindow = new(); 23 | newWindow.TrackWindow(); 24 | return newWindow; 25 | } 26 | 27 | public static void TrackWindow(this Window window) 28 | { 29 | window.Closed += (sender, args) => 30 | { 31 | ActiveWindows.Remove(window); 32 | }; 33 | ActiveWindows.Add(window); 34 | } 35 | 36 | public static Window GetWindowForElement(this UIElement element) 37 | { 38 | if (element.XamlRoot != null) 39 | { 40 | foreach (Window window in ActiveWindows) 41 | { 42 | if (element.XamlRoot == window.Content.XamlRoot) 43 | { 44 | return window; 45 | } 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | public static AppWindow GetAppWindowForCurrentWindow(this Window window) 52 | { 53 | IntPtr hWnd = WindowNative.GetWindowHandle(window); 54 | WindowId myWndId = Win32Interop.GetWindowIdFromWindow(hWnd); 55 | return AppWindow.GetFromWindowId(myWndId); 56 | } 57 | 58 | public static AppWindow GetAppWindowForCurrentWindow() => UIHelper.MainWindow.GetAppWindowForCurrentWindow(); 59 | 60 | #region Window Immersive Dark Mode 61 | 62 | /// 63 | /// Tries to remove ImmersiveDarkMode effect from the . 64 | /// 65 | /// The window to which the effect is to be applied. 66 | /// if invocation of native Windows function succeeds. 67 | public static bool RemoveWindowDarkMode(this Window window) 68 | => GetHandle(window, out IntPtr windowHandle) && RemoveWindowDarkMode(windowHandle); 69 | 70 | /// 71 | /// Tries to remove ImmersiveDarkMode effect from the window handle. 72 | /// 73 | /// Window handle. 74 | /// if invocation of native Windows function succeeds. 75 | public static bool RemoveWindowDarkMode(IntPtr handle) 76 | { 77 | int pvAttribute = 0x0; // Disable 78 | Dwmapi.DWMWINDOWATTRIBUTE dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; 79 | 80 | if (!22523.IsOSVersonGreater()) 81 | { 82 | dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; 83 | } 84 | 85 | // TODO: Validate HRESULT 86 | _ = Dwmapi.DwmSetWindowAttribute( 87 | handle, 88 | dwAttribute, 89 | ref pvAttribute, 90 | Marshal.SizeOf(typeof(int))); 91 | 92 | return true; 93 | } 94 | 95 | /// 96 | /// Tries to apply ImmersiveDarkMode effect for the . 97 | /// 98 | /// The window to which the effect is to be applied. 99 | /// if invocation of native Windows function succeeds. 100 | public static bool ApplyWindowDarkMode(this Window window) 101 | => GetHandle(window, out IntPtr windowHandle) && ApplyWindowDarkMode(windowHandle); 102 | 103 | /// 104 | /// Tries to apply ImmersiveDarkMode effect for the window handle. 105 | /// 106 | /// Window handle. 107 | /// if invocation of native Windows function succeeds. 108 | public static bool ApplyWindowDarkMode(IntPtr handle) 109 | { 110 | int pvAttribute = 0x1; // Enable 111 | Dwmapi.DWMWINDOWATTRIBUTE dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; 112 | 113 | if (!22523.IsOSVersonGreater()) 114 | { 115 | dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; 116 | } 117 | 118 | // TODO: Validate HRESULT 119 | _ = Dwmapi.DwmSetWindowAttribute( 120 | handle, 121 | dwAttribute, 122 | ref pvAttribute, 123 | Marshal.SizeOf(typeof(int))); 124 | 125 | return true; 126 | } 127 | 128 | #endregion 129 | 130 | /// 131 | /// Tries to get the pointer to the window handle. 132 | /// 133 | /// 134 | /// 135 | /// if the handle is not . 136 | public static bool GetHandle(Window window, out IntPtr windowHandle) 137 | { 138 | windowHandle = WindowNative.GetWindowHandle(window); 139 | 140 | return windowHandle != IntPtr.Zero; 141 | } 142 | 143 | public static List ActiveWindows { get; } = new List(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  8 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | using PInvoke; 3 | using System; 4 | using UMCLauncher.Helpers; 5 | using UMCLauncher.Pages; 6 | using WinRT.Interop; 7 | 8 | // To learn more about WinUI, the WinUI project structure, 9 | // and more about our project templates, see: http://aka.ms/winui-project-info. 10 | 11 | namespace UMCLauncher 12 | { 13 | /// 14 | /// An empty window that can be used on its own or navigated to within a Frame. 15 | /// 16 | public sealed partial class MainWindow : Window 17 | { 18 | public static float ScalingFactor; 19 | public BackdropHelper Backdrop; 20 | 21 | public MainWindow() 22 | { 23 | InitializeComponent(); 24 | Backdrop = new BackdropHelper(this); 25 | UIHelper.MainWindow = this; 26 | MainPage MainPage = new(); 27 | Content = MainPage; 28 | SetBackdrop(); 29 | GetDPI(); 30 | } 31 | 32 | private void SetBackdrop() 33 | { 34 | BackdropType type = SettingsHelper.Get(SettingsHelper.SelectedBackdrop); 35 | Backdrop.SetBackdrop(type); 36 | } 37 | 38 | private void GetDPI() 39 | { 40 | IntPtr hwnd = WindowNative.GetWindowHandle(this); 41 | int dpi = User32.GetDpiForWindow(hwnd); 42 | ScalingFactor = (float)dpi / 96; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 13 | 14 | 15 | UMCL 16 | wherewhere 17 | Assets\StoreLogo.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Pages/BrowserPage.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Pages/BrowserPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | using Microsoft.UI.Xaml.Navigation; 3 | using Microsoft.Web.WebView2.Core; 4 | using System; 5 | using UMCLauncher.Helpers; 6 | 7 | // To learn more about WinUI, the WinUI project structure, 8 | // and more about our project templates, see: http://aka.ms/winui-project-info. 9 | 10 | namespace UMCLauncher.Pages 11 | { 12 | /// 13 | /// An empty page that can be used on its own or navigated to within a Frame. 14 | /// 15 | public sealed partial class BrowserPage : Page 16 | { 17 | private bool IsLoginPage; 18 | private readonly string LoginUrl = "https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL &redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"; 19 | public BrowserPage() => InitializeComponent(); 20 | 21 | protected override void OnNavigatedTo(NavigationEventArgs e) 22 | { 23 | base.OnNavigatedTo(e); 24 | object[] vs = e.Parameter as object[]; 25 | if (vs[0] is string && !string.IsNullOrEmpty(vs[0] as string)) 26 | { 27 | string uri = vs[0] as string; 28 | if (!uri.Contains("://")) 29 | { 30 | uri = $"http://{uri}"; 31 | } 32 | WebView.Source = new Uri(uri); 33 | } 34 | else if (vs[0] is bool boolean && boolean) 35 | { 36 | IsLoginPage = boolean; 37 | WebView.Source = new Uri(LoginUrl); 38 | } 39 | } 40 | 41 | private void WebView_NavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args) 42 | { 43 | UIHelper.ShowProgressBar(); 44 | if (IsLoginPage && args.Uri.StartsWith("https://login.live.com/oauth20_desktop.srf?code=")) 45 | { 46 | CheckLogin(args.Uri[(args.Uri.IndexOf("=") + 1)..]); 47 | } 48 | else if (args.Uri == LoginUrl) 49 | { 50 | IsLoginPage = true; 51 | } 52 | } 53 | 54 | private void WebView_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) 55 | { 56 | if (!IsLoginPage) 57 | { 58 | UIHelper.HideProgressBar(); 59 | } 60 | } 61 | 62 | private async void CheckLogin(string code) 63 | { 64 | UIHelper.MainPage.AppTitle.Text = "正在登录..."; 65 | if (!string.IsNullOrEmpty(code) && await SettingsHelper.CheckLogin(AuthenticatorType.MicrosoftAuthenticator, new object[] { code })) 66 | { 67 | if (Frame.CanGoBack) { Frame.GoBack(); } 68 | UIHelper.ShowMessage("登录成功", InfoBarSeverity.Success); 69 | UIHelper.MainPage.HelloWorld(); 70 | UIHelper.HideProgressBar(); 71 | } 72 | else 73 | { 74 | UIHelper.ShowMessage("登录失败", InfoBarSeverity.Error); 75 | UIHelper.ErrorProgressBar(); 76 | UIHelper.MainPage.AppTitle.Text = UIHelper.AppTitle; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Pages/DownloadPage.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 31 | 32 | 33 | 37 | 42 | 47 | 48 | 49 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | 90 | 94 | 95 | 96 | 97 | 98 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Pages/DownloadPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | using Microsoft.UI.Xaml.Controls; 3 | using Microsoft.UI.Xaml.Navigation; 4 | using ModuleLauncher.Re.Downloaders.Concrete; 5 | using ModuleLauncher.Re.Models.Downloaders; 6 | using ModuleLauncher.Re.Models.Downloaders.Minecraft; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | using UMCLauncher.Helpers; 12 | using UMCLauncher.Helpers.DataHelper; 13 | using Windows.Foundation; 14 | 15 | // To learn more about WinUI, the WinUI project structure, 16 | // and more about our project templates, see: http://aka.ms/winui-project-info. 17 | 18 | namespace UMCLauncher.Pages 19 | { 20 | /// 21 | /// An empty page that can be used on its own or navigated to within a Frame. 22 | /// 23 | public sealed partial class DownloadPage : Page 24 | { 25 | internal MinecraftDS MinecraftRS = new(MinecraftDownloadType.Release); 26 | internal MinecraftDS MinecraftSS = new(MinecraftDownloadType.Snapshot); 27 | internal MinecraftDS MinecraftOB = new(MinecraftDownloadType.OldBeta); 28 | internal MinecraftDS MinecraftOA = new(MinecraftDownloadType.OldAlpha); 29 | 30 | public DownloadPage() => InitializeComponent(); 31 | 32 | protected override void OnNavigatedTo(NavigationEventArgs e) 33 | { 34 | base.OnNavigatedTo(e); 35 | _ = Refresh(-2); 36 | } 37 | 38 | private async Task Refresh(int p = -1) 39 | { 40 | UIHelper.ShowProgressBar(); 41 | if (p == -2) 42 | { 43 | await MinecraftRS.Refresh(); 44 | await MinecraftSS.Refresh(); 45 | await MinecraftOB.Refresh(); 46 | await MinecraftOA.Refresh(); 47 | } 48 | else 49 | { 50 | switch (p) 51 | { 52 | case 1: await MinecraftRS.Refresh(); break; 53 | case 2: await MinecraftSS.Refresh(); break; 54 | case 3: await MinecraftOB.Refresh(); break; 55 | case 4: await MinecraftOA.Refresh(); break; 56 | default: _ = Refresh(-2); break; 57 | } 58 | } 59 | UIHelper.HideProgressBar(); 60 | } 61 | 62 | private async void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args) 63 | { 64 | using Deferral RefreshCompletionDeferral = args.GetDeferral(); 65 | switch (sender.Name) 66 | { 67 | case "RefreshRelease": await Refresh(1); break; 68 | case "RefreshSnapshot": await Refresh(2); break; 69 | case "RefreshBeta": await Refresh(3); break; 70 | case "RefreshAlpha": await Refresh(4); break; 71 | default: await Refresh(-2); break; 72 | } 73 | } 74 | 75 | private async void ListViewItem_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) 76 | { 77 | FrameworkElement element = sender as FrameworkElement; 78 | ContentDialog dialog = new() 79 | { 80 | Title = "下载", 81 | Content = $"确认下载 Minecraft {element.Tag} ?", 82 | PrimaryButtonText = "开始下载", 83 | CloseButtonText = "取消", 84 | DefaultButton = ContentDialogButton.Primary, 85 | XamlRoot = XamlRoot 86 | }; 87 | ContentDialogResult result = await dialog.ShowAsync(); 88 | if (result == ContentDialogResult.Primary) 89 | { 90 | UIHelper.ShowProgressBar(); 91 | UIHelper.MainPage.AppTitle.Text = "正在下载 Jar 文件..."; 92 | await MinecraftRS.Downloader.Download(element.Tag.ToString(), true); 93 | await DownloadHelper.DownloadDependencies(element.Tag.ToString(), true); 94 | UIHelper.HideProgressBar(); 95 | } 96 | } 97 | } 98 | 99 | /// 100 | /// Provide list of Minecraft Download Versions.
101 | /// You can bind this ds to ItemSource to enable incremental loading , 102 | /// or call LoadMoreItemsAsync to load more. 103 | ///
104 | internal class MinecraftDS : DataSourceBase 105 | { 106 | private IEnumerable Minecrafts; 107 | internal MinecraftDownloader Downloader; 108 | private readonly MinecraftDownloadType _type; 109 | private DownloaderSource _source; 110 | private int _loaditems; 111 | 112 | internal MinecraftDS(MinecraftDownloadType Type = MinecraftDownloadType.Release, DownloaderSource Source = DownloaderSource.Official) 113 | { 114 | _type = Type; 115 | _source = Source; 116 | } 117 | 118 | protected override async Task> LoadItemsAsync(uint count) 119 | { 120 | Downloader = DownloadHelper.MinecraftDownload(_source); 121 | if (_currentPage == 1) 122 | { 123 | Minecrafts = (await Downloader.GetRemoteMinecrafts()).Where(x => x.Type == _type); 124 | _loaditems = 0; 125 | } 126 | if (_loaditems == Minecrafts.Count()) 127 | { 128 | return null; 129 | } 130 | else if (_loaditems + count > Minecrafts.Count()) 131 | { 132 | List results = Minecrafts.ToList().GetRange(_loaditems, Minecrafts.Count() - _loaditems); 133 | _loaditems = Minecrafts.Count(); 134 | return results; 135 | } 136 | else 137 | { 138 | List results = Minecrafts.ToList().GetRange(_loaditems, (int)count); 139 | _loaditems += (int)count; 140 | return results; 141 | } 142 | } 143 | 144 | protected override void AddItems(IList items) 145 | { 146 | if (items != null) 147 | { 148 | foreach (MinecraftDownloadItem news in items) 149 | { 150 | if (!this.Any(n => n.Id == news.Id)) 151 | { 152 | Add(news); 153 | } 154 | } 155 | } 156 | } 157 | 158 | public void ChangeChannel(DownloaderSource Source) 159 | { 160 | _source = Source; 161 | _ = Refresh(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /UMCLauncher/UMCLauncher/Pages/HomePage.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 |