Skip to content

Commit 4a7cb2a

Browse files
committed
Allow selecting skin variant
1 parent c0b1ce7 commit 4a7cb2a

File tree

7 files changed

+126
-43
lines changed

7 files changed

+126
-43
lines changed

crates/backend/src/backend_handler.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bridge::{
55
install::{ContentDownload, ContentInstall, ContentInstallFile, ContentInstallPath, InstallTarget}, instance::{ContentSummary, ContentType}, keep_alive::KeepAlive, message::{AccountCapesResult, AccountSkinResult, BackendConfigWithPassword, EmbeddedOrRaw, LogFiles, MessageToBackend, MessageToFrontend}, meta::MetadataResult, modal_action::{ModalAction, ModalActionVisitUrl, ProgressTracker, ProgressTrackerFinishType}, safe_path::SafePath, serial::AtomicOptionSerial
66
};
77
use futures::TryFutureExt;
8-
use schema::{auxiliary::AuxiliaryContentMeta, content::ContentSource, curseforge::{CachedCurseforgeFileInfo, CurseforgeGetFilesRequest, CurseforgeGetModFilesRequest, CurseforgeModLoaderType}, minecraft_profile::MinecraftProfileResponse, modrinth::{ModrinthLoader, ModrinthSideRequirement}, version::{LaunchArgument, LaunchArgumentValue}};
8+
use schema::{auxiliary::AuxiliaryContentMeta, content::ContentSource, curseforge::{CachedCurseforgeFileInfo, CurseforgeGetFilesRequest, CurseforgeGetModFilesRequest, CurseforgeModLoaderType}, minecraft_profile::{MinecraftProfileResponse, SkinVariant}, modrinth::{ModrinthLoader, ModrinthSideRequirement}, version::{LaunchArgument, LaunchArgumentValue}};
99
use serde::{Deserialize, Serialize};
1010
use strum::IntoEnumIterator;
1111
use tokio::{io::AsyncBufReadExt, sync::{Semaphore, TryAcquireError}};
@@ -1560,9 +1560,9 @@ impl BackendState {
15601560
};
15611561

15621562
if let Some(skin) = account.active_skin() {
1563-
SkinManager::frontend_request(&backend, skin.url.clone(), result);
1563+
SkinManager::frontend_request(&backend, skin.url.clone(), skin.variant, result);
15641564
} else {
1565-
_ = result.send(AccountSkinResult::Success { skin: None });
1565+
_ = result.send(AccountSkinResult::Success { skin: None, variant: SkinVariant::Classic });
15661566
}
15671567
});
15681568
},
@@ -1573,7 +1573,7 @@ impl BackendState {
15731573
};
15741574

15751575
let variant_str = match variant {
1576-
schema::minecraft_profile::SkinVariant::Slim => "slim",
1576+
SkinVariant::Slim => "slim",
15771577
_ => "classic",
15781578
};
15791579

crates/backend/src/skin_manager.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use bridge::message::{AccountSkinResult, BridgeDataLoadState, MessageToFrontend,
44
use image::DynamicImage;
55
use parking_lot::RwLock;
66
use rustc_hash::FxHashMap;
7+
use schema::minecraft_profile::SkinVariant;
78
use tokio::sync::oneshot::Sender;
89
use uuid::Uuid;
910

@@ -34,7 +35,7 @@ impl Default for SkinManager {
3435
enum SkinEntry {
3536
Loading {
3637
accounts: Vec<Uuid>,
37-
frontend_requests: Vec<Sender<AccountSkinResult>>,
38+
frontend_requests: Vec<(SkinVariant, Sender<AccountSkinResult>)>,
3839
},
3940
Loaded {
4041
skin: Arc<[u8]>,
@@ -57,25 +58,32 @@ impl SkinManager {
5758
pub fn frontend_request(
5859
backend: &BackendState,
5960
skin_url: Arc<str>,
61+
skin_variant: SkinVariant,
6062
send: Sender<AccountSkinResult>
6163
) {
6264
{
6365
let mut skin_manager = backend.skin_manager.write();
6466
if let Some(existing) = skin_manager.skins_download.get_mut(&skin_url) {
6567
match existing {
6668
SkinEntry::Loading { frontend_requests, .. } => {
67-
frontend_requests.push(send);
69+
frontend_requests.push((skin_variant, send));
6870
},
6971
SkinEntry::Loaded { skin, .. } => {
7072
let skin = skin.clone();
7173
drop(skin_manager);
72-
_ = send.send(AccountSkinResult::Success { skin: Some(skin) });
74+
_ = send.send(AccountSkinResult::Success {
75+
skin: Some(skin),
76+
variant: skin_variant,
77+
});
7378
},
7479
SkinEntry::Failed => {}
7580
}
7681
return;
7782
}
78-
skin_manager.skins_download.insert(skin_url.clone(), SkinEntry::Loading { accounts: Vec::new(), frontend_requests: vec![send] });
83+
skin_manager.skins_download.insert(skin_url.clone(), SkinEntry::Loading {
84+
accounts: Vec::new(),
85+
frontend_requests: vec![(skin_variant, send)]
86+
});
7987
}
8088

8189
Self::download_skin(backend, skin_url);
@@ -119,8 +127,8 @@ impl SkinManager {
119127
let previous = skin_manager.write().skins_download.insert(skin_url, SkinEntry::Failed);
120128

121129
if let Some(SkinEntry::Loading { frontend_requests, .. }) = previous {
122-
for fronend_request in frontend_requests {
123-
_ = fronend_request.send(AccountSkinResult::UnableToLoadSkin);
130+
for (_, frontend_request) in frontend_requests {
131+
_ = frontend_request.send(AccountSkinResult::UnableToLoadSkin);
124132
}
125133
}
126134
}
@@ -188,8 +196,11 @@ impl SkinManager {
188196
return;
189197
};
190198

191-
for frontend_request in frontend_requests {
192-
_ = frontend_request.send(AccountSkinResult::Success { skin: Some(skin.clone()) });
199+
for (variant, frontend_request) in frontend_requests {
200+
_ = frontend_request.send(AccountSkinResult::Success {
201+
skin: Some(skin.clone()),
202+
variant,
203+
});
193204
}
194205

195206
if accounts.is_empty() {

crates/bridge/src/message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,12 @@ pub enum EmbeddedOrRaw {
411411
Raw(Arc<[u8]>),
412412
}
413413

414+
414415
#[derive(Debug, Clone)]
415416
pub enum AccountSkinResult {
416417
Success {
417418
skin: Option<Arc<[u8]>>,
419+
variant: SkinVariant,
418420
},
419421
NeedsLogin,
420422
UnableToLoadSkin,

crates/frontend/src/component/player_model.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22

33
use gpui::{App, AppContext, AvailableSpace, Bounds, Element, Entity, IntoElement, RenderImage, Size, Style, Task, px, size};
4+
use schema::minecraft_profile::SkinVariant;
45

56
pub const DEFAULT_YAW: f64 = 22.5;
67
pub const DEFAULT_PITCH: f64 = 10.5;
@@ -10,6 +11,7 @@ struct RenderedPlayerModel {
1011
image: Arc<RenderImage>,
1112
skin: Arc<[u8]>,
1213
cape: Option<Arc<[u8]>>,
14+
variant: SkinVariant,
1315
yaw: f64,
1416
pitch: f64,
1517
animation: f64,
@@ -20,6 +22,7 @@ struct RenderedPlayerModel {
2022
pub struct PlayerModelState {
2123
pub skin: Arc<[u8]>,
2224
pub cape: Option<Arc<[u8]>>,
25+
pub variant: SkinVariant,
2326
pub yaw: f64,
2427
pub pitch: f64,
2528
pub animation: f64,
@@ -28,10 +31,11 @@ pub struct PlayerModelState {
2831
}
2932

3033
impl PlayerModelState {
31-
pub fn new(cx: &mut App, skin: Arc<[u8]>) -> Entity<Self> {
34+
pub fn new(cx: &mut App, skin: Arc<[u8]>, variant: SkinVariant) -> Entity<Self> {
3235
let entity = cx.new(|_| Self {
3336
skin,
3437
cape: None,
38+
variant,
3539
yaw: DEFAULT_YAW,
3640
pitch: DEFAULT_PITCH,
3741
animation: DEFAULT_ANIMATION,
@@ -52,6 +56,7 @@ impl PlayerModelState {
5256
};
5357
if rendered.width != width || rendered.height != height || rendered.yaw != self.yaw
5458
|| rendered.pitch != self.pitch || rendered.animation != self.animation
59+
|| rendered.variant != self.variant
5560
|| !Arc::ptr_eq(&rendered.skin, &self.skin)
5661
{
5762
return true;
@@ -159,11 +164,12 @@ impl Element for PlayerModel {
159164
let yaw = state.yaw;
160165
let pitch = state.pitch;
161166
let animation = state.animation;
167+
let variant = state.variant;
162168

163169
let (send, recv) = tokio::sync::oneshot::channel();
164170

165171
cx.background_executor().spawn(async move {
166-
send.send(crate::skin_renderer::render_skin_3d(&skin, cape.as_deref(), image_width, image_height, yaw, pitch, animation))
172+
send.send(crate::skin_renderer::render_skin_3d(&skin, cape.as_deref(), variant, image_width, image_height, yaw, pitch, animation))
167173
}).detach();
168174

169175
let skin = state.skin.clone();
@@ -187,6 +193,7 @@ impl Element for PlayerModel {
187193
image: render_image,
188194
skin,
189195
cape,
196+
variant,
190197
yaw,
191198
pitch,
192199
animation,

crates/frontend/src/component/player_model_widget.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::{sync::Arc, time::Instant};
22

33
use gpui::{prelude::*, *};
4-
use gpui_component::{Sizable, button::Button, h_flex, slider::{Slider, SliderEvent, SliderState}, v_flex};
4+
use gpui_component::{Selectable, Sizable, button::Button, h_flex, slider::{Slider, SliderEvent, SliderState}, v_flex};
5+
use schema::minecraft_profile::SkinVariant;
56

67
use crate::{component::player_model::{self, PlayerModel, PlayerModelState}, icon::PandoraIcon};
78

@@ -14,6 +15,7 @@ pub struct PlayerModelWidget {
1415
animating_pitch_positive: bool,
1516
animating_pitch: bool,
1617
animating_animation: bool,
18+
variant: SkinVariant,
1719
last_drag: Option<Point<Pixels>>,
1820
last_render: Instant,
1921
}
@@ -30,35 +32,52 @@ impl PlayerModelWidget {
3032
SliderState::new().min(0.0).max(1.0).step(1.0/800.0).default_value(player_model::DEFAULT_ANIMATION as f32)
3133
});
3234

35+
let variant = crate::skin_renderer::determine_skin_variant(&skin).unwrap_or(SkinVariant::Classic);
36+
3337
cx.subscribe(&yaw_slider_state, Self::on_yaw_changed).detach();
3438
cx.subscribe(&pitch_slider_state, Self::on_pitch_changed).detach();
3539
cx.subscribe(&animation_slider_state, Self::on_animation_changed).detach();
3640

3741
Self {
38-
player_model_state: PlayerModelState::new(cx, skin),
42+
player_model_state: PlayerModelState::new(cx, skin, variant),
3943
yaw_slider_state,
4044
pitch_slider_state,
4145
animation_slider_state,
4246
animating_yaw: false,
4347
animating_pitch_positive: true,
4448
animating_pitch: false,
4549
animating_animation: false,
50+
variant,
4651
last_drag: None,
4752
last_render: Instant::now(),
4853
}
4954
}
5055

51-
pub fn set_skin(&mut self, cx: &mut App, skin: Arc<[u8]>) {
52-
self.player_model_state.as_mut(cx).skin = skin;
56+
pub fn set_skin(&mut self, cx: &mut App, skin: Arc<[u8]>, variant: SkinVariant) {
57+
self.variant = variant;
58+
let mut state = self.player_model_state.as_mut(cx);
59+
state.skin = skin;
60+
state.variant = variant;
5361
}
5462

5563
pub fn set_cape(&mut self, cx: &mut App, cape: Option<Arc<[u8]>>) {
5664
self.player_model_state.as_mut(cx).cape = cape;
5765
}
5866

59-
pub fn set_skin_and_cape(&mut self, cx: &mut App, skin: Arc<[u8]>, cape: Option<Arc<[u8]>>) {
67+
pub fn set_variant(&mut self, cx: &mut App, variant: SkinVariant) {
68+
self.variant = variant;
69+
self.player_model_state.as_mut(cx).variant = variant;
70+
}
71+
72+
pub fn get_variant(&self) -> SkinVariant {
73+
self.variant
74+
}
75+
76+
pub fn set_skin_and_cape(&mut self, cx: &mut App, skin: Arc<[u8]>, variant: SkinVariant, cape: Option<Arc<[u8]>>) {
77+
self.variant = variant;
6078
let mut state = self.player_model_state.as_mut(cx);
6179
state.skin = skin;
80+
state.variant = variant;
6281
state.cape = cape;
6382
}
6483

@@ -207,6 +226,30 @@ impl Render for PlayerModelWidget {
207226
}))
208227
)
209228
.child(v_flex().p_4().w_full()
229+
.child(h_flex()
230+
.w_full()
231+
.gap_2()
232+
.pb_2()
233+
.child(Button::new("classic")
234+
.label("Classic")
235+
.flex_1()
236+
.selected(self.variant == SkinVariant::Classic)
237+
.on_click(cx.listener(|this, _, _, cx| {
238+
this.variant = SkinVariant::Classic;
239+
this.player_model_state.update(cx, |state, _| {
240+
state.variant = SkinVariant::Classic;
241+
});
242+
})))
243+
.child(Button::new("slim")
244+
.label("Slim")
245+
.flex_1()
246+
.selected(self.variant == SkinVariant::Slim)
247+
.on_click(cx.listener(|this, _, _, cx| {
248+
this.variant = SkinVariant::Slim;
249+
this.player_model_state.update(cx, |state, _| {
250+
state.variant = SkinVariant::Slim;
251+
});
252+
}))))
210253
.child(v_flex()
211254
.child(h_flex().text_sm().gap_1()
212255
.child(format!("Yaw: {}°", yaw as i32))

0 commit comments

Comments
 (0)