Mapped entire login state and added trivial offline login flow.

master
James T. Martin 2020-07-25 01:33:00 -07:00
parent afc8a4aaaa
commit a89562f9d6
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
6 changed files with 175 additions and 9 deletions

View File

@ -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(())
}

View File

@ -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

View File

@ -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.

View File

@ -1,4 +1,6 @@
pub mod handshake;
pub mod login;
pub mod play;
pub mod status;
use crate::net::packet_map::PacketMap;

69
src/net/state/login.rs Normal file
View File

@ -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);

22
src/net/state/play.rs Normal file
View File

@ -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);