Join Game and Client Settings
This commit is contained in:
parent
4b4c962f7b
commit
9def1e2def
@ -1 +1 @@
|
||||
|
||||
pub mod player;
|
||||
|
8
src/entity/player.rs
Normal file
8
src/entity/player.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::world::location::Location;
|
||||
use crate::server::NetworkClient;
|
||||
|
||||
pub struct Player {
|
||||
position: Location,
|
||||
display_name: String,
|
||||
connection: NetworkClient,
|
||||
}
|
@ -7,11 +7,13 @@ use packets::*;
|
||||
use serde_json::json;
|
||||
use std::sync::mpsc::{self, Receiver, TryRecvError};
|
||||
use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
|
||||
use crate::entity::player::Player;
|
||||
|
||||
/// The struct containing all the data and running all the updates.
|
||||
pub struct Server {
|
||||
pub network_clients: Vec<NetworkClient>,
|
||||
network_clients: Vec<NetworkClient>,
|
||||
network_receiver: Receiver<NetworkClient>,
|
||||
pub players: Vec<Player>,
|
||||
}
|
||||
impl Server {
|
||||
pub fn new<A: 'static + ToSocketAddrs + Send>(addr: A) -> Server {
|
||||
@ -26,12 +28,7 @@ impl Server {
|
||||
.accept()
|
||||
.await
|
||||
.expect("Network receiver disconnected");
|
||||
tx.send(NetworkClient {
|
||||
id: id as u128,
|
||||
connected: true,
|
||||
stream,
|
||||
state: NetworkClientState::Handshake,
|
||||
})
|
||||
tx.send(NetworkClient::new(stream, id as u128))
|
||||
.expect("Network receiver disconnected");
|
||||
id += 1;
|
||||
}
|
||||
@ -40,6 +37,7 @@ impl Server {
|
||||
Server {
|
||||
network_receiver: rx,
|
||||
network_clients: vec![],
|
||||
players: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,8 +99,22 @@ pub struct NetworkClient {
|
||||
pub connected: bool,
|
||||
pub stream: TcpStream,
|
||||
pub state: NetworkClientState,
|
||||
pub uuid: Option<String>,
|
||||
pub username: Option<String>,
|
||||
}
|
||||
impl NetworkClient {
|
||||
/// Create a new `NetworkClient`
|
||||
pub fn new(stream: TcpStream, id: u128) -> NetworkClient {
|
||||
NetworkClient {
|
||||
id,
|
||||
connected: true,
|
||||
stream,
|
||||
state: NetworkClientState::Handshake,
|
||||
uuid: None,
|
||||
username: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the client.
|
||||
///
|
||||
/// Updating could mean connecting new clients, reading packets,
|
||||
@ -156,7 +168,6 @@ impl NetworkClient {
|
||||
"description": {
|
||||
"text": CONFIG.motd
|
||||
},
|
||||
// TODO: Dynamically send the icon instead of linking statically.
|
||||
"favicon": format!("data:image/png;base64,{}", if FAVICON.is_ok() { radix64::STD.encode(FAVICON.as_ref().unwrap().as_slice()) } else { "".to_owned() })
|
||||
})
|
||||
.to_string()
|
||||
@ -179,13 +190,25 @@ impl NetworkClient {
|
||||
let loginstart = LoginStart::read(&mut self.stream).await.unwrap();
|
||||
debug!("{:?}", loginstart);
|
||||
// Offline mode skips encryption and compression.
|
||||
// TODO: Encryption and compression
|
||||
let mut loginsuccess = LoginSuccess::new();
|
||||
// We're in offline mode, so this is a temporary uuid.
|
||||
// TODO: Get uuid and username from Mojang servers.
|
||||
loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into();
|
||||
loginsuccess.username = loginstart.player_name;
|
||||
loginsuccess.write(&mut self.stream).await.unwrap();
|
||||
debug!("{:?}", loginsuccess);
|
||||
self.uuid = Some(loginsuccess.uuid.clone().into());
|
||||
self.username = Some(loginsuccess.username.clone().into());
|
||||
self.state = NetworkClientState::Play;
|
||||
let joingame = JoinGame::new();
|
||||
/// TODO: Fill out `joingame` with actual information.
|
||||
joingame.write(&mut self.stream).await.unwrap();
|
||||
debug!("{:?}", joingame);
|
||||
let (packet_length, packet_id) =
|
||||
read_packet_header(&mut self.stream).await.unwrap();
|
||||
let clientsettings = ClientSettings::read(&mut self.stream).await.unwrap();
|
||||
debug!("{:?}", clientsettings);
|
||||
}
|
||||
NetworkClientState::Play => {}
|
||||
NetworkClientState::Disconnected => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::mctypes::*;
|
||||
use crate::CONFIG;
|
||||
use std::convert::{Into, TryFrom};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
@ -163,3 +164,66 @@ impl LoginDisconnect {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinGame {
|
||||
entity_id: MCInt, // The player's Entity ID (EID)
|
||||
gamemode: MCUnsignedByte, // 0: Survival, 1: Creative, 2: Adventure, 3: Spectator. Bit 3 (0x8) is the hardcore flag.
|
||||
dimension: MCByte, // -1: Nether, 0: Overworld, 1: End
|
||||
difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard
|
||||
max_players: MCUnsignedByte, // Used by the client to draw the player list
|
||||
level_type: MCString, // default, flat, largeBiomes, amplified, default_1_1
|
||||
reduced_debug_info: MCBoolean, // If true, a Notchian client shows reduced information on the debug screen.
|
||||
}
|
||||
impl Into<Vec<u8>> for JoinGame {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(0x01).into(); // 0x01 Join Game.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.entity_id));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.gamemode));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.dimension));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.difficulty));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.max_players));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.level_type));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.reduced_debug_info));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for JoinGame {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
impl JoinGame {
|
||||
pub fn new() -> Self {
|
||||
JoinGame {
|
||||
entity_id: 0.into(),
|
||||
gamemode: 1.into(), // Default to creative mode.
|
||||
dimension: 0.into(), // Default to overworld.
|
||||
difficulty: 2.into(),
|
||||
max_players: (CONFIG.max_players as u8).into(),
|
||||
level_type: "default".into(), // Use the default world type.
|
||||
reduced_debug_info: false.into(), // The debug info should be useful.
|
||||
}
|
||||
}
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut joingame = JoinGame::new();
|
||||
joingame.entity_id = MCInt::read(t).await?;
|
||||
joingame.gamemode = MCUnsignedByte::read(t).await?;
|
||||
joingame.dimension = MCByte::read(t).await?;
|
||||
joingame.difficulty = MCUnsignedByte::read(t).await?;
|
||||
joingame.max_players = MCUnsignedByte::read(t).await?;
|
||||
joingame.level_type = MCString::read(t).await?;
|
||||
joingame.reduced_debug_info = MCBoolean::read(t).await?;
|
||||
Ok(joingame)
|
||||
}
|
||||
pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -162,3 +162,66 @@ impl LoginStart {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientSettings {
|
||||
pub locale: MCString,
|
||||
pub view_distance: MCByte,
|
||||
pub chat_mode: MCVarInt, // 0: enabled, 1: commands only, 2: hidden.
|
||||
pub chat_colors: MCBoolean,
|
||||
pub displayed_skin_parts: MCUnsignedByte, // Bit mask
|
||||
// Displayed skin parts flags:
|
||||
// Bit 0 (0x01): Cape enabled
|
||||
// Bit 1 (0x02): Jacket enabled
|
||||
// Bit 2 (0x04): Left Sleeve enabled
|
||||
// Bit 3 (0x08): Right Sleeve enabled
|
||||
// Bit 4 (0x10): Left Pants Leg enabled
|
||||
// Bit 5 (0x20): Right Pants Leg enabled
|
||||
// Bit 6 (0x40): Hat enabled
|
||||
}
|
||||
impl Into<Vec<u8>> for ClientSettings {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(0x15).into(); // 0x15 Client Settings.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.locale));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.view_distance));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.chat_mode));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.chat_colors));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.displayed_skin_parts));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ClientSettings {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
impl ClientSettings {
|
||||
pub fn new() -> Self {
|
||||
ClientSettings {
|
||||
locale: "en_US".into(),
|
||||
view_distance: 8.into(), // 8 chunks.
|
||||
chat_mode: 0.into(), // All chat enabled.
|
||||
chat_colors: true.into(),
|
||||
displayed_skin_parts: 0xff.into(), // Enable all parts.
|
||||
}
|
||||
}
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut clientsettings = ClientSettings::new();
|
||||
clientsettings.locale = MCString::read(t).await?;
|
||||
clientsettings.view_distance = MCByte::read(t).await?;
|
||||
clientsettings.chat_mode = MCVarInt::read(t).await?;
|
||||
clientsettings.chat_colors = MCBoolean::read(t).await?;
|
||||
clientsettings.displayed_skin_parts = MCUnsignedByte::read(t).await?;
|
||||
Ok(clientsettings)
|
||||
}
|
||||
pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
54
src/world/location.rs
Normal file
54
src/world/location.rs
Normal file
@ -0,0 +1,54 @@
|
||||
/// Used to store a point in a world.
|
||||
pub struct Location {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub pitch: f32,
|
||||
pub yaw: f32,
|
||||
// TODO: Store a reference to the world this location is in.
|
||||
}
|
||||
impl Location {
|
||||
/// Create a new `Location`.
|
||||
pub fn new(x: f64, y: f64, z: f64, pitch: f32, yaw: f32) -> Location {
|
||||
Location {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
pitch,
|
||||
yaw,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no rotation.
|
||||
pub fn position(x: f64, y: f64, z: f64) -> Location {
|
||||
Location {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no position.
|
||||
pub fn rotation(pitch: f32, yaw: f32) -> Location {
|
||||
Location {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
pitch,
|
||||
yaw,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no rotation or position.
|
||||
pub fn zero() -> Location {
|
||||
Location {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
|
||||
pub mod location;
|
||||
|
Loading…
x
Reference in New Issue
Block a user