diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | rustfmt.toml | 6 | ||||
-rw-r--r-- | src/main.rs | 260 |
4 files changed, 231 insertions, 40 deletions
@@ -209,7 +209,6 @@ dependencies = [ [[package]] name = "mcproto-rs" version = "0.2.0" -source = "git+https://github.com/Web-44/mcproto-rs.git#2572b329cc18a0413dea19297bab805be2b9f2ab" dependencies = [ "base64", "rand", @@ -20,9 +20,11 @@ version = "*" [dependencies.mcproto-rs] version = "*" +features = ["default", "v1_19_3"] [patch.crates-io.mcproto-rs] -git = "https://github.com/Web-44/mcproto-rs.git" +# git = "https://github.com/Web-44/mcproto-rs.git" +path = "../mcproto-rs" [patch.crates-io.craftio-rs] git = "https://github.com/romangraef/craftio-rs.git" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..2fd9f19 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,6 @@ +format_macro_matchers = true +reorder_imports = true +format_macro_bodies = true +skip_macro_invocations = [] +imports_indent = "Visual" +indent_style = "Visual"
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8f925e3..b7f1203 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,14 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream}; use anyhow::Result; use craftio_rs::{CraftIo, CraftSyncReader, CraftSyncWriter, CraftTcpConnection}; -use mcproto_rs::protocol::State; -use mcproto_rs::status::{StatusFaviconSpec, StatusPlayersSpec, StatusSpec, StatusVersionSpec}; -use mcproto_rs::types::{Chat, TextComponent}; -use mcproto_rs::types::Chat::Text; -use mcproto_rs::v1_19::{HandshakeNextState, Packet759, RawPacket759, StatusPingSpec, StatusPongSpec, StatusResponseSpec}; -use mcproto_rs::v1_19::Packet759Kind::{Handshake, PlayServerPlayerAbilities}; +use mcproto_rs::nbt::{NamedTag, Tag}; +use mcproto_rs::protocol::{HasPacketKind, State}; +use mcproto_rs::status::{StatusPlayersSpec, StatusSpec, StatusVersionSpec}; +use mcproto_rs::types::{Chat, NamedNbtTag}; +use mcproto_rs::uuid::UUID4; +use mcproto_rs::v1_19_3::{GameMode, HandshakeNextState, LoginPlaySpec, LoginSuccessSpec, + Packet761, Packet761Kind, PingRequestSpec, PingResponseSpec, + PreviousGameMode, RawPacket761, StatusResponseSpec}; use tokio; pub struct MinecraftClient { @@ -19,29 +21,128 @@ impl MinecraftClient { Self { connection } } - pub fn from_stream(stream: TcpStream) -> anyhow::Result<Self> { + pub fn from_stream(stream: TcpStream) -> Result<Self> { Ok(Self { - connection: CraftTcpConnection::from_std(stream, mcproto_rs::protocol::PacketDirection::ServerBound)?, + connection: CraftTcpConnection::from_std( + stream, + mcproto_rs::protocol::PacketDirection::ServerBound, + )?, }) } - pub async fn read_next_packet(&mut self) -> Result<Option<Packet759>> { - if let Some(raw) = self.connection.read_packet::<RawPacket759>()? { + pub async fn read_next_packet(&mut self) -> Result<Option<Packet761>> { + if let Some(raw) = self.connection.read_packet::<RawPacket761>()? { println!("Client -> Server: {:?}", raw); Ok(Some(raw)) } else { Ok(None) } } - pub async fn send_packet(&mut self, packet: Packet759) -> Result<()> { + pub async fn send_packet(&mut self, packet: Packet761) -> Result<()> { + println!("Server -> Client: {:?}", packet); self.connection.write_packet(packet)?; Ok(()) } + pub async fn wait_for_packet(&mut self, packet_kind: Packet761Kind) -> Result<Packet761> { + loop { + if let Some(packet) = self.read_next_packet().await? { + if packet.kind() == packet_kind { + return Ok(packet); + } + println!("Skipping packet {:?}", packet); + } + } + } +} + +macro_rules! await_packet { + ($packet_type:ident, $conn:expr) => { + assert_packet!($packet_type, + $conn.wait_for_packet(Packet761Kind::$packet_type).await?) + }; } macro_rules! assert_packet { - ($packet_type:ident, $obj:expr) => {if let Packet759::$packet_type(packet_data) = $obj { packet_data } else {panic!("Expected packet of type {}", stringify!($packet_type))}}; + ($packet_type:ident, $obj:expr) => { + if let Packet761::$packet_type(packet_data) = $obj { + packet_data + } else { + panic!("Expected packet of type {}", stringify!($packet_type)) + } + }; + ($packet_type:ident, $conn:expr, $errmgs:literal) => { + assert_packet!($packet_type, + $conn.read_next_packet() + .await? + .ok_or(anyhow::anyhow!("Missing packet"))?) + }; +} + +trait ShittyToNbt { + fn to_nbt(self) -> Tag; +} + +impl ShittyToNbt for String { + fn to_nbt(self) -> Tag { + Tag::String(self) + } +} + +impl ShittyToNbt for &str { + fn to_nbt(self) -> Tag { + Tag::String(self.into()) + } +} + +impl ShittyToNbt for i32 { + fn to_nbt(self) -> Tag { + Tag::Int(self) + } +} + +impl ShittyToNbt for i64 { + fn to_nbt(self) -> Tag { + Tag::Long(self) + } +} + +impl ShittyToNbt for bool { + fn to_nbt(self) -> Tag { + Tag::Byte(if self { 1 } else { 0 }) + } } +impl ShittyToNbt for Tag { + fn to_nbt(self) -> Tag { + self + } +} + +macro_rules! nbt { + { {$($name:literal :: $value:tt),* $(,)? } } => { + Tag::Compound(vec![ + $( + nbt!($value).with_name($name) + ),* + ]) + }; + { $($name:literal :: $value:tt),* $(,)? } => { + Tag::Compound(vec![ + $( + nbt!($value).with_name($name) + ),* + ]) + }; + ([ $($value:tt),* $(,)? ]) => { + Tag::List(vec![ + $( + nbt!($value) + ),* + ]) + }; + (($e:expr)) => { + ShittyToNbt::to_nbt($e) + }; +} #[tokio::main] async fn main() -> Result<()> { println!("Starting server"); @@ -49,30 +150,116 @@ async fn main() -> Result<()> { loop { if let Ok((socket, address)) = bind.accept() { println!("Connection accepted from {}", address); - let mut client = MinecraftClient::from_stream(socket)?; - if let Err(x) = handle_conn(client).await { - println!("Error: {:?}", x); - } + let client = MinecraftClient::from_stream(socket)?; + tokio::spawn(async { + if let Err(x) = handle_conn(client).await { + println!("Error: {:?}", x); + } + }); } } } async fn handle_conn(mut client: MinecraftClient) -> Result<()> { - let hs = assert_packet!(Handshake, client.read_next_packet().await?.ok_or(anyhow::anyhow!("Missing packet"))?); - println!("Hs: {:?}", hs); + let hs = assert_packet!(Handshake, client, "Missing packet"); if hs.next_state == HandshakeNextState::Login { client.connection.set_state(State::Login); - let loginstart = assert_packet!(LoginStart, client.read_next_packet().await?.ok_or(anyhow::anyhow!("Missing packet"))?); - println!("Login: {:?}", loginstart) + let loginstart = assert_packet!(LoginStart, + client.read_next_packet() + .await? + .ok_or(anyhow::anyhow!("Missing packet"))?); + println!("Login: {:?}", loginstart); + let uuid = UUID4::random(); + client.send_packet(Packet761::LoginSuccess(LoginSuccessSpec { uuid, + username: + loginstart.name, + properties: + vec![].into() })) + .await?; + let registry_doex = nbt! { + "minecraft:dimension_type" :: { + "type" :: ("minecraft:dimension_type"), + "value" :: [{ + "id" :: (0), + "name" :: ("minecraft:overworld"), + "element" :: { + "ambient_light" :: (false), + "bed_works" :: (true), + "coordinate_scale" :: (1), + "effects" :: ("minecraft:overworld"), + "has_ceiling" :: (false), + "has_skylight" :: (true), + "has_raids" :: (false), + "height" :: (384), + "infiniburn" :: ("#minecraft:infiniburn_overworld"), + "logical_height" :: (384), + "min_y" :: (-64), + "monster_spawn_block_light_limit" :: (0), + "monster_spawn_light_level" :: { + "type" :: ("minecraft:uniform"), + "value" :: { + "max_inclusive" :: (7), + "min_inclusive" :: (0), + }, + }, + "natural" :: (true), + "piglin_safe" :: (false), + "respawn_anchor_works" :: (false), + "ultrawarm" :: (false), + + }, + }] + }, + }; + client.connection.set_state(State::Play); + client + .send_packet(Packet761::LoginPlay(LoginPlaySpec { + entity_id: 0, + is_hardcore: false, + gamemode: GameMode::Survival, + previous_gamemode: PreviousGameMode::NoPrevious, + dimension_names: vec!["world".into()].into(), + registry_codex: NamedNbtTag { + root: registry_doex.with_name("root"), + }, + dimension_type: "minecraft:overworld".into(), + dimension_name: "world".into(), + hashed_seed: 0, + max_players: 10.into(), + view_distance: 10.into(), + simulation_distance: 10.into(), + reduced_debug_info: false, + enable_respawn_screen: false, + is_debug: false, + is_flat: false, + death_location: None, + })) + .await?; + let client_information = await_packet!(ClientInformation, client); + println!("Client Information: {:?}", client_information); + anyhow::bail!("TODO"); + } else if hs.next_state == HandshakeNextState::Status { + return handle_status(client).await; } else { - client.connection.set_state(State::Status); - loop { - println!("Polling status packet"); - match client.read_next_packet().await?.ok_or(anyhow::anyhow!("Missing packet"))? { - Packet759::StatusRequest(_) => { - client.send_packet(Packet759::StatusResponse(StatusResponseSpec { + anyhow::bail!("Unknown next status") + } +} + +async fn handle_status(mut client: MinecraftClient) -> Result<()> { + client.connection.set_state(State::Status); + loop { + match client.read_next_packet() + .await? + .ok_or(anyhow::anyhow!("Missing packet"))? + { + Packet761::StatusRequest(_) => { + client + .send_packet(Packet761::StatusRespone(StatusResponseSpec { response: StatusSpec { - version: Some(StatusVersionSpec { name: "1.19 mgasi".to_string(), protocol: 759 }), + version: Some(StatusVersionSpec { + name: "1.19.3 mgasi".to_string(), + protocol: 761, + }), players: StatusPlayersSpec { max: 100, online: 0, @@ -81,18 +268,15 @@ async fn handle_conn(mut client: MinecraftClient) -> Result<()> { description: Chat::from_text("hehehe"), favicon: None, }, - })).await?; - } - Packet759::StatusPing(StatusPingSpec { payload }) => { - client.send_packet(Packet759::StatusPong(StatusPongSpec { - payload, - })).await?; - return Ok(()) - } - _ => anyhow::bail!("") + })) + .await?; } + Packet761::PingRequest(PingRequestSpec { payload }) => { + client.send_packet(Packet761::PingResponse(PingResponseSpec { payload })) + .await?; + return Ok(()); + } + _ => anyhow::bail!("Unexpected packet during status communication"), } } - Ok(()) } - |