diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f0607b0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,394 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Composition" +version = "0.1.0" +dependencies = [ + "ozelot", + "serde", + "serde_json", +] + +[[package]] +name = "addr2line" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "backtrace" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "curl" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "762e34611d2d5233a506a79072be944fddd057db2f18e04c0d6fa79e3fd466fd" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.31+curl-7.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcd62757cc4f5ab9404bc6ca9f0ae447e729a1403948ce5106bd588ceac6a3b0" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "error-chain" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "flate2" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" + +[[package]] +name = "libz-sys" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +dependencies = [ + "adler32", +] + +[[package]] +name = "netbuf" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41e95a536af3c11c6dcf16f30b8319aad291049e67c8aa4701f69f11761a9db" + +[[package]] +name = "object" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" + +[[package]] +name = "openssl" +version = "0.10.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ozelot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00748638603838e4c5907d8d10449ee9a7e548667af7c20c684888006f6657" +dependencies = [ + "byteorder", + "curl", + "error-chain", + "flate2", + "netbuf", + "openssl", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "proc-macro2" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "ryu" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "serde" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" + +[[package]] +name = "serde_derive" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vcpkg" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4063952 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "Composition" +version = "0.1.0" +authors = ["ElementG9 "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1.0.110" +serde_json = "1.0.53" +ozelot = "0.9.0" diff --git a/LICENSE b/LICENSE index edf0ac8..f7297e7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 ElementG9 +Copyright (c) 2020 Garen Tyler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f92e05f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +#![allow(non_snake_case)] + +pub static DEBUG_LOGGING: bool = true; +pub static SERVER_VERSION: &str = "1.15.2"; +pub static SERVER_PROTOCOL_VERSION: usize = 578; +pub static SERVER_MOTD: &str = "ligma balls"; + +// mod net; +// mod mctypes; + +fn main() -> std::io::Result<()> { + // Listener loops forever. + // net::start_listener().expect("could not start listener"); + // println!("Stopping server"); + use std::{io::prelude::*, net::TcpStream}; + let mut stream = TcpStream::connect("206.189.67.44:25565")?; +} diff --git a/src/mctypes.rs b/src/mctypes.rs new file mode 100644 index 0000000..e32f775 --- /dev/null +++ b/src/mctypes.rs @@ -0,0 +1,432 @@ +use std::io::prelude::*; +use std::net::TcpStream; + +pub fn read_byte(t: &mut TcpStream) -> std::io::Result { + let mut buffer = [0u8; 1]; + t.read_exact(&mut buffer)?; + Ok(buffer[0]) +} +pub fn get_bytes(v: Vec, l: usize) -> Box<[u8]> { + use std::collections::VecDeque; + let mut v = VecDeque::from(v); + while v.len() > l { + v.pop_front(); + } + while v.len() < l { + v.push_front(0u8); + } + let mut a = Vec::new(); + for b in v { + a.push(b); + } + a.into_boxed_slice() +} +pub fn io_error(s: &str) -> std::io::Error { + use std::io::{Error, ErrorKind}; + Error::new(ErrorKind::Other, s) +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum MCBoolean { + True, + False, +} +impl From for MCBoolean { + fn from(v: bool) -> MCBoolean { + if v { + MCBoolean::True + } else { + MCBoolean::False + } + } +} +#[allow(dead_code)] +impl MCBoolean { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let b = read_byte(t)?; + Ok(MCBoolean::from_bytes(vec![b])) + } + pub fn from_bytes(v: Vec) -> MCBoolean { + if get_bytes(v, 1)[0] == 0x01 { + MCBoolean::True + } else { + MCBoolean::False + } + } + pub fn to_bytes(&self) -> Vec { + match self { + MCBoolean::True => vec![0x01], + MCBoolean::False => vec![0x00], + } + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCByte { + pub value: i8 // -128 to 127 +} +impl From for MCByte { + fn from(v: i8) -> MCByte { + MCByte { + value: v + } + } +} +#[allow(dead_code)] +impl MCByte { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + Ok(MCByte::from_bytes(vec![read_byte(t)?])) + } + pub fn from_bytes(v: Vec) -> MCByte { + MCByte { + value: get_bytes(v, 1)[0] as i8 + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCUnsignedByte { + pub value: u8 // 0 to 255 +} +impl From for MCUnsignedByte { + fn from(v: u8) -> MCUnsignedByte { + MCUnsignedByte { + value: v + } + } +} +#[allow(dead_code)] +impl MCUnsignedByte { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + Ok(MCUnsignedByte::from_bytes(vec![read_byte(t)?])) + } + pub fn from_bytes(v: Vec) -> MCUnsignedByte { + MCUnsignedByte { + value: get_bytes(v, 1)[0] + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCShort { + pub value: i16 // -32768 to 32767 +} +impl From for MCShort { + fn from(v: i16) -> MCShort { + MCShort { + value: v + } + } +} +#[allow(dead_code)] +impl MCShort { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + bytes.push(read_byte(t)?); // MSD + bytes.push(read_byte(t)?); // LSD + Ok(MCShort::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCShort { + let mut a = [0u8; 2]; + a.copy_from_slice(&get_bytes(v, 2)); + MCShort { + value: i16::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCUnsignedShort { + pub value: u16 // 0 to 65535 +} +impl From for MCUnsignedShort { + fn from(v: u16) -> MCUnsignedShort { + MCUnsignedShort { + value: v + } + } +} +#[allow(dead_code)] +impl MCUnsignedShort { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + bytes.push(read_byte(t)?); // MSD + bytes.push(read_byte(t)?); // LSD + Ok(MCUnsignedShort::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCUnsignedShort { + let mut a = [0u8; 2]; + a.copy_from_slice(&get_bytes(v, 2)); + MCUnsignedShort { + value: u16::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCInt { + pub value: i32 // -2147483648 to 2147483647 +} +impl From for MCInt { + fn from(v: i32) -> MCInt { + MCInt { + value: v + } + } +} +#[allow(dead_code)] +impl MCInt { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + for _ in 0..4 { + bytes.push(read_byte(t)?); + } + Ok(MCInt::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCInt { + let mut a = [0u8; 4]; + a.copy_from_slice(&get_bytes(v, 4)); + MCInt { + value: i32::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCLong { + pub value: i64 // -9223372036854775808 to 9223372036854775807 +} +impl From for MCLong { + fn from(v: i64) -> MCLong { + MCLong { + value: v + } + } +} +#[allow(dead_code)] +impl MCLong { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + for _ in 0..8 { + bytes.push(read_byte(t)?); + } + Ok(MCLong::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCLong { + let mut a = [0u8; 8]; + a.copy_from_slice(&get_bytes(v, 8)); + MCLong { + value: i64::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCFloat { + pub value: f32 // 32-bit floating point number +} +impl From for MCFloat { + fn from(v: f32) -> MCFloat { + MCFloat { + value: v + } + } +} +#[allow(dead_code)] +impl MCFloat { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + for _ in 0..4 { + bytes.push(read_byte(t)?); + } + Ok(MCFloat::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCFloat { + let mut a = [0u8; 4]; + a.copy_from_slice(&get_bytes(v, 4)); + MCFloat { + value: f32::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCDouble { + pub value: f64 // 64-bit floating point number +} +impl From for MCDouble { + fn from(v: f64) -> MCDouble { + MCDouble { + value: v + } + } +} +#[allow(dead_code)] +impl MCDouble { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut bytes = Vec::new(); + for _ in 0..8 { + bytes.push(read_byte(t)?); + } + Ok(MCDouble::from_bytes(bytes)) + } + pub fn from_bytes(v: Vec) -> MCDouble { + let mut a = [0u8; 8]; + a.copy_from_slice(&get_bytes(v, 8)); + MCDouble { + value: f64::from_be_bytes(a) + } + } + pub fn to_bytes(&self) -> Vec { + self.value.to_be_bytes().to_vec() + } +} +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct MCVarInt { + pub value: i32 // Variable length 32-bit integer +} +impl From for MCVarInt { + fn from(v: i32) -> MCVarInt { + MCVarInt { + value: v + } + } +} +#[allow(dead_code)] +impl MCVarInt { + pub fn new(i: i32) -> MCVarInt { + MCVarInt { + value: i + } + } + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let mut numRead = 0; + let mut result = 0i32; + let mut read = 0u8; + let mut run_once = false; + while (read & 0b10000000) != 0 || !run_once { + run_once = true; + read = read_byte(t)?; + let value = (read & 0b01111111) as i32; + result |= value << (7 * numRead); + numRead += 1; + if numRead > 5 { + return Err(io_error("MCVarInt is too big")); + } + } + Ok(MCVarInt { + value: result + }) + } + pub fn from_bytes(_: Vec) -> MCVarInt { + panic!("Cannot construct MCVarInt from raw bytes"); + } + pub fn to_bytes(&self) -> Vec { + let mut value = self.value.clone(); + let mut run_once = false; + let mut out: Vec = Vec::new(); + while value != 0 || !run_once { + run_once = true; + let mut temp: u8 = (value & 0b01111111) as u8; + value >>= 7; + if value != 0 { + temp |= 0b10000000; + } + out.push(temp); + } + return out; + } +} +impl std::fmt::Display for MCVarInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} +#[allow(dead_code)] +#[derive(Debug)] +pub struct MCString { + pub value: String +} +#[allow(dead_code)] +impl MCString { + pub fn from_stream(t: &mut TcpStream) -> std::io::Result { + let length = MCVarInt::from_stream(t)?.value as u32; + let mut bytes = Vec::new(); + for _ in 0..length { + bytes.push(read_byte(t)?); + } + let value = String::from_utf8(bytes); + if value.is_ok() { + Ok(MCString { + value: value.unwrap() + }) + } else { + return Err(io_error("MCString contains invalid utf-8")); + } + } + pub fn from_bytes(_: Vec) -> MCString { + panic!("Cannot construct MCVarInt from raw bytes"); + } + pub fn to_bytes(&self) -> Vec { + let mut out = Vec::new(); + let length = MCVarInt { + value: self.value.len() as i32 + }; + for b in length.to_bytes() { + out.push(b); + } + for b in self.value.clone().into_bytes() { + out.push(b); + } + out + } +} +impl From<&str> for MCString { + fn from(s: &str) -> MCString { + MCString { + value: s.to_owned() + } + } +} +impl From for MCString { + fn from(s: String) -> MCString { + MCString { + value: s.clone() + } + } +} +impl Clone for MCString { + fn clone(&self) -> Self { + MCString { + value: self.value.clone() + } + } +} +impl std::fmt::Display for MCString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"{}\" ({} chars)", self.value, self.value.len()) + } +} diff --git a/src/net.rs b/src/net.rs new file mode 100644 index 0000000..bb7f4ac --- /dev/null +++ b/src/net.rs @@ -0,0 +1,217 @@ +pub static SERVER_LISTENER_ADDRESS: &str = "127.0.0.1:25565"; +pub static SOCKET_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); +extern crate serde; +extern crate serde_json; +use serde::Serialize; +use serde_json::json; +use std::io::prelude::*; +use std::net::{TcpStream, TcpListener}; +use std::thread; +use crate::mctypes::*; + +pub struct MCPacket { + pub id: MCVarInt, + pub data: Vec +} +#[allow(dead_code)] +impl MCPacket { + pub fn read_header(t: &mut TcpStream) -> std::io::Result<(MCVarInt, MCVarInt)> { + let length = MCVarInt::from_stream(t)?; + let id = MCVarInt::from_stream(t)?; + Ok((length, id)) + } + pub fn new(id: u8) -> MCPacket { + MCPacket { + id: MCVarInt::new(id as i32), + data: Vec::new(), + } + } + pub fn write(&mut self, v: Vec) { + for b in v { + self.data.push(b); + } + } + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + for b in MCVarInt::new(( + self.id.to_bytes().len() + + self.data.len() + ) as i32).to_bytes() { + bytes.push(b); + } + for b in self.id.to_bytes() { + bytes.push(b); + } + for b in &self.data { + bytes.push(*b); + } + bytes + } +} +#[allow(dead_code)] +#[derive(PartialEq, Debug)] +pub enum GameState { + Handshake, + Status, + Login, + Play, + Closed +} +#[allow(dead_code)] +pub struct GameConnection { + pub stream: TcpStream, + pub state: GameState, +} + +pub fn start_listener() -> std::io::Result<()> { + if crate::DEBUG_LOGGING { + println!("Started listener at {}", SERVER_LISTENER_ADDRESS); + } + let listener = TcpListener::bind(SERVER_LISTENER_ADDRESS)?; + // Spawn a new thread for each connection. + for stream in listener.incoming() { + let stream = stream?; + thread::Builder::new().name(format!("GameConnection {}", stream.peer_addr().unwrap())).spawn(move || -> std::io::Result<()> { + if crate::DEBUG_LOGGING { + println!("Client connected at {}", stream.peer_addr().unwrap()); + } + stream + .set_read_timeout(Some(SOCKET_TIMEOUT)) + .expect("set_read_timeout call failed"); + stream + .set_write_timeout(Some(SOCKET_TIMEOUT)) + .expect("set_write_timeout call failed"); + handle_client(GameConnection { + stream: stream, + state: GameState::Handshake, + })?; + Ok(()) + })?; + } + Ok(()) +} +pub fn handle_client(mut gc: GameConnection) -> std::io::Result<()> { + loop { + let (packet_length, packet_id) = MCPacket::read_header(&mut gc.stream)?; + if crate::DEBUG_LOGGING { + println!("Packet Length: {}, Packet ID: {}", packet_length.value, packet_id.value); + } + match gc.state { + GameState::Handshake => { + match packet_id.value { + 0x00 => { + handshake(&mut gc)?; + }, + _ => { + if crate::DEBUG_LOGGING { + println!("Unknown packet id {} in Handshake", packet_id); + } + } + } + }, + GameState::Login => { + match packet_id.value { + 0x00 => { + login(&mut gc)?; + }, + _ => { + if crate::DEBUG_LOGGING { + println!("Unknown packet id {} in Login", packet_id); + } + } + } + }, + GameState::Status => { + match packet_id.value { + 0x00 => { + // Send a response packet. + let mut packet = MCPacket::new(0x00); + let json_response = json!({ + "version": { + "name": crate::SERVER_VERSION, + "protocol": crate::SERVER_PROTOCOL_VERSION + }, + "players": { + "max": 100, + "online": 5, + "sample": [ + { + "name": "thinkofdeath", + "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20" + } + ] + }, + "description": { + "text": crate::SERVER_MOTD + } + // No favicon for now. + // "favicon": "data:image/png;base64," + }).to_string(); + packet.write(MCVarInt::new(json_response.len() as i32).to_bytes()); + packet.write(MCString::from(json_response.clone()).to_bytes()); + gc.stream.write(&packet.to_bytes())?; + println!("=== SENT SERVER RESPONSE ===\n{}", json_response); + }, + _ => { + if crate::DEBUG_LOGGING { + println!("Unknown packet id {} in Status", packet_id); + } + } + } + } + _ => { + if crate::DEBUG_LOGGING { + println!("Unknown gamestate {:?}", gc.state); + } + } + } + } +} +pub fn handshake(gc: &mut GameConnection) -> std::io::Result<()> { + // C->S Handshake + let protocol_version = MCVarInt::from_stream(&mut gc.stream)?; + let server_address = MCString::from_stream(&mut gc.stream)?; + let server_port = MCUnsignedShort::from_stream(&mut gc.stream)?; + let next_state = match MCVarInt::from_stream(&mut gc.stream)?.value { + 1 => GameState::Status, + 2 => GameState::Login, + _ => { + if crate::DEBUG_LOGGING { + println!("Unknown next_state in handshake"); + } + GameState::Handshake + } + }; + if crate::DEBUG_LOGGING { + println!("Handshake: Protocol Version: {}, Server Address: {}:{}, Next State: {:?}", + protocol_version.value, + server_address.value, + server_port.value, + next_state + ); + } + gc.state = next_state; + Ok(()) +} +pub fn login(gc: &mut GameConnection) -> std::io::Result<()> { + // C->S Login Start + let player_username = MCString::from_stream(&mut gc.stream)?; + if crate::DEBUG_LOGGING { + println!("Login: Player Username: {}", player_username); + } + // S->C Encryption Request + // C->S Encryption Response + // S->C Set Compression + // S->C Login Success + let mut login_success = MCPacket::new(0x02); + login_success.write(MCString::from("00000000-0000-0000-0000-000000000000").to_bytes()); // UUID + login_success.write(player_username.to_bytes()); + gc.stream.write(&login_success.to_bytes())?; + // Move to Play state + gc.state = GameState::Play; + play(gc)?; + Ok(()) +} +pub fn play(gc: &mut GameConnection) -> std::io::Result<()> { + Ok(()) +}