diff --git a/Cargo.lock b/Cargo.lock index 403f186f3..029703abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,54 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "colored" version = "2.1.0" @@ -18,6 +60,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -42,12 +93,43 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", +] + [[package]] name = "itoa" version = "1.0.11" @@ -72,6 +154,16 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "simd-adler32", +] + [[package]] name = "mio" version = "1.0.1" @@ -91,6 +183,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -100,6 +201,19 @@ dependencies = [ "libc", ] +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -119,8 +233,10 @@ dependencies = [ name = "pumpkin" version = "0.1.0" dependencies = [ + "base64", "byteorder", "crossbeam-channel", + "image", "log", "mio", "serde", @@ -175,6 +291,12 @@ dependencies = [ "serde", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simple_logger" version = "5.0.0" diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 5c6caf2fc..926dc108d 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -8,6 +8,9 @@ edition = "2021" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +base64 = "0.22.1" +image = { version = "0.25", default-features = false, features = ["png"]} + simple_logger = "5.0.0" log = "0.4" byteorder = "1" diff --git a/pumpkin/icon.png b/pumpkin/icon.png new file mode 100644 index 000000000..944a32268 Binary files /dev/null and b/pumpkin/icon.png differ diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 98ff59aa8..32ae90527 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -1,10 +1,11 @@ + + use crate::protocol::{ client::status::{CPingResponse, CStatusResponse}, server::{ handshake::SHandShake, status::{SPingRequest, SStatusRequest}, }, - StatusResponse, Version, }; use super::Client; @@ -26,15 +27,9 @@ impl ClientPacketProcessor for Client { fn handle_status_request(&mut self, _status_request: SStatusRequest) { dbg!("sending status"); - let response = StatusResponse { - version: Version { - version: "1.21".into(), - protocol: 767, - }, - description: "Pumpkin Server".into(), - }; + self.send_packet(CStatusResponse::new( - serde_json::to_string(&response).unwrap(), + serde_json::to_string(&self.server.status_response).unwrap(), )) } diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 608b6aa8e..dae67ea28 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -1,14 +1,18 @@ use std::{ collections::VecDeque, io::{self, Write}, + rc::Rc, }; -use crate::protocol::{ - server::{ - handshake::SHandShake, - status::{SPingRequest, SStatusRequest}, +use crate::{ + protocol::{ + server::{ + handshake::SHandShake, + status::{SPingRequest, SStatusRequest}, + }, + ClientPacket, RawPacket, }, - ClientPacket, RawPacket, + server::Server, }; use crate::protocol::{bytebuf::buffer::ByteBuffer, ConnectionState}; @@ -21,6 +25,7 @@ pub mod player; use client_packet::ClientPacketProcessor; pub struct Client { + pub server: Rc, pub connection_state: ConnectionState, pub closed: bool, pub connection: TcpStream, @@ -29,8 +34,9 @@ pub struct Client { } impl Client { - pub fn new(connection: TcpStream) -> Self { + pub fn new(server: Rc, connection: TcpStream) -> Self { Self { + server, connection_state: ConnectionState::HandShake, connection, closed: false, diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 9e03653fb..232674b21 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -8,10 +8,14 @@ const SERVER: Token = Token(0); pub mod client; pub mod protocol; +pub mod server; #[cfg(not(target_os = "wasi"))] fn main() -> io::Result<()> { + use std::rc::Rc; + use client::{interrupted, Client}; + use server::Server; simple_logger::SimpleLogger::new().init().unwrap(); @@ -22,11 +26,11 @@ fn main() -> io::Result<()> { // Setup the TCP server socket. let addr = "127.0.0.1:25565".parse().unwrap(); - let mut server = TcpListener::bind(addr)?; + let mut listener = TcpListener::bind(addr)?; // Register the server with poll we can receive events for it. poll.registry() - .register(&mut server, SERVER, Interest::READABLE)?; + .register(&mut listener, SERVER, Interest::READABLE)?; // Map of `Token` -> `TcpStream`. let mut connections = HashMap::new(); @@ -35,7 +39,8 @@ fn main() -> io::Result<()> { log::info!("You can connect to the server using `nc`:"); log::info!(" $ nc 127.0.0.1 9000"); - log::info!("You'll see our welcome message and anything you type will be printed here."); + + let server = Rc::new(Server::new()); loop { if let Err(err) = poll.poll(&mut events, None) { @@ -50,7 +55,7 @@ fn main() -> io::Result<()> { SERVER => loop { // Received an event for the TCP server socket, which // indicates we can accept an connection. - let (mut connection, address) = match server.accept() { + let (mut connection, address) = match listener.accept() { Ok((connection, address)) => (connection, address), Err(e) if e.kind() == io::ErrorKind::WouldBlock => { // If we get a `WouldBlock` error we know our @@ -75,7 +80,7 @@ fn main() -> io::Result<()> { Interest::READABLE.add(Interest::WRITABLE), )?; - connections.insert(token, Client::new(connection)); + connections.insert(token, Client::new(Rc::clone(&server), connection)); }, token => { // Maybe received an event for a TCP connection. diff --git a/pumpkin/src/protocol/mod.rs b/pumpkin/src/protocol/mod.rs index ad5f2e82b..56fd39a56 100644 --- a/pumpkin/src/protocol/mod.rs +++ b/pumpkin/src/protocol/mod.rs @@ -44,11 +44,26 @@ pub trait ClientPacket { #[derive(Serialize, Deserialize)] pub struct StatusResponse { pub version: Version, + pub players: Players, pub description: String, - // Players, favicon ... + pub favicon: String, // data:image/png;base64, + // Players, favicon ... } #[derive(Serialize, Deserialize)] pub struct Version { - pub version: String, + pub name: String, pub protocol: u32, } + +#[derive(Serialize, Deserialize)] +pub struct Players { + pub max: u32, + pub online: u32, + pub sample: Sample, +} + +#[derive(Serialize, Deserialize)] +pub struct Sample { + pub name: String, + pub id: String, // uuid +} diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs new file mode 100644 index 000000000..5d0527083 --- /dev/null +++ b/pumpkin/src/server.rs @@ -0,0 +1,53 @@ +use std::io::Cursor; + +use base64::{engine::general_purpose, Engine}; + +use crate::protocol::{Players, Sample, StatusResponse, Version}; + +pub struct Server { + pub status_response: StatusResponse, +} + +impl Default for Server { + fn default() -> Self { + Self::new() + } +} + +impl Server { + pub fn new() -> Self { + let path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png"); + + let status_response = StatusResponse { + version: Version { + name: "1.21".into(), + protocol: 767, + }, + players: Players { + max: 20, + online: 0, + sample: Sample { + name: "".into(), + id: "".into(), + }, + }, + description: "Pumpkin Server".into(), + favicon: Self::load_icon(path), + }; + Self { status_response } + } + + pub fn load_icon(path: &str) -> String { + let mut icon = match image::open(path).map_err(|e| panic!("error loading icon: {}", e)) { + Ok(icon) => icon, + Err(_) => return "".into(), + }; + icon = icon.resize_exact(64, 64, image::imageops::FilterType::Triangle); + let mut image = Vec::new(); + icon.write_to(&mut Cursor::new(&mut image), image::ImageFormat::Png) + .unwrap(); + let mut result = "data:image/png;base64,".to_owned(); + general_purpose::STANDARD.encode_string(image, &mut result); + result + } +}