Skip to content

Commit d071bf0

Browse files
committed
feat: add support for skins
1 parent e5269d3 commit d071bf0

File tree

63 files changed

+2342
-325
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2342
-325
lines changed

Cargo.lock

Lines changed: 1083 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ members = [
2727
anyhow = "1.0.99"
2828
clap = { version = "4.5.47", features = ["derive"] }
2929
flate2 = "1.1.2"
30+
futures = "0.3.31"
3031
hmac = "0.12.1"
3132
proc-macro2 = "1.0.101"
3233
quick-xml = "0.38.3"
3334
quote = "1.0.40"
3435
rand = "0.9.2"
3536
rayon = "1.11.0"
37+
reqwest = { version = "0.12.23", features = ["json"] }
3638
serde = { version = "1.0.219", features = ["derive"] }
3739
serde_json = "1.0.143"
3840
sha2 = "0.10.9"

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ in Rust, designed primarily as an AFK or waiting server. Its core focus is on ef
3131
packets required for client login and maintaining connection (keep-alive) without unnecessary overhead.
3232

3333
While not aiming to replicate every Minecraft server feature, PicoLimbo supports **all Minecraft versions from 1.7.2
34-
through 1.21.8**, excluding snapshots, with only 27 implemented packets covering over 47 different protocol versions or
34+
through 1.21.8**, excluding snapshots, with only 36 implemented packets covering over 47 different protocol versions or
3535
75 Minecraft versions.
3636

3737
## Features
@@ -46,6 +46,10 @@ layers.
4646
Uses **0% CPU while idle** and handles **hundreds of players** under 10 MB RAM.
4747
[View benchmarks](https://picolimbo.quozul.dev/about/benchmarks.html).
4848

49+
### 👤 Skin Support
50+
51+
Player skins are supported. What else to say about that?
52+
4953
### 🔀 Built-in Proxy Support
5054

5155
Integrates with all major Minecraft proxies:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod property;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use minecraft_protocol::prelude::*;
2+
3+
#[derive(PacketOut, PacketIn, Clone)]
4+
pub struct Property {
5+
name: String,
6+
value: String,
7+
signature: Optional<String>,
8+
}
9+
10+
impl Property {
11+
const TEXTURES_NAMES: &'static str = "textures";
12+
13+
pub fn new(name: String, value: String, signature: Option<String>) -> Self {
14+
Self {
15+
name,
16+
value,
17+
signature: signature.into(),
18+
}
19+
}
20+
21+
pub fn textures<S>(value: &S, signature: Option<&S>) -> Self
22+
where
23+
S: ToString + ?Sized,
24+
{
25+
let signature = signature.map(|t| t.to_string());
26+
27+
Self {
28+
name: Self::TEXTURES_NAMES.to_string(),
29+
value: value.to_string(),
30+
signature: signature.into(),
31+
}
32+
}
33+
34+
pub fn is_textures(&self) -> bool {
35+
self.name == Self::TEXTURES_NAMES
36+
}
37+
38+
pub fn value(&self) -> &str {
39+
&self.value
40+
}
41+
42+
pub fn signature(&self) -> Option<String> {
43+
self.signature.clone().into()
44+
}
45+
}

crates/minecraft_packets/src/login/game_profile_packet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::login::login_success_packet::Property;
1+
use crate::login::Property;
22
use minecraft_protocol::prelude::*;
33

44
/// This is the equivalent of LoginSuccessPacket but for versions before 1.21.2.
Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::login::Property;
12
use minecraft_protocol::prelude::*;
23

34
/// This packet was introduced in 1.21.2, previous versions uses the GameProfilePacket.
@@ -18,26 +19,3 @@ impl LoginSuccessPacket {
1819
}
1920
}
2021
}
21-
22-
pub struct Property {
23-
pub name: String,
24-
pub value: String,
25-
pub is_signed: bool,
26-
pub signature: Option<String>,
27-
}
28-
29-
impl EncodePacket for Property {
30-
fn encode(
31-
&self,
32-
writer: &mut BinaryWriter,
33-
protocol_version: ProtocolVersion,
34-
) -> Result<(), BinaryWriterError> {
35-
self.name.encode(writer, protocol_version)?;
36-
self.value.encode(writer, protocol_version)?;
37-
self.is_signed.encode(writer, protocol_version)?;
38-
if let Some(signature) = &self.signature {
39-
signature.encode(writer, protocol_version)?;
40-
}
41-
Ok(())
42-
}
43-
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
pub mod custom_query_answer_packet;
22
pub mod custom_query_packet;
3+
mod data;
34
pub mod game_profile_packet;
45
pub mod login_acknowledged_packet;
56
pub mod login_disconnect_packet;
67
pub mod login_state_packet;
78
pub mod login_success_packet;
9+
10+
pub use data::property::Property;

crates/minecraft_packets/src/play/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ pub mod game_event_packet;
77
pub mod legacy_chat_message_packet;
88
pub mod login_packet;
99
pub mod play_client_bound_plugin_message_packet;
10+
pub mod player_info_update_packet;
1011
pub mod player_position_packet;
1112
pub mod set_chunk_cache_center_packet;
1213
pub mod set_default_spawn_position_packet;
14+
pub mod set_entity_data_packet;
1315
pub mod set_player_position_and_rotation_packet;
1416
pub mod set_player_position_packet;
1517
pub mod synchronize_player_position_packet;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::login::Property;
2+
use minecraft_protocol::prelude::*;
3+
use pico_text_component::prelude::Component;
4+
5+
#[derive(PacketOut)]
6+
pub struct PlayerInfoUpdatePacket {
7+
#[pvn(..761)]
8+
action: VarInt,
9+
10+
#[pvn(761..)]
11+
v1_19_3_mask: u8,
12+
players: LengthPaddedVec<Player>,
13+
}
14+
15+
impl PlayerInfoUpdatePacket {
16+
pub fn skin(name: String, uuid: Uuid, property: Property) -> Self {
17+
let properties = LengthPaddedVec::new(vec![property]);
18+
let add_player_action = AddPlayer {
19+
name,
20+
properties,
21+
game_mode: VarInt::new(1),
22+
ping: VarInt::new(1),
23+
display_name: Optional::None,
24+
sig_data: Optional::None,
25+
};
26+
27+
let actions = vec![
28+
PlayerActions::AddPlayer(add_player_action.clone()),
29+
PlayerActions::UpdateListed { listed: true },
30+
];
31+
32+
let mut mask = 0;
33+
for action in &actions {
34+
mask |= action.get_mask();
35+
}
36+
37+
let (most_sig, least_sig) = uuid.as_u64_pair();
38+
39+
let player_action = Player {
40+
v1_16_uuid: uuid,
41+
uuid_most_sig: most_sig,
42+
uuid_least_sig: least_sig,
43+
action: add_player_action,
44+
actions,
45+
};
46+
47+
Self {
48+
action: VarInt::new(0),
49+
v1_19_3_mask: mask,
50+
players: LengthPaddedVec::new(vec![player_action]),
51+
}
52+
}
53+
}
54+
55+
#[derive(PacketOut)]
56+
struct Player {
57+
#[pvn(735..)]
58+
v1_16_uuid: Uuid,
59+
#[pvn(..735)]
60+
uuid_most_sig: u64,
61+
#[pvn(..735)]
62+
uuid_least_sig: u64,
63+
#[pvn(..761)]
64+
action: AddPlayer,
65+
#[pvn(761..)]
66+
actions: Vec<PlayerActions>,
67+
}
68+
69+
#[derive(PacketOut, Clone)]
70+
struct AddPlayer {
71+
name: String,
72+
properties: LengthPaddedVec<Property>,
73+
#[pvn(..761)]
74+
game_mode: VarInt,
75+
#[pvn(..761)]
76+
ping: VarInt,
77+
#[pvn(..761)]
78+
display_name: Optional<Component>,
79+
#[pvn(759..761)]
80+
sig_data: Optional<SigData>,
81+
}
82+
83+
#[derive(PacketOut, Clone)]
84+
struct SigData {
85+
timestamp: i64,
86+
public_key: LengthPaddedVec<i8>,
87+
signature: LengthPaddedVec<i8>,
88+
}
89+
90+
#[derive(Clone)]
91+
enum PlayerActions {
92+
AddPlayer(AddPlayer),
93+
UpdateListed { listed: bool },
94+
}
95+
96+
impl PlayerActions {
97+
fn get_mask(&self) -> u8 {
98+
match self {
99+
PlayerActions::AddPlayer { .. } => 0x01,
100+
PlayerActions::UpdateListed { .. } => 0x08,
101+
}
102+
}
103+
}
104+
105+
impl EncodePacket for PlayerActions {
106+
fn encode(
107+
&self,
108+
writer: &mut BinaryWriter,
109+
protocol_version: ProtocolVersion,
110+
) -> Result<(), BinaryWriterError> {
111+
match self {
112+
PlayerActions::AddPlayer(value) => {
113+
value.encode(writer, protocol_version)?;
114+
Ok(())
115+
}
116+
PlayerActions::UpdateListed { listed } => {
117+
listed.encode(writer, protocol_version)?;
118+
Ok(())
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)