mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Don't tamper with the texture payload
The texture payload (skin + cape URL) that we get from the auth server needs to stay intact so the signature is not invalidated. However, skins still aren't loading. Not sure what's up with that.
This commit is contained in:
parent
055dd03ffc
commit
e97df76a75
|
@ -97,6 +97,15 @@ impl Config for Game {
|
||||||
if client.created_tick() == server.current_tick() {
|
if client.created_tick() == server.current_tick() {
|
||||||
client.set_game_mode(GameMode::Creative);
|
client.set_game_mode(GameMode::Creative);
|
||||||
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
||||||
|
|
||||||
|
world.meta.player_list_mut().insert(
|
||||||
|
client.uuid(),
|
||||||
|
client.username().to_string(),
|
||||||
|
client.textures().cloned(),
|
||||||
|
client.game_mode(),
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl Config for Game {
|
||||||
|
|
||||||
fn online_mode(&self) -> bool {
|
fn online_mode(&self) -> bool {
|
||||||
// You'll want this to be true on real servers.
|
// You'll want this to be true on real servers.
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn server_list_ping(&self, _server: &Server, _remote_addr: SocketAddr) -> ServerListPing {
|
async fn server_list_ping(&self, _server: &Server, _remote_addr: SocketAddr) -> ServerListPing {
|
||||||
|
@ -105,12 +105,11 @@ impl Config for Game {
|
||||||
client.set_max_view_distance(32);
|
client.set_max_view_distance(32);
|
||||||
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
||||||
|
|
||||||
world.meta.player_list_mut().add_player(
|
world.meta.player_list_mut().insert(
|
||||||
client.uuid(),
|
client.uuid(),
|
||||||
client.username().to_string(),
|
client.username().to_string(),
|
||||||
None,
|
client.textures().cloned(),
|
||||||
None,
|
client.game_mode(),
|
||||||
GameMode::Creative,
|
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,14 +25,15 @@ use crate::packets::play::s2c::{
|
||||||
PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec, S2cPlayPacket, SpawnPosition,
|
PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec, S2cPlayPacket, SpawnPosition,
|
||||||
UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||||
};
|
};
|
||||||
|
use crate::player_textures::SignedPlayerTextures;
|
||||||
use crate::protocol::{BoundedInt, Nbt};
|
use crate::protocol::{BoundedInt, Nbt};
|
||||||
use crate::server::C2sPacketChannels;
|
use crate::server::C2sPacketChannels;
|
||||||
use crate::slotmap::{Key, SlotMap};
|
use crate::slotmap::{Key, SlotMap};
|
||||||
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
|
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
|
||||||
use crate::var_int::VarInt;
|
use crate::var_int::VarInt;
|
||||||
use crate::{
|
use crate::{
|
||||||
ident, ChunkPos, Chunks, DimensionId, Entities, EntityId, Server, SpatialIndex, Text, Ticks,
|
ident, ChunkPos, Chunks, DimensionId, Entities, EntityId, NewClientData, Server, SpatialIndex,
|
||||||
WorldMeta, LIBRARY_NAMESPACE,
|
Text, Ticks, WorldMeta, LIBRARY_NAMESPACE,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Clients {
|
pub struct Clients {
|
||||||
|
@ -120,8 +121,9 @@ pub struct Client {
|
||||||
recv: Receiver<C2sPlayPacket>,
|
recv: Receiver<C2sPlayPacket>,
|
||||||
/// The tick this client was created.
|
/// The tick this client was created.
|
||||||
created_tick: Ticks,
|
created_tick: Ticks,
|
||||||
username: String,
|
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
|
username: String,
|
||||||
|
textures: Option<SignedPlayerTextures>,
|
||||||
on_ground: bool,
|
on_ground: bool,
|
||||||
new_position: Vec3<f64>,
|
new_position: Vec3<f64>,
|
||||||
old_position: Vec3<f64>,
|
old_position: Vec3<f64>,
|
||||||
|
@ -176,9 +178,8 @@ impl<'a> Deref for ClientMut<'a> {
|
||||||
impl Client {
|
impl Client {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
packet_channels: C2sPacketChannels,
|
packet_channels: C2sPacketChannels,
|
||||||
username: String,
|
|
||||||
uuid: Uuid,
|
|
||||||
server: &Server,
|
server: &Server,
|
||||||
|
ncd: NewClientData,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (send, recv) = packet_channels;
|
let (send, recv) = packet_channels;
|
||||||
|
|
||||||
|
@ -186,8 +187,9 @@ impl Client {
|
||||||
send: Some(send),
|
send: Some(send),
|
||||||
recv,
|
recv,
|
||||||
created_tick: server.current_tick(),
|
created_tick: server.current_tick(),
|
||||||
username,
|
uuid: ncd.uuid,
|
||||||
uuid,
|
username: ncd.username,
|
||||||
|
textures: ncd.textures,
|
||||||
on_ground: false,
|
on_ground: false,
|
||||||
new_position: Vec3::default(),
|
new_position: Vec3::default(),
|
||||||
old_position: Vec3::default(),
|
old_position: Vec3::default(),
|
||||||
|
@ -218,12 +220,16 @@ impl Client {
|
||||||
self.created_tick
|
self.created_tick
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uuid(&self) -> Uuid {
|
||||||
|
self.uuid
|
||||||
|
}
|
||||||
|
|
||||||
pub fn username(&self) -> &str {
|
pub fn username(&self) -> &str {
|
||||||
&self.username
|
&self.username
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uuid(&self) -> Uuid {
|
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
||||||
self.uuid
|
self.textures.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Vec3<f64> {
|
pub fn position(&self) -> Vec3<f64> {
|
||||||
|
@ -602,6 +608,9 @@ impl<'a> ClientMut<'a> {
|
||||||
// Send the join game packet and other initial packets. We defer this until now
|
// Send the join game packet and other initial packets. We defer this until now
|
||||||
// so that the user can set the client's location, game mode, etc.
|
// so that the user can set the client's location, game mode, etc.
|
||||||
if self.created_tick == current_tick {
|
if self.created_tick == current_tick {
|
||||||
|
meta.player_list()
|
||||||
|
.initial_packets(|pkt| self.send_packet(pkt));
|
||||||
|
|
||||||
self.send_packet(JoinGame {
|
self.send_packet(JoinGame {
|
||||||
entity_id: 0, // EntityId 0 is reserved for clients.
|
entity_id: 0, // EntityId 0 is reserved for clients.
|
||||||
is_hardcore: false, // TODO
|
is_hardcore: false, // TODO
|
||||||
|
@ -621,7 +630,7 @@ impl<'a> ClientMut<'a> {
|
||||||
max_players: VarInt(0),
|
max_players: VarInt(0),
|
||||||
view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)),
|
view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)),
|
||||||
simulation_distance: VarInt(16),
|
simulation_distance: VarInt(16),
|
||||||
reduced_debug_info: false, // TODO
|
reduced_debug_info: false,
|
||||||
enable_respawn_screen: false,
|
enable_respawn_screen: false,
|
||||||
is_debug: false,
|
is_debug: false,
|
||||||
is_flat: meta.is_flat(),
|
is_flat: meta.is_flat(),
|
||||||
|
@ -630,9 +639,6 @@ impl<'a> ClientMut<'a> {
|
||||||
.map(|(id, pos)| (ident!("{LIBRARY_NAMESPACE}:dimension_{}", id.0), pos)),
|
.map(|(id, pos)| (ident!("{LIBRARY_NAMESPACE}:dimension_{}", id.0), pos)),
|
||||||
});
|
});
|
||||||
|
|
||||||
meta.player_list()
|
|
||||||
.initial_packets(|pkt| self.send_packet(pkt));
|
|
||||||
|
|
||||||
self.teleport(self.position(), self.yaw(), self.pitch());
|
self.teleport(self.position(), self.yaw(), self.pitch());
|
||||||
} else {
|
} else {
|
||||||
if self.0.old_game_mode != self.0.new_game_mode {
|
if self.0.old_game_mode != self.0.new_game_mode {
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub mod util;
|
||||||
mod var_int;
|
mod var_int;
|
||||||
mod var_long;
|
mod var_long;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
pub mod player_textures;
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
pub use biome::{Biome, BiomeId};
|
pub use biome::{Biome, BiomeId};
|
||||||
|
|
|
@ -329,6 +329,16 @@ mod private {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def_struct! {
|
||||||
|
#[derive(PartialEq, Serialize, Deserialize)]
|
||||||
|
Property {
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
signature: Option<String>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Packets and types used during the handshaking state.
|
/// Packets and types used during the handshaking state.
|
||||||
pub mod handshake {
|
pub mod handshake {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -413,14 +423,6 @@ pub mod login {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
|
||||||
Property {
|
|
||||||
name: String,
|
|
||||||
value: String,
|
|
||||||
signature: Option<String>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
SetCompression 0x03 {
|
SetCompression 0x03 {
|
||||||
threshold: VarInt
|
threshold: VarInt
|
||||||
|
@ -474,7 +476,6 @@ pub mod play {
|
||||||
pub mod s2c {
|
pub mod s2c {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
use crate::packets::login::c2s::SignatureData;
|
use crate::packets::login::c2s::SignatureData;
|
||||||
use crate::packets::login::s2c::Property;
|
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
SpawnEntity 0x00 {
|
SpawnEntity 0x00 {
|
||||||
|
|
|
@ -3,15 +3,14 @@ use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::client::GameMode;
|
use crate::client::GameMode;
|
||||||
use crate::packets::login::s2c::Property;
|
|
||||||
use crate::packets::play::s2c::{
|
use crate::packets::play::s2c::{
|
||||||
PlayerInfo, PlayerInfoAddPlayer, PlayerListHeaderFooter, S2cPlayPacket,
|
PlayerInfo, PlayerInfoAddPlayer, PlayerListHeaderFooter, S2cPlayPacket,
|
||||||
};
|
};
|
||||||
|
use crate::packets::Property;
|
||||||
|
use crate::player_textures::SignedPlayerTextures;
|
||||||
use crate::var_int::VarInt;
|
use crate::var_int::VarInt;
|
||||||
use crate::Text;
|
use crate::Text;
|
||||||
|
|
||||||
|
@ -55,16 +54,11 @@ impl PlayerList {
|
||||||
username: e.username.clone().into(),
|
username: e.username.clone().into(),
|
||||||
properties: {
|
properties: {
|
||||||
let mut properties = Vec::new();
|
let mut properties = Vec::new();
|
||||||
if e.skin().is_some() || e.cape().is_some() {
|
if let Some(textures) = &e.textures {
|
||||||
let textures = PlayerTextures {
|
|
||||||
skin: e.skin().cloned().map(TextureUrl::new),
|
|
||||||
cape: e.cape().cloned().map(TextureUrl::new),
|
|
||||||
};
|
|
||||||
|
|
||||||
properties.push(Property {
|
properties.push(Property {
|
||||||
name: "textures".into(),
|
name: "textures".into(),
|
||||||
value: base64::encode(serde_json::to_string(&textures).unwrap()),
|
value: base64::encode(textures.payload()),
|
||||||
signature: None,
|
signature: Some(base64::encode(textures.signature())),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
properties
|
properties
|
||||||
|
@ -104,16 +98,11 @@ impl PlayerList {
|
||||||
for (&uuid, e) in self.entries.iter() {
|
for (&uuid, e) in self.entries.iter() {
|
||||||
if e.flags.created_this_tick() {
|
if e.flags.created_this_tick() {
|
||||||
let mut properties = Vec::new();
|
let mut properties = Vec::new();
|
||||||
if e.skin().is_some() || e.cape().is_some() {
|
if let Some(textures) = &e.textures {
|
||||||
let textures = PlayerTextures {
|
|
||||||
skin: e.skin().cloned().map(TextureUrl::new),
|
|
||||||
cape: e.cape().cloned().map(TextureUrl::new),
|
|
||||||
};
|
|
||||||
|
|
||||||
properties.push(Property {
|
properties.push(Property {
|
||||||
name: "textures".into(),
|
name: "textures".into(),
|
||||||
value: base64::encode(serde_json::to_string(&textures).unwrap()),
|
value: base64::encode(textures.payload()),
|
||||||
signature: None,
|
signature: Some(base64::encode(textures.signature())),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,51 +175,41 @@ impl<'a> PlayerListMut<'a> {
|
||||||
PlayerListMut(self.0)
|
PlayerListMut(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_player(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
username: impl Into<String>,
|
username: impl Into<String>,
|
||||||
skin: Option<Url>,
|
textures: Option<SignedPlayerTextures>,
|
||||||
cape: Option<Url>,
|
|
||||||
game_mode: GameMode,
|
game_mode: GameMode,
|
||||||
ping: i32,
|
ping: i32,
|
||||||
display_name: impl Into<Option<Text>>,
|
display_name: impl Into<Option<Text>>,
|
||||||
) {
|
) {
|
||||||
match self.0.entries.entry(uuid) {
|
match self.0.entries.entry(uuid) {
|
||||||
Entry::Occupied(mut oe) => {
|
Entry::Occupied(mut oe) => {
|
||||||
let mut entry = PlayerListEntryMut(oe.get_mut());
|
let mut e = PlayerListEntryMut(oe.get_mut());
|
||||||
let username = username.into();
|
let username = username.into();
|
||||||
|
|
||||||
if entry.username() != username
|
if e.username() != username || e.textures != textures {
|
||||||
|| entry.skin() != skin.as_ref()
|
|
||||||
|| entry.cape() != cape.as_ref()
|
|
||||||
{
|
|
||||||
self.0.removed.insert(*oe.key());
|
self.0.removed.insert(*oe.key());
|
||||||
|
|
||||||
oe.insert(PlayerListEntry {
|
oe.insert(PlayerListEntry {
|
||||||
username,
|
username,
|
||||||
textures: PlayerTextures {
|
textures,
|
||||||
skin: skin.map(TextureUrl::new),
|
|
||||||
cape: cape.map(TextureUrl::new),
|
|
||||||
},
|
|
||||||
game_mode,
|
game_mode,
|
||||||
ping,
|
ping,
|
||||||
display_name: display_name.into(),
|
display_name: display_name.into(),
|
||||||
flags: EntryFlags::new().with_created_this_tick(true),
|
flags: EntryFlags::new().with_created_this_tick(true),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
entry.set_game_mode(game_mode);
|
e.set_game_mode(game_mode);
|
||||||
entry.set_ping(ping);
|
e.set_ping(ping);
|
||||||
entry.set_display_name(display_name);
|
e.set_display_name(display_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Entry::Vacant(ve) => {
|
Entry::Vacant(ve) => {
|
||||||
ve.insert(PlayerListEntry {
|
ve.insert(PlayerListEntry {
|
||||||
username: username.into(),
|
username: username.into(),
|
||||||
textures: PlayerTextures {
|
textures,
|
||||||
skin: skin.map(TextureUrl::new),
|
|
||||||
cape: cape.map(TextureUrl::new),
|
|
||||||
},
|
|
||||||
game_mode,
|
game_mode,
|
||||||
ping,
|
ping,
|
||||||
display_name: display_name.into(),
|
display_name: display_name.into(),
|
||||||
|
@ -240,7 +219,7 @@ impl<'a> PlayerListMut<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_player(&mut self, uuid: Uuid) -> bool {
|
pub fn remove(&mut self, uuid: Uuid) -> bool {
|
||||||
if self.0.entries.remove(&uuid).is_some() {
|
if self.0.entries.remove(&uuid).is_some() {
|
||||||
self.0.removed.insert(uuid);
|
self.0.removed.insert(uuid);
|
||||||
true
|
true
|
||||||
|
@ -283,7 +262,7 @@ impl<'a> PlayerListMut<'a> {
|
||||||
|
|
||||||
pub struct PlayerListEntry {
|
pub struct PlayerListEntry {
|
||||||
username: String,
|
username: String,
|
||||||
textures: PlayerTextures,
|
textures: Option<SignedPlayerTextures>,
|
||||||
game_mode: GameMode,
|
game_mode: GameMode,
|
||||||
ping: i32,
|
ping: i32,
|
||||||
display_name: Option<Text>,
|
display_name: Option<Text>,
|
||||||
|
@ -295,12 +274,8 @@ impl PlayerListEntry {
|
||||||
&self.username
|
&self.username
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skin(&self) -> Option<&Url> {
|
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
||||||
self.textures.skin.as_ref().map(|t| &t.url)
|
self.textures.as_ref()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cape(&self) -> Option<&Url> {
|
|
||||||
self.textures.cape.as_ref().map(|t| &t.url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn game_mode(&self) -> GameMode {
|
pub fn game_mode(&self) -> GameMode {
|
||||||
|
@ -363,23 +338,3 @@ struct EntryFlags {
|
||||||
#[bits(4)]
|
#[bits(4)]
|
||||||
_pad: u8,
|
_pad: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
|
||||||
pub(crate) struct PlayerTextures {
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
skin: Option<TextureUrl>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
cape: Option<TextureUrl>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
|
||||||
struct TextureUrl {
|
|
||||||
url: Url,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureUrl {
|
|
||||||
fn new(url: Url) -> Self {
|
|
||||||
Self { url }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
76
src/player_textures.rs
Normal file
76
src/player_textures.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use anyhow::Context;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct SignedPlayerTextures {
|
||||||
|
payload: Box<[u8]>,
|
||||||
|
signature: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignedPlayerTextures {
|
||||||
|
pub fn payload(&self) -> &[u8] {
|
||||||
|
&self.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> &[u8] {
|
||||||
|
&self.signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_textures(&self) -> PlayerTextures {
|
||||||
|
self.to_textures_fallible()
|
||||||
|
.expect("payload should have been validated earlier")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_textures_fallible(&self) -> anyhow::Result<PlayerTextures> {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Textures {
|
||||||
|
textures: PlayerTexturesPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
let textures: Textures = serde_json::from_slice(&self.payload)?;
|
||||||
|
|
||||||
|
Ok(PlayerTextures {
|
||||||
|
skin: textures.textures.skin.map(|t| t.url),
|
||||||
|
cape: textures.textures.cape.map(|t| t.url),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_base64(payload: String, signature: String) -> anyhow::Result<Self> {
|
||||||
|
let res = Self {
|
||||||
|
payload: base64::decode(payload)?.into_boxed_slice(),
|
||||||
|
signature: base64::decode(signature)?.into_boxed_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match res.to_textures_fallible() {
|
||||||
|
Ok(_) => Ok(res),
|
||||||
|
Err(e) => Err(e).context("failed to parse textures payload"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Default, Debug)]
|
||||||
|
pub struct PlayerTextures {
|
||||||
|
pub skin: Option<Url>,
|
||||||
|
pub cape: Option<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SignedPlayerTextures> for PlayerTextures {
|
||||||
|
fn from(spt: SignedPlayerTextures) -> Self {
|
||||||
|
spt.to_textures()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
pub struct PlayerTexturesPayload {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
skin: Option<TextureUrl>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
cape: Option<TextureUrl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
struct TextureUrl {
|
||||||
|
url: Url,
|
||||||
|
}
|
|
@ -29,14 +29,14 @@ use uuid::Uuid;
|
||||||
use crate::codec::{Decoder, Encoder};
|
use crate::codec::{Decoder, Encoder};
|
||||||
use crate::config::{Config, ServerListPing};
|
use crate::config::{Config, ServerListPing};
|
||||||
use crate::packets::handshake::{Handshake, HandshakeNextState};
|
use crate::packets::handshake::{Handshake, HandshakeNextState};
|
||||||
use crate::packets::login;
|
|
||||||
use crate::packets::login::c2s::{EncryptionResponse, LoginStart, VerifyTokenOrMsgSig};
|
use crate::packets::login::c2s::{EncryptionResponse, LoginStart, VerifyTokenOrMsgSig};
|
||||||
use crate::packets::login::s2c::{EncryptionRequest, LoginSuccess, SetCompression};
|
use crate::packets::login::s2c::{EncryptionRequest, LoginSuccess, SetCompression};
|
||||||
use crate::packets::play::c2s::C2sPlayPacket;
|
use crate::packets::play::c2s::C2sPlayPacket;
|
||||||
use crate::packets::play::s2c::S2cPlayPacket;
|
use crate::packets::play::s2c::S2cPlayPacket;
|
||||||
use crate::packets::status::c2s::{Ping, Request};
|
use crate::packets::status::c2s::{Ping, Request};
|
||||||
use crate::packets::status::s2c::{Pong, Response};
|
use crate::packets::status::s2c::{Pong, Response};
|
||||||
use crate::player_list::PlayerTextures;
|
use crate::packets::{login, Property};
|
||||||
|
use crate::player_textures::SignedPlayerTextures;
|
||||||
use crate::protocol::{BoundedArray, BoundedString};
|
use crate::protocol::{BoundedArray, BoundedString};
|
||||||
use crate::util::valid_username;
|
use crate::util::valid_username;
|
||||||
use crate::var_int::VarInt;
|
use crate::var_int::VarInt;
|
||||||
|
@ -90,6 +90,7 @@ struct ServerInner {
|
||||||
pub struct NewClientData {
|
pub struct NewClientData {
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
pub textures: Option<SignedPlayerTextures>,
|
||||||
pub remote_addr: SocketAddr,
|
pub remote_addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,17 +415,12 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) {
|
||||||
let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity);
|
let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity);
|
||||||
let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity);
|
let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity);
|
||||||
|
|
||||||
let client_packet_channels: S2cPacketChannels = (serverbound_tx, clientbound_rx);
|
let s2c_packet_channels: S2cPacketChannels = (serverbound_tx, clientbound_rx);
|
||||||
let server_packet_channels: C2sPacketChannels = (clientbound_tx, serverbound_rx);
|
let c2s_packet_channels: C2sPacketChannels = (clientbound_tx, serverbound_rx);
|
||||||
|
|
||||||
let _ = msg.reply.send(client_packet_channels);
|
let _ = msg.reply.send(s2c_packet_channels);
|
||||||
|
|
||||||
let mut client = Client::new(
|
let mut client = Client::new(c2s_packet_channels, server, msg.ncd);
|
||||||
server_packet_channels,
|
|
||||||
msg.ncd.username,
|
|
||||||
msg.ncd.uuid,
|
|
||||||
server,
|
|
||||||
);
|
|
||||||
let mut client_mut = ClientMut::new(&mut client);
|
let mut client_mut = ClientMut::new(&mut client);
|
||||||
|
|
||||||
match server
|
match server
|
||||||
|
@ -641,12 +637,6 @@ async fn handle_login(
|
||||||
properties: Vec<Property>,
|
properties: Vec<Property>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Property {
|
|
||||||
name: String,
|
|
||||||
value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let hash = Sha1::new()
|
let hash = Sha1::new()
|
||||||
.chain(&shared_secret)
|
.chain(&shared_secret)
|
||||||
.chain(&server.0.public_key_der)
|
.chain(&server.0.public_key_der)
|
||||||
|
@ -670,8 +660,11 @@ async fn handle_login(
|
||||||
let uuid = Uuid::parse_str(&data.id).context("failed to parse player's UUID")?;
|
let uuid = Uuid::parse_str(&data.id).context("failed to parse player's UUID")?;
|
||||||
|
|
||||||
let textures = match data.properties.into_iter().find(|p| p.name == "textures") {
|
let textures = match data.properties.into_iter().find(|p| p.name == "textures") {
|
||||||
Some(p) => decode_textures(p.value).context("failed to decode skin blob")?,
|
Some(p) => SignedPlayerTextures::from_base64(
|
||||||
None => bail!("failed to find skin blob in auth response"),
|
p.value,
|
||||||
|
p.signature.context("missing signature for textures")?,
|
||||||
|
)?,
|
||||||
|
None => bail!("failed to find textures in auth response"),
|
||||||
};
|
};
|
||||||
|
|
||||||
(uuid, Some(textures))
|
(uuid, Some(textures))
|
||||||
|
@ -694,6 +687,7 @@ async fn handle_login(
|
||||||
let npd = NewClientData {
|
let npd = NewClientData {
|
||||||
uuid,
|
uuid,
|
||||||
username,
|
username,
|
||||||
|
textures,
|
||||||
remote_addr,
|
remote_addr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -713,17 +707,6 @@ async fn handle_login(
|
||||||
Ok(Some(npd))
|
Ok(Some(npd))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_textures(encoded: String) -> anyhow::Result<PlayerTextures> {
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Textures {
|
|
||||||
textures: PlayerTextures,
|
|
||||||
}
|
|
||||||
|
|
||||||
let bytes = base64::decode(encoded)?;
|
|
||||||
let textures: Textures = serde_json::from_slice(&bytes)?;
|
|
||||||
Ok(textures.textures)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_play(server: &Server, c: Codec, ncd: NewClientData) -> anyhow::Result<()> {
|
async fn handle_play(server: &Server, c: Codec, ncd: NewClientData) -> anyhow::Result<()> {
|
||||||
let (reply_tx, reply_rx) = oneshot::channel();
|
let (reply_tx, reply_rx) = oneshot::channel();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue