parent
5ef70901e6
commit
188e2b7610
@ -0,0 +1,246 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
use varint_rs::{VarintReader, VarintWriter};
|
||||
|
||||
use crate::{
|
||||
Connected, Server, Status,
|
||||
error::ChatError,
|
||||
error::Result,
|
||||
readers::{read_packet, read_plugin_msg, read_string},
|
||||
status::ServerType,
|
||||
writers::{write_packet, write_plugin_msg, write_string},
|
||||
};
|
||||
|
||||
impl Server<Status> {
|
||||
pub fn connect(self) -> Result<Server<Connected>> {
|
||||
let Self {
|
||||
mut stream,
|
||||
addr,
|
||||
port,
|
||||
state:
|
||||
Status {
|
||||
typ,
|
||||
description,
|
||||
mods,
|
||||
},
|
||||
} = self;
|
||||
|
||||
let (uuid, username) = match typ {
|
||||
ServerType::Vanilla => {
|
||||
// Handshake
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_u8_varint(0)?;
|
||||
buf.write_u8_varint(5)?;
|
||||
buf.write_usize_varint(addr.len())?;
|
||||
buf.write_all(addr.as_bytes())?;
|
||||
buf.write_all(&port.to_le_bytes())?;
|
||||
buf.write_u8_varint(2)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Login start
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_u8_varint(0)?;
|
||||
let uname = "McChatClient";
|
||||
write_string(buf, uname)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
read_packet(&mut stream, |packet| {
|
||||
let id = packet.read_usize_varint()?;
|
||||
assert!(id == 2);
|
||||
let uuid = read_string(packet)?;
|
||||
let username = read_string(packet)?;
|
||||
Ok((uuid, username))
|
||||
})?
|
||||
}
|
||||
ServerType::Forge => {
|
||||
// Handshake
|
||||
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_u8_varint(0)?;
|
||||
buf.write_u8_varint(5)?;
|
||||
let addr = format!("{addr}\0FML\0");
|
||||
buf.write_usize_varint(addr.len())?;
|
||||
buf.write_all(addr.as_bytes())?;
|
||||
buf.write_all(&port.to_le_bytes())?;
|
||||
buf.write_u8_varint(2)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Login start
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_u8_varint(0)?;
|
||||
let uname = "McChatClient";
|
||||
write_string(buf, uname)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Login Success
|
||||
let (uuid, username) = read_packet(&mut stream, |packet| {
|
||||
let id = packet.read_usize_varint()?;
|
||||
assert!(id == 2);
|
||||
let uuid = read_string(packet)?;
|
||||
let username = read_string(packet)?;
|
||||
Ok((uuid, username))
|
||||
})?;
|
||||
|
||||
// Server Register Channels
|
||||
let (_channels, channels_buf) =
|
||||
read_plugin_msg(&mut stream, "REGISTER", |packet| {
|
||||
Ok((
|
||||
packet
|
||||
.split(|b| *b == 0)
|
||||
.map(|b| String::from_utf8(b.to_vec()).map_err(ChatError::from))
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
packet.to_vec(),
|
||||
))
|
||||
})?;
|
||||
|
||||
// Server Hello
|
||||
let _dim = read_plugin_msg(&mut stream, "FML|HS", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 0);
|
||||
let proto_ver = VarintReader::read(packet)?;
|
||||
assert_eq!(proto_ver, 2);
|
||||
let mut buf = [0u8; 4];
|
||||
packet.read_exact(&mut buf)?;
|
||||
let dim = i32::from_be_bytes(buf);
|
||||
Ok(dim)
|
||||
})?;
|
||||
|
||||
// Client Register Channels
|
||||
write_plugin_msg(&mut stream, "REGISTER", |body| {
|
||||
body.write_all(&channels_buf)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Client Hello
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0x1])?;
|
||||
body.write_all(&[0x2])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Client Modlist
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0x2])?;
|
||||
let mods = mods.as_ref().unwrap();
|
||||
body.write_usize_varint(mods.len())?;
|
||||
|
||||
for (id, ver) in mods {
|
||||
write_string(body, id)?;
|
||||
write_string(body, ver)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Server Modlist
|
||||
let _mods = read_plugin_msg(&mut stream, "FML|HS", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 2);
|
||||
let num_mods = VarintReader::read(packet)?;
|
||||
|
||||
let mut mods = HashMap::new();
|
||||
for _ in 0..num_mods {
|
||||
let id = read_string(packet)?;
|
||||
let ver = read_string(packet)?;
|
||||
mods.insert(id, ver);
|
||||
}
|
||||
Ok(mods)
|
||||
})?;
|
||||
|
||||
// HandshakeAck waitingseverdata
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0xFF, 0x02])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Server RegistryData
|
||||
read_plugin_msg(&mut stream, "FML|HS", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 3);
|
||||
let num_ids = packet.read_usize_varint()?;
|
||||
|
||||
for _ in 0..num_ids {
|
||||
let _name = read_string(packet)?;
|
||||
let _id = packet.read_usize_varint()?;
|
||||
}
|
||||
|
||||
if packet.len() > 0 {
|
||||
let len = packet.read_usize_varint()?;
|
||||
for _ in 0..len {
|
||||
read_string(packet)?;
|
||||
}
|
||||
|
||||
let len = packet.read_usize_varint()?;
|
||||
for _ in 0..len {
|
||||
read_string(packet)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Server HandshakeAck waitingack
|
||||
read_plugin_msg(&mut stream, "FML|HS", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 255);
|
||||
let phrase = VarintReader::read(packet)?;
|
||||
assert_eq!(phrase, 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// HandshakeAck waitingsevercomplete
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0xFF, 0x03])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Serrver FluidIdmap
|
||||
read_plugin_msg(&mut stream, "FORGE", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// HandshakeAck pendingcomplete
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0xFF, 0x04])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Server HandshakeAck complete
|
||||
read_plugin_msg(&mut stream, "FML|HS", |packet| {
|
||||
let discriminant = VarintReader::read(packet)?;
|
||||
assert_eq!(discriminant, 255);
|
||||
let phrase = VarintReader::read(packet)?;
|
||||
assert_eq!(phrase, 3);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// HandshakeAck complete
|
||||
write_plugin_msg(&mut stream, "FML|HS", |body| {
|
||||
body.write_all(&[0xFF, 0x05])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
(uuid, username)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Server {
|
||||
stream,
|
||||
addr,
|
||||
port,
|
||||
state: Connected {
|
||||
typ,
|
||||
description,
|
||||
uuid,
|
||||
username,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
use serde_json::Value;
|
||||
use std::{io, result::Result as StdResult, string::FromUtf8Error};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ChatError {
|
||||
IoError(io::Error),
|
||||
FromUtf8Error(FromUtf8Error),
|
||||
JsonError(JsonError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JsonError {
|
||||
SerdeError(serde_json::Error),
|
||||
ExpectedObject(Value),
|
||||
ExpectedArray,
|
||||
ExpectedString,
|
||||
ExpectedMember(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ChatError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for ChatError {
|
||||
fn from(value: FromUtf8Error) -> Self {
|
||||
Self::FromUtf8Error(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonError> for ChatError {
|
||||
fn from(value: JsonError) -> Self {
|
||||
Self::JsonError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for JsonError {
|
||||
fn from(value: serde_json::Error) -> Self {
|
||||
Self::SerdeError(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = StdResult<T, ChatError>;
|
@ -1,19 +1,47 @@
|
||||
use std::{io::Read as _, net::TcpStream};
|
||||
use std::{io::Read, net::TcpStream};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
use varint_rs::VarintReader as _;
|
||||
|
||||
pub fn read_string<I: Iterator<Item = u8>>(iter: &mut I) -> Option<String> {
|
||||
let len = iter.next()?.into();
|
||||
let bytes = iter.take(len).collect::<Vec<_>>();
|
||||
(len == bytes.len()).then_some(())?;
|
||||
String::from_utf8(bytes).ok()
|
||||
pub fn read_string(iter: &mut &[u8]) -> Result<String> {
|
||||
let len = iter.read_usize_varint()?;
|
||||
let mut buf = vec![0u8; len];
|
||||
iter.read_exact(&mut buf)?;
|
||||
String::from_utf8(buf).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn read_packet(stream: &mut TcpStream) -> Vec<u8> {
|
||||
let len = stream.read_usize_varint().unwrap();
|
||||
pub fn read_packet<T, F: Fn(&mut &[u8]) -> Result<T>>(
|
||||
stream: &mut TcpStream,
|
||||
read_body: F,
|
||||
) -> Result<T> {
|
||||
let len = stream.read_usize_varint()?;
|
||||
|
||||
let mut buf = vec![0u8; len];
|
||||
stream.read_exact(&mut buf).unwrap();
|
||||
stream.read_exact(&mut buf)?;
|
||||
|
||||
let mut packet = &buf[..];
|
||||
read_body(&mut packet)
|
||||
}
|
||||
|
||||
pub fn read_plugin_msg<T, F: Fn(&mut &[u8]) -> Result<T>>(
|
||||
stream: &mut TcpStream,
|
||||
channel: &str,
|
||||
read_body: F,
|
||||
) -> Result<T> {
|
||||
read_packet(stream, |packet| {
|
||||
let id = packet.read_usize_varint()?;
|
||||
assert!(id == 63);
|
||||
|
||||
let channel_read = read_string(packet)?;
|
||||
assert_eq!(channel_read, channel);
|
||||
let mut buf = [0u8; 2];
|
||||
packet.read_exact(&mut buf)?;
|
||||
let len = u16::from_be_bytes(buf);
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
packet.read_exact(&mut buf)?;
|
||||
|
||||
buf
|
||||
let mut packet = &buf[..];
|
||||
read_body(&mut packet)
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
use std::{collections::HashMap, io::Write, net::TcpStream};
|
||||
|
||||
use serde_json::Value;
|
||||
use varint_rs::{VarintReader, VarintWriter};
|
||||
|
||||
use crate::{
|
||||
Disconnected, Server, Status,
|
||||
error::ChatError,
|
||||
error::JsonError,
|
||||
error::Result,
|
||||
readers::{read_packet, read_string},
|
||||
writers::write_packet,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServerType {
|
||||
Vanilla,
|
||||
Forge,
|
||||
}
|
||||
|
||||
impl Server<Disconnected> {
|
||||
pub fn status(self) -> Result<Server<Status>> {
|
||||
let Self {
|
||||
mut stream,
|
||||
addr,
|
||||
port,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// Handshake
|
||||
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_u8_varint(0)?;
|
||||
buf.write_u8_varint(5)?;
|
||||
buf.write_usize_varint(addr.len())?;
|
||||
buf.write_all(addr.as_bytes())?;
|
||||
buf.write_all(&port.to_le_bytes())?;
|
||||
buf.write_u8_varint(1)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
write_packet(&mut stream, |buf| {
|
||||
buf.write_all(&[0x0])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let json: Value = read_packet(&mut stream, |packet| {
|
||||
let id = packet.read_usize_varint()?;
|
||||
assert!(id == 0);
|
||||
let json = read_string(packet)?;
|
||||
serde_json::from_str(&json)
|
||||
.map_err(<JsonError as From<serde_json::Error>>::from)
|
||||
.map_err(ChatError::from)
|
||||
})?;
|
||||
|
||||
let json = json
|
||||
.as_object()
|
||||
.ok_or(JsonError::ExpectedObject(json.clone()))?;
|
||||
let description = json
|
||||
.get("description")
|
||||
.ok_or(JsonError::ExpectedMember("description".to_string()))?
|
||||
.as_str()
|
||||
.ok_or(JsonError::ExpectedString)?
|
||||
.to_string();
|
||||
|
||||
let (typ, mods) = match json.get("modinfo") {
|
||||
Some(info) => {
|
||||
if info
|
||||
.as_object()
|
||||
.ok_or(JsonError::ExpectedObject(info.clone()))?
|
||||
.get("type")
|
||||
.ok_or(JsonError::ExpectedMember("type".to_string()))?
|
||||
.as_str()
|
||||
.ok_or(JsonError::ExpectedString)?
|
||||
== "FML"
|
||||
{
|
||||
let mod_list = info
|
||||
.get("modList")
|
||||
.ok_or(JsonError::ExpectedMember("modList".to_string()))?;
|
||||
let mod_list = mod_list.as_array().ok_or(JsonError::ExpectedArray)?;
|
||||
let mut mods = HashMap::new();
|
||||
for mod_ in mod_list {
|
||||
let mod_ = mod_
|
||||
.as_object()
|
||||
.ok_or(JsonError::ExpectedObject(mod_.clone()))?;
|
||||
let id = mod_
|
||||
.get("modid")
|
||||
.ok_or(JsonError::ExpectedMember("modid".to_string()))?;
|
||||
let ver = mod_
|
||||
.get("version")
|
||||
.ok_or(JsonError::ExpectedMember("version".to_string()))?;
|
||||
mods.insert(
|
||||
id.as_str().ok_or(JsonError::ExpectedString)?.to_string(),
|
||||
ver.as_str().ok_or(JsonError::ExpectedString)?.to_string(),
|
||||
);
|
||||
}
|
||||
(ServerType::Forge, Some(mods))
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
None => (ServerType::Vanilla, None),
|
||||
};
|
||||
|
||||
stream.shutdown(std::net::Shutdown::Both)?;
|
||||
|
||||
Ok(Server {
|
||||
stream: TcpStream::connect(format!("{addr}:{port}"))?,
|
||||
addr,
|
||||
port,
|
||||
state: Status {
|
||||
typ,
|
||||
description,
|
||||
mods,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
use std::{io::Write, net::TcpStream};
|
||||
|
||||
use varint_rs::VarintWriter;
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
pub fn write_packet<F: Fn(&mut Vec<u8>) -> Result<()>>(
|
||||
stream: &mut TcpStream,
|
||||
build_packet: F,
|
||||
) -> Result<()> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
build_packet(&mut buf)?;
|
||||
|
||||
stream.write_usize_varint(buf.len())?;
|
||||
stream.write_all(&buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_string<W: Write>(buf: &mut W, val: &str) -> Result<()> {
|
||||
buf.write_usize_varint(val.len())?;
|
||||
buf.write_all(val.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_plugin_msg<F: Fn(&mut Vec<u8>) -> Result<()>>(
|
||||
stream: &mut TcpStream,
|
||||
channel: &str,
|
||||
build_body: F,
|
||||
) -> Result<()> {
|
||||
let mut body: Vec<u8> = Vec::new();
|
||||
build_body(&mut body)?;
|
||||
|
||||
write_packet(stream, |mut buf| {
|
||||
buf.write_u8_varint(0x17)?;
|
||||
write_string(&mut buf, channel)?;
|
||||
buf.write_all(&(body.len() as u16).to_be_bytes())?;
|
||||
buf.write_all(&body)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
Loading…
Reference in new issue