├── .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 |
5 |
6 |
7 |
8 |
9 |
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 | [](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 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/HomePage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Navigation;
4 | using ModuleLauncher.Re.Launcher;
5 | using ModuleLauncher.Re.Models.Locators.Minecraft;
6 | using System;
7 | using System.Collections.ObjectModel;
8 | using System.Threading.Tasks;
9 | using UMCLauncher.Core.Helpers;
10 | using UMCLauncher.Helpers;
11 | using WinRT.Interop;
12 |
13 | // To learn more about WinUI, the WinUI project structure,
14 | // and more about our project templates, see: http://aka.ms/winui-project-info.
15 |
16 | namespace UMCLauncher.Pages
17 | {
18 | ///
19 | /// 启动页面
20 | ///
21 | public sealed partial class HomePage : Page
22 | {
23 | public HomePage()
24 | {
25 | InitializeComponent();
26 | GetDPI.Text = $"Scale: {MainWindow.ScalingFactor}";
27 | SettingsHelper.GetCapacity();
28 | SettingsHelper.GetAvailable();
29 | _ = LaunchHelper.GetMinecrafts();
30 | GetMemory.Text = $"{SettingsHelper.Available.GetSizeString()} / {SettingsHelper.Capacity.GetSizeString()}";
31 | }
32 |
33 | protected override void OnNavigatedTo(NavigationEventArgs e)
34 | {
35 | base.OnNavigatedTo(e);
36 | _ = Refresh();
37 | }
38 |
39 | private async Task Refresh()
40 | {
41 | UIHelper.ShowProgressBar();
42 | await LaunchHelper.GetMinecrafts();
43 | ObservableCollection List = new();
44 | foreach (Minecraft item in LaunchHelper.Minecrafts)
45 | {
46 | if (item.Locality.Jar.Exists)
47 | {
48 | List.Add(item.Locality.Version.Name);
49 | }
50 | }
51 | ChooseMC.ItemsSource = List;
52 | ChooseMC.SelectedValue = SettingsHelper.Get(SettingsHelper.ChooseVersion);
53 | UIHelper.HideProgressBar();
54 | }
55 |
56 | private void Button_Click(object sender, RoutedEventArgs e)
57 | {
58 | FrameworkElement element = sender as FrameworkElement;
59 | switch (element.Name)
60 | {
61 | case "Start":
62 | StartLaunch();
63 | break;
64 | default:
65 | break;
66 | }
67 | }
68 |
69 | private async void StartLaunch()
70 | {
71 | UIHelper.ShowProgressBar();
72 | Launcher launcher = LaunchHelper.Launch(false);
73 | //await DownloadHelper.DownloadDependencies(ChooseMC.SelectedValue.ToString(), true);
74 | UIHelper.MainPage.AppTitle.Text = "正在加载...";
75 | System.Diagnostics.Process Process = await launcher.Launch(ChooseMC.SelectedValue.ToString());
76 | SettingsHelper.Set(SettingsHelper.ChooseVersion, ChooseMC.SelectedValue.ToString());
77 | UIHelper.MainPage.AppTitle.Text = "正在启动...";
78 | string Title = await Process.StandardOutput.ReadLineAsync();
79 | UIHelper.HideProgressBar();
80 | while (!string.IsNullOrEmpty(Title))
81 | {
82 | UIHelper.MainPage.AppTitle.Text = Title;
83 | Title = await Process.StandardOutput.ReadLineAsync();
84 | }
85 | UIHelper.MainPage.AppTitle.Text = "已退出";
86 | await Task.Delay(3000);
87 | UIHelper.MainPage.AppTitle.Text = UIHelper.AppTitle;
88 | }
89 |
90 | private void ComboBox_TextSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
91 | {
92 |
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/ListPage.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 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/ListPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 | using Microsoft.UI.Xaml.Navigation;
3 | using ModuleLauncher.Re.Models.Locators.Minecraft;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using UMCLauncher.Helpers;
8 | using UMCLauncher.Helpers.DataHelper;
9 | using Windows.Foundation;
10 |
11 | // To learn more about WinUI, the WinUI project structure,
12 | // and more about our project templates, see: http://aka.ms/winui-project-info.
13 |
14 | namespace UMCLauncher.Pages
15 | {
16 | ///
17 | /// 列表页面
18 | ///
19 | public sealed partial class ListPage : Page
20 | {
21 | internal VersionDS VersionDS = new();
22 |
23 | public ListPage() => InitializeComponent();
24 |
25 | protected override void OnNavigatedTo(NavigationEventArgs e)
26 | {
27 | base.OnNavigatedTo(e);
28 | _ = Refresh();
29 | }
30 |
31 | private async Task Refresh(int p = -1)
32 | {
33 | UIHelper.ShowProgressBar();
34 | if (p == -2)
35 | {
36 | await VersionDS.Refresh();
37 | }
38 | else
39 | {
40 | _ = VersionDS.LoadMoreItemsAsync(20);
41 | }
42 | UIHelper.HideProgressBar();
43 | }
44 |
45 | private async void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
46 | {
47 | using Deferral RefreshCompletionDeferral = args.GetDeferral();
48 | await Refresh(-2);
49 | }
50 | }
51 |
52 | ///
53 | /// Provide list of Minecraft Versions.
54 | /// You can bind this ds to ItemSource to enable incremental loading ,
55 | /// or call LoadMoreItemsAsync to load more.
56 | ///
57 | internal class VersionDS : DataSourceBase
58 | {
59 | private int _loaditems;
60 |
61 | protected override async Task> LoadItemsAsync(uint count)
62 | {
63 | if (_currentPage == 1)
64 | {
65 | await LaunchHelper.GetMinecrafts();
66 | _loaditems = 0;
67 | }
68 | if (_loaditems == LaunchHelper.Minecrafts.Count())
69 | {
70 | return null;
71 | }
72 | else if (_loaditems + count > LaunchHelper.Minecrafts.Count())
73 | {
74 | List results = LaunchHelper.Minecrafts.ToList().GetRange(_loaditems, LaunchHelper.Minecrafts.Count() - _loaditems);
75 | _loaditems = LaunchHelper.Minecrafts.Count();
76 | return results;
77 | }
78 | else
79 | {
80 | List results = LaunchHelper.Minecrafts.ToList().GetRange(_loaditems, (int)count);
81 | _loaditems += (int)count;
82 | return results;
83 | }
84 | }
85 |
86 | protected override void AddItems(IList items)
87 | {
88 | if (items != null)
89 | {
90 | foreach (Minecraft news in items)
91 | {
92 | if (!this.Any(n => n.Locality.Json == news.Locality.Json))
93 | {
94 | Add(news);
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
34 |
41 |
42 |
43 |
54 |
55 |
59 |
63 |
67 |
68 |
69 |
70 |
71 |
79 |
83 |
84 |
85 |
86 |
87 |
88 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/MyPage.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
28 |
29 |
30 |
34 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
58 |
59 |
60 |
65 |
66 |
67 |
68 |
69 |
70 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
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 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/MyPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using LiteDB;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 | using Microsoft.UI.Xaml.Navigation;
5 | using ModuleLauncher.Re.Models.Authenticators;
6 | using UMCLauncher.Control;
7 | using UMCLauncher.Helpers;
8 | using Windows.Foundation;
9 |
10 | // To learn more about WinUI, the WinUI project structure,
11 | // and more about our project templates, see: http://aka.ms/winui-project-info.
12 |
13 | namespace UMCLauncher.Pages
14 | {
15 | ///
16 | /// 账户页面
17 | ///
18 | public sealed partial class MyPage : Page
19 | {
20 | public MyPage() => InitializeComponent();
21 |
22 | protected override void OnNavigatedTo(NavigationEventArgs e)
23 | {
24 | base.OnNavigatedTo(e);
25 | Refresh();
26 | }
27 |
28 | private void Refresh(int p = -1)
29 | {
30 | UIHelper.ShowProgressBar();
31 | if (p == -2)
32 | {
33 | _ = ScrollViewer.ChangeView(null, 0, null);
34 | }
35 | using (LiteDatabase db = new(SettingsHelper.AccountPath))
36 | {
37 | ILiteCollection AuthenticateResults = db.GetCollection();
38 | ListView.ItemsSource = AuthenticateResults.FindAll();
39 | }
40 | UIHelper.HideProgressBar();
41 | }
42 |
43 | private void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
44 | {
45 | using Deferral RefreshCompletionDeferral = args.GetDeferral();
46 | Refresh(-2);
47 | }
48 |
49 | private void ListViewItem_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
50 | {
51 | FrameworkElement element = sender as FrameworkElement;
52 | if (element.Tag.ToString() == "登录")
53 | {
54 | LoginDialog dialog = new()
55 | {
56 | XamlRoot = XamlRoot
57 | };
58 | _ = dialog.ShowAsync();
59 | }
60 | else
61 | {
62 | using LiteDatabase db = new(SettingsHelper.AccountPath);
63 | ILiteCollection AuthenticateResults = db.GetCollection();
64 | SettingsHelper.Authentication = AuthenticateResults.FindById(element.Tag.ToString());
65 | SettingsHelper.Set(SettingsHelper.UserUID, SettingsHelper.Authentication.Uuid);
66 | UIHelper.ShowMessage("切换成功", InfoBarSeverity.Success);
67 | UIHelper.MainPage.HelloWorld();
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/SettingPages/SettingPage.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
34 |
39 |
40 |
41 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
74 |
81 |
82 |
87 |
92 |
93 |
100 |
101 |
106 |
111 |
112 |
117 |
118 |
123 |
128 |
129 |
133 |
137 |
141 |
145 |
149 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/SettingPages/SettingPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Input;
4 | using PInvoke;
5 | using System;
6 | using System.Runtime.InteropServices;
7 | using UMCLauncher.Helpers;
8 | using UMCLauncher.Models;
9 | using Windows.ApplicationModel;
10 | using Windows.Storage;
11 | using Windows.Storage.Pickers;
12 | using Windows.System;
13 | using WinRT;
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.SettingPages
19 | {
20 | [ComImport, Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
21 | public interface IInitializeWithWindow
22 | {
23 | void Initialize([In] IntPtr hwnd);
24 | }
25 |
26 | ///
27 | /// 设置页面
28 | ///
29 | public sealed partial class SettingPage : Page
30 | {
31 | internal int SelectedTheme
32 | {
33 | get => 2 - (int)ThemeHelper.RootTheme;
34 | set
35 | {
36 | if (SelectedTheme != value)
37 | {
38 | ThemeHelper.RootTheme = (ElementTheme)(2 - value);
39 | }
40 | }
41 | }
42 |
43 | internal static string VersionTextBlockText
44 | {
45 | get
46 | {
47 | string ver = $"{Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}";
48 | string name = UIHelper.AppTitle;
49 | return $"{name} v{ver}";
50 | }
51 | }
52 |
53 | public SettingPage()
54 | {
55 | InitializeComponent();
56 | LaunchHelper.GetJavas();
57 | SetValue();
58 | }
59 |
60 | private void SetValue()
61 | {
62 | Java8Root.ItemsSource = Java16Root.ItemsSource = LaunchHelper.Javas;
63 | Java8Root.Text = SettingsHelper.Get(SettingsHelper.Java8Root);
64 | MCRoot.Text = SettingsHelper.Get(SettingsHelper.MinecraftRoot);
65 | Java16Root.Text = SettingsHelper.Get(SettingsHelper.Java16Root);
66 | }
67 |
68 | private static void SaveValue(string title, string value, bool isbackground = false)
69 | {
70 | SettingsHelper.Set(title, value);
71 | if (SettingsHelper.Get(title) == value)
72 | {
73 | if (!isbackground)
74 | {
75 | UIHelper.ShowMessage("保存成功", InfoBarSeverity.Success);
76 | }
77 | }
78 | else
79 | {
80 | UIHelper.ShowMessage("保存失败", InfoBarSeverity.Error);
81 | }
82 | }
83 |
84 | private async void Button_Click(object sender, RoutedEventArgs e)
85 | {
86 | FrameworkElement element = sender as FrameworkElement;
87 | switch (element.Name)
88 | {
89 | case "TestPage":
90 | _ = Frame.Navigate(typeof(TestPage));
91 | break;
92 | case "SaveMCRoot":
93 | SaveValue(SettingsHelper.MinecraftRoot, MCRoot.Text);
94 | break;
95 | case "SaveJava8Root":
96 | SaveValue(SettingsHelper.Java8Root, Java8Root.Text);
97 | break;
98 | case "SaveJava16Root":
99 | SaveValue(SettingsHelper.Java16Root, Java16Root.Text);
100 | break;
101 | case "LogFolder":
102 | _ = await Windows.System.Launcher.LaunchFolderAsync(await ApplicationData.Current.LocalFolder.CreateFolderAsync("MetroLogs", CreationCollisionOption.OpenIfExists));
103 | break;
104 | default:
105 | break;
106 | }
107 | }
108 |
109 | private async void OpenButton_Click(object sender, RoutedEventArgs e)
110 | {
111 | FrameworkElement element = sender as FrameworkElement;
112 |
113 | FileOpenPicker FileOpen = new();
114 | FolderPicker Folder = new();
115 | if (element.Name == "ChooseMCRoot")
116 | {
117 | Folder.FileTypeFilter.Add("*");
118 | Folder.SuggestedStartLocation = PickerLocationId.ComputerFolder;
119 | }
120 | else if (element.Name == "ChooseJava8Root" || element.Name == "ChooseJava16Root")
121 | {
122 | FileOpen.FileTypeFilter.Add(".exe");
123 | FileOpen.SuggestedStartLocation = PickerLocationId.ComputerFolder;
124 | }
125 |
126 | // When running on win32, FileOpenPicker needs to know the top-level hwnd via IInitializeWithWindow::Initialize.
127 | if (Window.Current == null)
128 | {
129 | IInitializeWithWindow initializeWithWindowWrapper = null;
130 | if (element.Name == "ChooseMCRoot")
131 | {
132 | initializeWithWindowWrapper = Folder.As();
133 | }
134 | else if (element.Name == "ChooseJava8Root" || element.Name == "ChooseJava16Root")
135 | {
136 | initializeWithWindowWrapper = FileOpen.As();
137 | }
138 | IntPtr hwnd = User32.GetActiveWindow();
139 | initializeWithWindowWrapper.Initialize(hwnd);
140 | }
141 |
142 | if (element.Name == "ChooseMCRoot")
143 | {
144 | StorageFolder folder = await Folder.PickSingleFolderAsync();
145 | if (folder != null)
146 | {
147 | switch (element.Name)
148 | {
149 | case "ChooseMCRoot":
150 | MCRoot.Text = folder.Path;
151 | SaveValue(SettingsHelper.MinecraftRoot, MCRoot.Text, true);
152 | break;
153 | }
154 | }
155 | }
156 | else if (element.Name == "ChooseJava8Root" || element.Name == "ChooseJava16Root")
157 | {
158 | StorageFile file = await FileOpen.PickSingleFileAsync();
159 | if (file != null)
160 | {
161 | switch (element.Name)
162 | {
163 | case "ChooseJava8Root":
164 | Java8Root.Text = file.Path;
165 | SaveValue(SettingsHelper.Java8Root, Java8Root.Text, true);
166 | break;
167 | case "ChooseJava16Root":
168 | Java16Root.Text = file.Path;
169 | SaveValue(SettingsHelper.Java16Root, Java16Root.Text, true);
170 | break;
171 | default:
172 | break;
173 | }
174 | }
175 | }
176 | }
177 |
178 | private void TextBox_KeyDown(object sender, KeyRoutedEventArgs e)
179 | {
180 | if (e.Key == VirtualKey.Enter)
181 | {
182 | FrameworkElement element = sender as FrameworkElement;
183 | switch (element.Name)
184 | {
185 | case "MCRoot":
186 | SaveValue(SettingsHelper.MinecraftRoot, MCRoot.Text);
187 | break;
188 | case "Java8Root":
189 | SaveValue(SettingsHelper.Java8Root, Java8Root.Text);
190 | break;
191 | case "Java16Root":
192 | SaveValue(SettingsHelper.Java16Root, Java16Root.Text);
193 | break;
194 | default:
195 | break;
196 | }
197 | }
198 | }
199 |
200 | private async void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
201 | {
202 | sender.Text = (args.SelectedItem as JavaVersion).JavaPath;
203 | switch (sender.Name)
204 | {
205 | case "Java8Root":
206 | if ((args.SelectedItem as JavaVersion).Version.ProductMajorPart > 9 || (args.SelectedItem as JavaVersion).Version.ProductMajorPart < 8)
207 | {
208 | ContentDialog dialog = new()
209 | {
210 | Title = "确认",
211 | Content = $"{(args.SelectedItem as JavaVersion).Version.ProductVersion}并不是 JAVA 8,是否仍然使用?",
212 | PrimaryButtonText = "继续",
213 | CloseButtonText = "算了",
214 | DefaultButton = ContentDialogButton.Primary,
215 | XamlRoot = XamlRoot
216 | };
217 | ContentDialogResult result = await dialog.ShowAsync();
218 | if (result == ContentDialogResult.Primary)
219 | {
220 | SaveValue(SettingsHelper.Java8Root, Java8Root.Text);
221 | }
222 | else
223 | {
224 | sender.Text = SettingsHelper.Get(SettingsHelper.Java8Root);
225 | }
226 | }
227 | else
228 | {
229 | SaveValue(SettingsHelper.Java8Root, Java8Root.Text, true);
230 | }
231 | break;
232 | case "Java16Root":
233 | if ((args.SelectedItem as JavaVersion).Version.ProductMajorPart < 16)
234 | {
235 | ContentDialog dialog = new()
236 | {
237 | Title = "确认",
238 | Content = $"{(args.SelectedItem as JavaVersion).Version.ProductVersion}并不是 JAVA 16,是否仍然使用?",
239 | PrimaryButtonText = "继续",
240 | CloseButtonText = "算了",
241 | DefaultButton = ContentDialogButton.Primary,
242 | XamlRoot = XamlRoot
243 | };
244 | ContentDialogResult result = await dialog.ShowAsync();
245 | if (result == ContentDialogResult.Primary)
246 | {
247 | SaveValue(SettingsHelper.Java16Root, Java16Root.Text);
248 | }
249 | else
250 | {
251 | sender.Text = SettingsHelper.Get(SettingsHelper.Java16Root);
252 | }
253 | }
254 | else
255 | {
256 | SaveValue(SettingsHelper.Java16Root, Java16Root.Text, true);
257 | }
258 | break;
259 | default:
260 | break;
261 | }
262 | }
263 | }
264 | }
265 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Pages/SettingPages/TestPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
37 |
38 |
43 |
48 |
49 |
50 |
55 |
60 |
61 |
62 |
67 |
72 |
77 |
82 |
83 |
87 |
91 |
92 |
97 |
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/SettingPages/TestPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System;
4 | using System.Threading;
5 | using UMCLauncher.Helpers;
6 | using Windows.System;
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.Pages.SettingPages
12 | {
13 | ///
14 | /// 测试页面
15 | ///
16 | public sealed partial class TestPage : Page
17 | {
18 | internal int SelectedBackdrop
19 | {
20 | get => (int)SettingsHelper.Get(SettingsHelper.SelectedBackdrop);
21 | set
22 | {
23 | if (SelectedBackdrop != value)
24 | {
25 | BackdropType type = (BackdropType)value;
26 | SettingsHelper.Set(SettingsHelper.SelectedBackdrop, type);
27 | UIHelper.MainWindow.Backdrop.SetBackdrop(type);
28 | }
29 | }
30 | }
31 | public TestPage() => InitializeComponent();
32 |
33 | private void Button_Click(object sender, RoutedEventArgs e)
34 | {
35 | FrameworkElement element = sender as FrameworkElement;
36 | switch (element.Name)
37 | {
38 | case "OpenEdge":
39 | _ = Launcher.LaunchUriAsync(new Uri(WebUrl.Text));
40 | break;
41 | case "ShowError":
42 | throw new Exception(NotifyMessage.Text);
43 | case "ShowMessage":
44 | UIHelper.ShowMessage(NotifyMessage.Text);
45 | break;
46 | case "OpenBrowser":
47 | _ = Frame.Navigate(typeof(BrowserPage), new object[] { WebUrl.Text });
48 | break;
49 | case "ShowAsyncError":
50 | Thread thread = new(() => throw new Exception(NotifyMessage.Text));
51 | thread.Start();
52 | break;
53 | case "ShowProgressBar":
54 | UIHelper.ShowProgressBar();
55 | break;
56 | case "HideProgressBar":
57 | UIHelper.HideProgressBar();
58 | break;
59 | case "ErrorProgressBar":
60 | UIHelper.ErrorProgressBar();
61 | break;
62 | case "PausedProgressBar":
63 | UIHelper.PausedProgressBar();
64 | break;
65 | case "PrograssRingState":
66 | if (UIHelper.IsShowingProgressRing)
67 | {
68 | UIHelper.HideProgressRing();
69 | }
70 | else
71 | {
72 | UIHelper.ShowProgressRing();
73 | }
74 | break;
75 | default:
76 | break;
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Properties/PublishProfiles/win10-arm64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | arm64
9 | win10-arm64
10 | bin\$(Platform)\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
11 | False
12 | True
13 | False
14 | False
15 | True
16 |
17 | $(SelfContained)
18 |
19 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Properties/PublishProfiles/win10-x64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | x64
9 | win10-x64
10 | bin\$(Platform)\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
11 | False
12 | True
13 | False
14 | False
15 | True
16 |
17 | $(SelfContained)
18 |
19 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Properties/PublishProfiles/win10-x86.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | x86
9 | win10-x86
10 | bin\$(Platform)\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
11 | False
12 | True
13 | False
14 | False
15 | True
16 |
17 | $(SelfContained)
18 |
19 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "UMCLauncher (Package)": {
4 | "commandName": "MsixPackage",
5 | "nativeDebugging": true
6 | },
7 | "UMCLauncher (Unpackaged)": {
8 | "commandName": "Project"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/Strings/zh-CN/Resources.resw:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 一个基于 WinUI 3 的 Minecraft Java 启动器
122 |
123 |
124 | UMCL
125 |
126 |
127 | 程序出现了错误……
128 |
129 |
130 | 图片加载失败
131 |
132 |
133 | 无法连接到网络
134 |
135 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/UMCLauncher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | app.manifest
6 | True
7 | zh-CN
8 | True
9 | WinExe
10 | x86;x64;arm64
11 | win10-$(Platform).pubxml
12 | UMCLauncher
13 | win10-x86;win10-x64;win10-arm64
14 | net6.0-windows10.0.22621.0
15 | 10.0.17763.0
16 | true
17 |
18 |
19 |
20 | AppPackages
21 | True
22 | 0CDF4A03E9BE9DD789894BB3C7AD3DEDECD9AB25
23 |
24 |
25 |
26 | .allowedextension
27 | embedded
28 | True
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
60 |
61 | true
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/UMCLauncher/UMCLauncher/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------