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 _;
|
use varint_rs::VarintReader as _;
|
||||||
|
|
||||||
pub fn read_string<I: Iterator<Item = u8>>(iter: &mut I) -> Option<String> {
|
pub fn read_string(iter: &mut &[u8]) -> Result<String> {
|
||||||
let len = iter.next()?.into();
|
let len = iter.read_usize_varint()?;
|
||||||
let bytes = iter.take(len).collect::<Vec<_>>();
|
let mut buf = vec![0u8; len];
|
||||||
(len == bytes.len()).then_some(())?;
|
iter.read_exact(&mut buf)?;
|
||||||
String::from_utf8(bytes).ok()
|
String::from_utf8(buf).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_packet(stream: &mut TcpStream) -> Vec<u8> {
|
pub fn read_packet<T, F: Fn(&mut &[u8]) -> Result<T>>(
|
||||||
let len = stream.read_usize_varint().unwrap();
|
stream: &mut TcpStream,
|
||||||
|
read_body: F,
|
||||||
|
) -> Result<T> {
|
||||||
|
let len = stream.read_usize_varint()?;
|
||||||
|
|
||||||
let mut buf = vec![0u8; len];
|
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