Mapped entire login state and added trivial offline login flow.
parent
afc8a4aaaa
commit
a89562f9d6
47
src/main.rs
47
src/main.rs
|
@ -6,6 +6,8 @@ mod net;
|
|||
use crate::net::chat::Chat;
|
||||
use crate::net::connection::Connection;
|
||||
use crate::net::state::handshake::Handshake;
|
||||
use crate::net::state::login::Login;
|
||||
use crate::net::state::play::Play;
|
||||
use crate::net::state::status::Status;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use std::io;
|
||||
|
@ -54,7 +56,11 @@ async fn accept_connection(socket: TcpStream) {
|
|||
}
|
||||
}
|
||||
|
||||
async fn interact_handshake<'a>(mut con: Connection<Handshake>) -> io::Result<()> {
|
||||
fn mk_err<A, S: std::borrow::Borrow<str>>(str: S) -> io::Result<A> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, str.borrow().to_string()))
|
||||
}
|
||||
|
||||
async fn interact_handshake(mut con: Connection<Handshake>) -> io::Result<()> {
|
||||
use crate::net::state::handshake::*;
|
||||
|
||||
match con.read().await? {
|
||||
|
@ -63,13 +69,13 @@ async fn interact_handshake<'a>(mut con: Connection<Handshake>) -> io::Result<()
|
|||
|
||||
match handshake.next_state {
|
||||
Status => interact_status(con.into_status()).await,
|
||||
Login => Err(io::Error::new(io::ErrorKind::Other, "We do not support client log-in yet.".to_string()))
|
||||
Login => interact_login(con.into_login()).await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn interact_status<'a>(mut con: Connection<Status>) -> io::Result<()> {
|
||||
async fn interact_status(mut con: Connection<Status>) -> io::Result<()> {
|
||||
use crate::net::state::status::*;
|
||||
|
||||
loop {
|
||||
|
@ -102,3 +108,38 @@ async fn interact_status<'a>(mut con: Connection<Status>) -> io::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn interact_login(mut con: Connection<Login>) -> io::Result<()> {
|
||||
use crate::net::state::login::*;
|
||||
|
||||
let name = match con.read().await? {
|
||||
Serverbound::LoginStart(login_start) => {
|
||||
login_start.name
|
||||
},
|
||||
_ => {
|
||||
con.write(&Clientbound::Disconnect(Disconnect {
|
||||
reason: Chat { text: "Unexpected packet (expected Login Start).".to_string() }
|
||||
})).await?;
|
||||
return mk_err("Unexpected packet (expected Login Start).");
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("Client set username to {}.", name);
|
||||
|
||||
con.write(&Clientbound::LoginSuccess(LoginSuccess {
|
||||
uuid: uuid::Uuid::nil(),
|
||||
username: name,
|
||||
})).await?;
|
||||
|
||||
interact_play(con.into_play()).await
|
||||
}
|
||||
|
||||
async fn interact_play(mut con: Connection<Play>) -> io::Result<()> {
|
||||
use crate::net::state::play::*;
|
||||
|
||||
con.write(&Clientbound::Disconnect(Disconnect {
|
||||
reason: Chat { text: "Goodbye!".to_string() }
|
||||
})).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::net::{Reader, Writer};
|
|||
use crate::net::format::{PacketFormat, DefaultPacketFormat};
|
||||
use crate::net::state::ProtocolState;
|
||||
use crate::net::state::handshake::Handshake;
|
||||
use crate::net::state::login::Login;
|
||||
use crate::net::state::play::Play;
|
||||
use crate::net::state::status::Status;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -13,11 +15,6 @@ pub struct Connection<St: ProtocolState> {
|
|||
st: PhantomData<St>,
|
||||
}
|
||||
|
||||
// Placeholders until I implement these protocol states.
|
||||
use crate::define_state;
|
||||
define_state!(Login, !, !);
|
||||
define_state!(Play, !, !);
|
||||
|
||||
impl<St: ProtocolState> Connection<St> {
|
||||
pub async fn write(&mut self, pkt: &St::Clientbound) -> io::Result<()> {
|
||||
DefaultPacketFormat.send::<St::Clientbound>(&mut self.dest, pkt).await
|
||||
|
|
|
@ -3,6 +3,7 @@ use serde::Serialize;
|
|||
use serde::de::DeserializeOwned;
|
||||
use std::borrow::Borrow;
|
||||
use std::convert::{From, Into};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub trait PacketSerializer: Sized {
|
||||
/// Write a slice of bytes directly, without a length prefix.
|
||||
|
@ -24,6 +25,7 @@ impl PacketSerializer for Vec<u8> {
|
|||
|
||||
pub trait PacketDeserializer: Sized {
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), String>;
|
||||
fn read_rest(&mut self) -> Result<Box<[u8]>, String>;
|
||||
fn read_eof(&mut self) -> Result<(), String>;
|
||||
|
||||
fn read<D: PacketReadable>(&mut self) -> Result<D, String> {
|
||||
|
@ -58,6 +60,13 @@ impl PacketDeserializer for VecPacketDeserializer<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn read_rest(&mut self) -> Result<Box<[u8]>, String> {
|
||||
let mut it = Vec::new();
|
||||
it.copy_from_slice(&self.data[self.index..]);
|
||||
self.index = self.data.len();
|
||||
Ok(it.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn read_eof(&mut self) -> Result<(), String> {
|
||||
if self.index != self.data.len() {
|
||||
return Err("Packet contained more data than necessary.".to_string());
|
||||
|
@ -113,7 +122,7 @@ macro_rules! impl_packet_data_for_num {
|
|||
}
|
||||
}
|
||||
|
||||
impl_packet_data_for_num!(u8, 1; i8, 1; u16, 2; i16, 2; u32, 4; i32, 4; u64, 8; i64, 8;
|
||||
impl_packet_data_for_num!(u8, 1; i8, 1; u16, 2; i16, 2; u32, 4; i32, 4; u64, 8; i64, 8; u128, 16;
|
||||
f32, 4; f64, 8);
|
||||
|
||||
// HACK: There is probably a better solution to this than a macro.
|
||||
|
@ -224,6 +233,20 @@ impl PacketWritable for &Box<[u8]> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Rest(pub Box<[u8]>);
|
||||
|
||||
impl PacketReadable for Rest {
|
||||
fn read(deser: &mut impl PacketDeserializer) -> Result<Self, String> {
|
||||
deser.read_rest().map(Rest)
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketWritable for &Rest {
|
||||
fn write(self, ser: &mut impl PacketSerializer) {
|
||||
ser.write(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketWritable for &str {
|
||||
fn write(self, ser: &mut impl PacketSerializer) {
|
||||
ser.write(self.as_bytes());
|
||||
|
@ -255,6 +278,18 @@ impl PacketWritable for &Box<str> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PacketReadable for Uuid {
|
||||
fn read(deser: &mut impl PacketDeserializer) -> Result<Self, String> {
|
||||
deser.read::<u128>().map(Uuid::from_u128)
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketWritable for &Uuid {
|
||||
fn write(self, ser: &mut impl PacketSerializer) {
|
||||
ser.write(self.as_u128());
|
||||
}
|
||||
}
|
||||
|
||||
/// A marker trait indicating that a JSON-serialiable type should be serialized as JSON in packets.
|
||||
/// Most primitive types are already serializable as JSON,
|
||||
/// but we explicitly *don't* want to serialize them as JSON in packets.
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
pub mod handshake;
|
||||
pub mod login;
|
||||
pub mod play;
|
||||
pub mod status;
|
||||
|
||||
use crate::net::packet_map::PacketMap;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
use crate::{define_packets, define_packet_maps, define_state};
|
||||
use crate::net::chat::Chat;
|
||||
use crate::net::serialize::{Rest, VarInt};
|
||||
use uuid::Uuid;
|
||||
|
||||
define_packets! {
|
||||
// Clientbound
|
||||
|
||||
packet Disconnect {
|
||||
reason: Chat
|
||||
}
|
||||
|
||||
packet EncryptionRequest {
|
||||
server_id: Box<str>,
|
||||
public_key: Box<[u8]>,
|
||||
verify_token: Box<[u8]>
|
||||
}
|
||||
|
||||
packet LoginSuccess {
|
||||
uuid: Uuid,
|
||||
username: Box<str>
|
||||
}
|
||||
|
||||
packet SetCompression {
|
||||
threshold: VarInt
|
||||
}
|
||||
|
||||
packet LoginPluginRequest {
|
||||
message_id: VarInt,
|
||||
// FIXME: Actually an Identifier.
|
||||
channel: String,
|
||||
data: Rest
|
||||
}
|
||||
|
||||
// Serverbound
|
||||
|
||||
packet LoginStart {
|
||||
name: Box<str>
|
||||
}
|
||||
|
||||
packet EncryptionResponse {
|
||||
shared_secret: Box<[u8]>,
|
||||
verify_token: Box<[u8]>
|
||||
}
|
||||
|
||||
packet LoginPluginResponse {
|
||||
message_id: VarInt,
|
||||
successful: bool,
|
||||
data: Rest
|
||||
}
|
||||
}
|
||||
|
||||
define_packet_maps! {
|
||||
packet_map Clientbound {
|
||||
0x00 => Disconnect,
|
||||
0x01 => EncryptionRequest,
|
||||
0x02 => LoginSuccess,
|
||||
0x03 => SetCompression,
|
||||
0x04 => LoginPluginRequest
|
||||
}
|
||||
|
||||
packet_map Serverbound {
|
||||
0x00 => LoginStart,
|
||||
0x01 => EncryptionResponse,
|
||||
0x02 => LoginPluginResponse
|
||||
}
|
||||
}
|
||||
|
||||
define_state!(Login, Clientbound, Serverbound);
|
|
@ -0,0 +1,22 @@
|
|||
use crate::{define_packets, define_packet_maps, define_state};
|
||||
use crate::net::chat::Chat;
|
||||
|
||||
// TODO: This protocol state isn't even close to entirely mapped.
|
||||
|
||||
define_packets! {
|
||||
packet Disconnect {
|
||||
reason: Chat
|
||||
}
|
||||
}
|
||||
|
||||
define_packet_maps! {
|
||||
packet_map Clientbound {
|
||||
0x1a => Disconnect
|
||||
}
|
||||
|
||||
packet_map Serverbound {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
define_state!(Play, Clientbound, Serverbound);
|
Loading…
Reference in New Issue