From d37a71d3f239e29983ec48337fa8b97b7a21588d Mon Sep 17 00:00:00 2001 From: Shrecknt <58538423+Shrecknt@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:59:51 -0800 Subject: [PATCH] joiner start + virtual fs testing server --- Cargo.lock | 14 +++++- Cargo.toml | 13 +++--- crates/bunger/Cargo.toml | 2 + crates/bunger/src/lib.rs | 73 +++++++++++++++++++++++++++++--- crates/bunger/src/main.rs | 8 +++- crates/bunger/src/varint/mod.rs | 13 ++++++ crates/bunger/src/varint/sync.rs | 54 +++++++++++++++++++++++ crates/config/src/lib.rs | 12 ++++++ crates/ram_server/Cargo.toml | 8 ++++ crates/ram_server/src/lib.rs | 73 ++++++++++++++++++++++++++++++++ crates/ram_server/src/main.rs | 7 +++ 11 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 crates/bunger/src/varint/sync.rs create mode 100644 crates/ram_server/Cargo.toml create mode 100644 crates/ram_server/src/lib.rs create mode 100644 crates/ram_server/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index aa0038c..560ae94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -852,9 +852,11 @@ dependencies = [ "config", "eyre", "flate2", + "ram_server", "serde", "serde_json", "tokio", + "uuid", ] [[package]] @@ -2943,6 +2945,14 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ram_server" +version = "0.1.0" +dependencies = [ + "config", + "semver", +] + [[package]] name = "rand" version = "0.8.5" @@ -3330,9 +3340,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 5336fc9..a3fa1e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,16 +3,18 @@ resolver = "2" members = ["crates/*"] [workspace.dependencies] -snowstorm = { version = "0.1.0", path = "crates/snowstorm" } bunger = { version = "0.1.0", path = "crates/bunger" } -database = { version = "0.1.0", path = "crates/database" } common = { version = "0.1.0", path = "crates/common" } +config = { version = "0.1.0", path = "crates/config" } +database = { version = "0.1.0", path = "crates/database" } +discord = { version = "0.1.0", path = "crates/discord" } io = { version = "0.1.0", path = "crates/io" } jwt = { version = "0.1.0", path = "crates/jwt" } -web = { version = "0.1.0", path = "crates/web" } +mowojang = { version = "0.1.0", path = "crates/mowojang" } +ram_server = { version = "0.1.0", path = "crates/ram_server" } scheduling = { version = "0.1.0", path = "crates/scheduling" } -discord = { version = "0.1.0", path = "crates/discord" } -config = { version = "0.1.0", path = "crates/config" } +snowstorm = { version = "0.1.0", path = "crates/snowstorm" } +web = { version = "0.1.0", path = "crates/web" } axum = { version = "0.6.20", features = ["macros", "ws", "headers"] } bcrypt = "0.15.0" boringtun = "0.6.0" @@ -60,3 +62,4 @@ pretty-duration = "0.1.1" toml = "0.8.10" smart-default = "0.7.1" flate2 = { version = "1.0.17", features = ["zlib"], default-features = false } +semver = "1.0.22" diff --git a/crates/bunger/Cargo.toml b/crates/bunger/Cargo.toml index 1ded195..038472d 100644 --- a/crates/bunger/Cargo.toml +++ b/crates/bunger/Cargo.toml @@ -5,8 +5,10 @@ edition = "2021" [dependencies] config = { workspace = true } +ram_server = { workspace = true } flate2 = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } eyre = { workspace = true } +uuid = { workspace = true } diff --git a/crates/bunger/src/lib.rs b/crates/bunger/src/lib.rs index d155688..e39ed01 100644 --- a/crates/bunger/src/lib.rs +++ b/crates/bunger/src/lib.rs @@ -3,9 +3,12 @@ use std::{ io::{Cursor, Read}, net::SocketAddrV4, }; -use tokio::{io::AsyncReadExt, net::TcpStream}; - -use crate::varint::AsyncVarint; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; +use uuid::uuid; +use varint::{AsyncVarint, SyncVarintWrite}; pub mod varint; @@ -20,10 +23,10 @@ impl JoinData { } } -pub async fn join(addr: SocketAddrV4) -> JoinData { +pub async fn join(addr: SocketAddrV4, version: i32) -> JoinData { let mut join_data = JoinData::new(); - let res = join_internal(addr, &mut join_data).await; + let res = join_internal(addr, version, &mut join_data).await; if let Err(err) = res { join_data.error = Some(err.to_string()); } @@ -31,12 +34,68 @@ pub async fn join(addr: SocketAddrV4) -> JoinData { join_data } -async fn join_internal(addr: SocketAddrV4, join_data: &mut JoinData) -> eyre::Result<()> { +async fn join_internal( + addr: SocketAddrV4, + version: i32, + join_data: &mut JoinData, +) -> eyre::Result<()> { let mut stream = TcpStream::connect(addr).await?; + let mut packet = Vec::new(); + packet.write_varint(0x00)?; + packet.write_varint(version)?; + let addr = b"shrecked.dev"; + packet.write_varint(addr.len() as i32)?; + packet.write_all(addr).await?; + packet.write_u16(42069).await?; + packet.write_varint(2)?; + write_packet(&mut stream, &packet, false).await?; + + let mut packet = Vec::new(); + packet.write_varint(0x00)?; + let username = b"Test_bot"; + packet.write_varint(username.len() as i32)?; + packet.write_all(username).await?; + if version > 47 { + // TODO: not sure when uuid field was added but its not in 1.8.x (47) + let player_uuid = uuid!("36d4d63f-7268-4879-a57f-122e9df006c2").as_bytes(); + packet.write_all(player_uuid).await?; + } + write_packet(&mut stream, &packet, false).await?; + let packet = read_packet(&mut stream, false).await?; - println!("packet = {packet:?}"); + println!("got packet = {packet:?}"); + match packet.0 { + 0 => { + // kick + } + 1 => { + // encryption, online mode probably + } + 3 => { + // compression, offline mode probably + } + _ => { + // unknown packet + } + }; + + Ok(()) +} + +async fn write_packet(stream: &mut TcpStream, packet: &[u8], compressed: bool) -> eyre::Result<()> { + let mut to_send = Vec::new(); + + if compressed { + todo!() + } else { + to_send.write_varint(packet.len() as i32)?; + to_send.write_all(packet).await?; + } + + println!("sending packet = {to_send:?}"); + stream.write_all(&to_send).await?; Ok(()) } diff --git a/crates/bunger/src/main.rs b/crates/bunger/src/main.rs index 3cc80a3..f1340e6 100644 --- a/crates/bunger/src/main.rs +++ b/crates/bunger/src/main.rs @@ -2,7 +2,13 @@ use std::{net::SocketAddrV4, str::FromStr}; #[tokio::main] async fn main() -> eyre::Result<()> { - let data = bunger::join(SocketAddrV4::from_str("130.61.123.184:25565").unwrap()).await; + let mut server = + ram_server::run_server("1.8.9", 25569, false).expect("unable to start server :<"); + + let data = bunger::join(SocketAddrV4::from_str("127.0.0.1:25569").unwrap(), 47).await; + + server.kill().expect("Unable to kill child process"); + println!("data = {data:?}"); Ok(()) diff --git a/crates/bunger/src/varint/mod.rs b/crates/bunger/src/varint/mod.rs index 255ecfe..d160e02 100644 --- a/crates/bunger/src/varint/mod.rs +++ b/crates/bunger/src/varint/mod.rs @@ -1,6 +1,7 @@ use std::future::Future; pub mod r#async; +pub mod sync; pub trait AsyncVarint { type Error; @@ -10,3 +11,15 @@ pub trait AsyncVarint { fn read_varint(&mut self) -> impl Future> + Send; fn read_varint_len(&mut self) -> impl Future> + Send; } + +pub trait SyncVarintRead { + type Error; + + fn read_varint(&mut self) -> Result; + fn read_varint_len(&mut self) -> Result<(u32, i32), Self::Error>; +} +pub trait SyncVarintWrite { + type Error; + + fn write_varint(&mut self, varint: i32) -> Result<(), Self::Error>; +} diff --git a/crates/bunger/src/varint/sync.rs b/crates/bunger/src/varint/sync.rs new file mode 100644 index 0000000..09c3a9e --- /dev/null +++ b/crates/bunger/src/varint/sync.rs @@ -0,0 +1,54 @@ +use super::{SyncVarintRead, SyncVarintWrite}; +use std::io::{Read, Write}; + +impl SyncVarintRead for T { + type Error = tokio::io::Error; + + fn read_varint(&mut self) -> Result { + Ok(self.read_varint_len()?.1) + } + + fn read_varint_len(&mut self) -> Result<(u32, i32), Self::Error> { + let mut buf = [0u8]; + let mut res = 0; + let mut count = 0u32; + + loop { + self.read_exact(&mut buf)?; + res |= (buf[0] as i32 & (0b0111_1111_i32)) + .checked_shl(7 * count) + .ok_or(tokio::io::ErrorKind::Other)?; + + count += 1; + if count > 5 { + break Err(tokio::io::ErrorKind::Other.into()); + } else if (buf[0] & (0b1000_0000_u8)) == 0 { + break Ok((count, res)); + } + } + } +} + +impl SyncVarintWrite for T { + type Error = tokio::io::Error; + + fn write_varint(&mut self, varint: i32) -> Result<(), Self::Error> { + let mut buffer = [0]; + let mut value = varint; + + if value == 0 { + self.write_all(&buffer)?; + } + + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i32::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + self.write_all(&buffer)?; + } + + Ok(()) + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index b895bf3..c5d6e78 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -29,6 +29,9 @@ pub struct Config { #[serde(default)] pub mowojang: MowojangConfig, + + #[serde(default)] + pub ram_server: RamServerConfig, } impl Config { @@ -90,6 +93,15 @@ pub struct MowojangConfig { pub internal_api_uri: Option, } +#[derive(Deserialize, SmartDefault)] +pub struct RamServerConfig { + pub java_8_path: PathBuf, + pub java_11_path: PathBuf, + pub java_17_path: PathBuf, + pub server_jar_path: PathBuf, + pub temp_fs_path: PathBuf, +} + #[derive(Deserialize, SmartDefault)] pub struct OauthConfig { #[serde(default)] diff --git a/crates/ram_server/Cargo.toml b/crates/ram_server/Cargo.toml new file mode 100644 index 0000000..5b9a436 --- /dev/null +++ b/crates/ram_server/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ram_server" +version = "0.1.0" +edition = "2021" + +[dependencies] +config = { workspace = true } +semver = { workspace = true } diff --git a/crates/ram_server/src/lib.rs b/crates/ram_server/src/lib.rs new file mode 100644 index 0000000..f709fcb --- /dev/null +++ b/crates/ram_server/src/lib.rs @@ -0,0 +1,73 @@ +use std::{ + fs, + io::{BufRead, BufReader}, + process::{Child, Command, Stdio}, +}; + +pub fn run_server(version: impl ToString, port: u16, online_mode: bool) -> Option { + let config = config::get(); + let version = version.to_string(); + let java_path = { + let version = semver::Version::parse(&version).expect("Unable to parse version"); + match version.minor { + ..=17 => &config.ram_server.java_8_path, + 18.. => &config.ram_server.java_17_path, + #[allow(unreachable_patterns)] + _ => &config.ram_server.java_11_path, + } + }; + run_setup(); + { + let mut from_path = config.ram_server.server_jar_path.clone().into_os_string(); + from_path.push(format!("{version}.jar")); + let mut to_path = config.ram_server.temp_fs_path.clone().into_os_string(); + to_path.push(format!("{version}.jar")); + fs::copy(from_path, to_path).expect("Unable to copy server jar to temp dir"); + } + { + let mut eula_path = config.ram_server.temp_fs_path.clone().into_os_string(); + eula_path.push("eula.txt"); + fs::write(eula_path, b"eula=true").expect("Unable to write to eula file"); + } + { + let mut server_properties_path = config.ram_server.temp_fs_path.clone().into_os_string(); + server_properties_path.push("server.properties"); + fs::write( + server_properties_path, + format!("server-port={port}\nonline-mode={online_mode}"), + ) + .expect("Unable to write to server properties file"); + } + + let mut res = Command::new(java_path) + .current_dir(&config.ram_server.temp_fs_path) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .stdin(Stdio::null()) + .arg("-jar") + .arg(format!("{version}.jar")) + .arg("--nogui") + .spawn() + .expect("Could not start process"); + + let done_str = "[Server thread/INFO]: Done"; + let stdout = res.stdout.take().expect("Could not take stdout"); + + let reader = BufReader::new(stdout); + let mut lines = reader.lines(); + while let Some(Ok(line)) = lines.next() { + println!("{line}"); + if line.contains(done_str) { + return Some(res); + } + } + + res.kill().expect("Count not kill child process"); + None +} + +fn run_setup() { + let path = &config::get().ram_server.temp_fs_path; + fs::remove_dir_all(path).expect("Unable to delete old temp dir"); + fs::create_dir(path).expect("Unable to create temp dir"); +} diff --git a/crates/ram_server/src/main.rs b/crates/ram_server/src/main.rs new file mode 100644 index 0000000..9e79a2e --- /dev/null +++ b/crates/ram_server/src/main.rs @@ -0,0 +1,7 @@ +fn main() { + println!("Hello, World!"); + let mut handle = + ram_server::run_server("1.20.4-paper", 25565, true).expect("Could not start server :<"); + println!("server started!"); + handle.kill().expect("Unable to kill server process"); +}