Skip to content

Commit

Permalink
初步实现了微软账户的皮肤更换
Browse files Browse the repository at this point in the history
  • Loading branch information
natsurainko committed Jul 18, 2024
1 parent 427737f commit 16b2c51
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 287 deletions.
2 changes: 1 addition & 1 deletion Natsurainko.FluentCore
Binary file removed Natsurainko.FluentLauncher/Assets/skin_steve.png
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
<None Remove="Assets\MinecraftTen.ttf" />
<None Remove="Assets\Rig_alex.obj" />
<None Remove="Assets\Rig_steve.obj" />
<None Remove="Assets\skin_steve.png" />
<Content Remove="Assets\Libs\authlib-injector-1.2.5.jar" />
<Content Remove="Assets\PackageIcons\SplashScreen.scale-100.scale-100.png" />
<Content Remove="Assets\PackageIcons\SplashScreen.scale-100.scale-125.png" />
Expand Down
33 changes: 4 additions & 29 deletions Natsurainko.FluentLauncher/Services/Storage/CacheSkinService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
using Nrk.FluentCore.Management.Downloader.Data;
using Nrk.FluentCore.Utils;
using System;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Windows.Storage;

namespace Natsurainko.FluentLauncher.Services.Storage;

internal class CacheSkinService
internal class CacheSkinService
{
private readonly LocalStorageService _localStorageService;

Expand All @@ -38,31 +34,10 @@ public async Task<bool> CacheSkinOfAccount(Account account)
var skinUrl = string.Empty;

if (account is YggdrasilAccount yggdrasil)
{
using var responseMessage = HttpUtils.HttpGet(
yggdrasil.YggdrasilServerUrl +
"/sessionserver/session/minecraft/profile/" +
yggdrasil.Uuid.ToString("N").ToLower()
, authorization);

responseMessage.EnsureSuccessStatusCode();

var jsonBase64 = JsonNode.Parse(responseMessage.Content.ReadAsString())!["properties"]![0]!["value"]!;
var json = JsonNode.Parse(jsonBase64.GetValue<string>().ConvertFromBase64());

skinUrl = json!["textures"]?["SKIN"]?["url"]?.GetValue<string>();
}
skinUrl = await SkinHelper.GetSkinUrlAsync(yggdrasil);

if (account is MicrosoftAccount microsoft)
{
using var responseMessage = HttpUtils.HttpGet("https://api.minecraftservices.com/minecraft/profile", authorization);
responseMessage.EnsureSuccessStatusCode();

var json = JsonNode.Parse(responseMessage.Content.ReadAsString())!["skins"]!
.AsArray().Where(item => (item!["state"]?.GetValue<string>().Equals("ACTIVE")).GetValueOrDefault()).FirstOrDefault();

skinUrl = json!["url"]!.GetValue<string>();
}
skinUrl = await SkinHelper.GetSkinUrlAsync(microsoft);

var skinFilePath = GetSkinFilePath(account);
if (!string.IsNullOrEmpty(skinUrl))
Expand All @@ -76,7 +51,7 @@ public async Task<bool> CacheSkinOfAccount(Account account)
if (downloadResult.IsFaulted)
return false;
}
else File.Copy((await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/skin_steve.png"))).Path, skinFilePath, true);
else return false;

WeakReferenceMessenger.Default.Send(new AccountSkinCacheUpdatedMessage(account));

Expand Down
102 changes: 0 additions & 102 deletions Natsurainko.FluentLauncher/ViewModels/Common/SkinManageViewModel.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Win32;
using Natsurainko.FluentLauncher.Services.Storage;
using Natsurainko.FluentLauncher.Utils;
using Nrk.FluentCore.Authentication;
using Nrk.FluentCore.Utils;
using System;
using System.IO;
using System.Threading.Tasks;

#nullable disable
namespace Natsurainko.FluentLauncher.ViewModels.Common;

public partial class UploadSkinDialogViewModel : ObservableObject
{
private readonly Account _account;
private ContentDialog _dialog;

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(UploadCommand))]
private string filePath;

[ObservableProperty]
private bool isSlimModel;

public UploadSkinDialogViewModel(Account account)
{
_account = account;
}

[RelayCommand]
public void LoadEvent(object args)
{
var grid = args.As<Grid, object>().sender;
_dialog = grid.FindName("Dialog") as ContentDialog;
}

[RelayCommand(CanExecute = nameof(CanUpload))]
public async Task Upload()
{
try
{
if (_account is MicrosoftAccount microsoftAccount)
await SkinHelper.UploadSkinAsync(microsoftAccount, IsSlimModel, FilePath);
else if (_account is YggdrasilAccount yggdrasilAccount)
await SkinHelper.UploadSkinAsync(yggdrasilAccount, IsSlimModel, FilePath);

await App.GetService<CacheSkinService>().CacheSkinOfAccount(_account);
}
catch (Exception ex)
{

}

App.DispatcherQueue.TryEnqueue(_dialog.Hide);
}

[RelayCommand]
public void BrowserFile()
{
var openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = false;
openFileDialog.Filter = "Png Image File|*.png";

if (openFileDialog.ShowDialog().GetValueOrDefault(false))
FilePath = openFileDialog.FileName;
}

[RelayCommand]
public void Cancel() => _dialog.Hide();

private bool CanUpload => File.Exists(FilePath);
}
82 changes: 56 additions & 26 deletions Natsurainko.FluentLauncher/ViewModels/Settings/SkinViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentLauncher.Infra.Settings.Mvvm;
using HelixToolkit.SharpDX.Core;
using HelixToolkit.WinUI;
using Natsurainko.FluentLauncher.Services.Accounts;
using Natsurainko.FluentLauncher.Services.Settings;
using Natsurainko.FluentLauncher.Services.Storage;
using Natsurainko.FluentLauncher.ViewModels.Common;
using Natsurainko.FluentLauncher.Views.Common;
using Nrk.FluentCore.Authentication;
using Nrk.FluentCore.Utils;
using System;
Expand All @@ -16,6 +18,7 @@
using Windows.ApplicationModel;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Windows.System;

namespace Natsurainko.FluentLauncher.ViewModels.Settings;

Expand Down Expand Up @@ -47,45 +50,56 @@ public SkinViewModel(
Task.Run(LoadModel);
}

private async void LoadModel()
#region Skin 3D Model Load

public async void LoadModel()
{
var loader = new ObjReader();
var object3Ds = loader.Read(Path.Combine(Package.Current.InstalledLocation.Path, $"Assets/{(await IsSlimSkin() ? "Rig_alex.obj" : "Rig_steve.obj")}"));
try
{
var loader = new ObjReader();
var object3Ds = loader.Read(Path.Combine(Package.Current.InstalledLocation.Path, $"Assets/{(await IsSlimSkin() ? "Rig_alex.obj" : "Rig_steve.obj")}"));

#region Create Skin Texture Stream
#region Create Skin Texture Stream

using var fileStream = File.OpenRead(_cacheSkinService.GetSkinFilePath(ActiveAccount));
using var randomAccessStream = fileStream.AsRandomAccessStream();
using var fileStream = File.OpenRead(_cacheSkinService.GetSkinFilePath(ActiveAccount));
using var randomAccessStream = fileStream.AsRandomAccessStream();

var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);
var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);

var transform = new BitmapTransform
{
InterpolationMode = BitmapInterpolationMode.NearestNeighbor,
ScaledWidth = (uint)1024,
ScaledHeight = (uint)1024
};
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
var transform = new BitmapTransform
{
InterpolationMode = BitmapInterpolationMode.NearestNeighbor,
ScaledWidth = (uint)1024,
ScaledHeight = (uint)1024
};
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();

using var bmp = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);

using var bmp = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetSoftwareBitmap(bmp);
await encoder.FlushAsync();

encoder.SetSoftwareBitmap(bmp);
await encoder.FlushAsync();
#endregion

#endregion
App.DispatcherQueue.TryEnqueue(() =>
{
var material = new DiffuseMaterial();
material.DiffuseMap = TextureModel.Create(stream.AsStreamForRead());
App.DispatcherQueue.TryEnqueue(() =>
ModelGeometry.Clear();
foreach (var object3D in object3Ds)
ModelGeometry.Add(new MeshGeometryModel3D() { Material = material, Geometry = object3D.Geometry });
});
}
catch
{
var material = new DiffuseMaterial();
material.DiffuseMap = TextureModel.Create(stream.AsStreamForRead());

foreach (var object3D in object3Ds)
ModelGeometry.Add(new MeshGeometryModel3D() { Material = material, Geometry = object3D.Geometry });
});
}
}

private async Task<bool> IsSlimSkin()
public async Task<bool> IsSlimSkin()
{
var authorization = new Tuple<string, string>("Bearer", ActiveAccount.AccessToken);
var skinUrl = string.Empty;
Expand Down Expand Up @@ -118,4 +132,20 @@ private async Task<bool> IsSlimSkin()
return false;
}

#endregion

[RelayCommand]
public async Task UploadSkin()
{
await new UploadSkinDialog() { DataContext = new UploadSkinDialogViewModel(ActiveAccount) }.ShowAsync();
_ = Task.Run(LoadModel);
}

[RelayCommand]
public void NavigateToWebsite()
{
if (ActiveAccount is YggdrasilAccount yggdrasilAccount)
_ = Launcher.LaunchUriAsync(new Uri("https://" + new Uri(yggdrasilAccount.YggdrasilServerUrl).Host));
else _ = Launcher.LaunchUriAsync(new Uri("https://www.minecraft.net/msaprofile/mygames/editskin"));
}
}
Loading

0 comments on commit 16b2c51

Please sign in to comment.