summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/connect.rs79
-rw-r--r--src/convention.rs189
-rw-r--r--src/main.rs453
-rw-r--r--src/nbtdsl.rs1
-rw-r--r--src/player.rs48
5 files changed, 467 insertions, 303 deletions
diff --git a/src/connect.rs b/src/connect.rs
index 1feb03e..cfebb29 100644
--- a/src/connect.rs
+++ b/src/connect.rs
@@ -1,9 +1,14 @@
+use std::future::Future;
+
use anyhow::Result;
-use craftio_rs::{CraftAsyncReader, CraftAsyncWriter, CraftTokioConnection};
-use mcproto_rs::protocol::HasPacketKind;
+use craftio_rs::{CraftAsyncReader, CraftAsyncWriter, CraftIo, CraftReader, CraftTokioConnection, CraftWrapper, CraftWriter};
+use mcproto_rs::protocol::{HasPacketKind, PacketDirection, State};
use mcproto_rs::v1_19_3::{Packet761, Packet761Kind, RawPacket761};
-use tokio::io::BufReader;
+use tokio::io::{AsyncRead, BufReader};
+use tokio::net::tcp::OwnedReadHalf;
use tokio::net::TcpStream;
+use tokio::sync::mpsc;
+use tokio::task::JoinHandle;
#[macro_export]
macro_rules! await_packet {
@@ -34,32 +39,69 @@ macro_rules! assert_packet {
)
};
}
+#[derive(Debug)]
+enum WriterAction {
+ Send(Packet761),
+ SetState(State),
+}
pub struct MinecraftClient {
- pub connection: CraftTokioConnection,
+ send_handle: JoinHandle<()>,
+ send_tx: mpsc::UnboundedSender<WriterAction>,
+ craft_reader: CraftReader<BufReader<OwnedReadHalf>>,
}
impl MinecraftClient {
pub fn from_stream(stream: TcpStream) -> Self {
let (read, write) = stream.into_split();
let bufread = BufReader::new(read);
+ let mut craft_writer = CraftWriter::wrap(write, PacketDirection::ClientBound);
+ let mut craft_reader = CraftReader::wrap(bufread, PacketDirection::ServerBound);
+ let (send_tx, mut send_recv) = mpsc::unbounded_channel::<WriterAction>();
+ let send_handle = tokio::spawn(async move {
+ loop {
+ match send_recv.recv().await {
+ None => break,
+ Some(WriterAction::Send(packet)) => {
+ if let Err(err) = craft_writer.write_packet_async(packet).await {
+ println!("Failed to send packet {:?}", err);
+ }
+ }
+ Some(WriterAction::SetState(state)) => {
+ craft_writer.set_state(state);
+ }
+ }
+ }
+ ()
+ });
MinecraftClient {
- connection: CraftTokioConnection::from_async((bufread, write), mcproto_rs::protocol::PacketDirection::ServerBound),
+ craft_reader,
+ send_tx,
+ send_handle,
}
}
+
+ pub async fn close_and_join(self) -> Result<()> {
+ drop(self.send_tx);
+ self.send_handle.await?;
+ // The reader is closed when the writer half is dropped by the send task.
+ Ok(())
+ }
+
+ pub fn set_state(&mut self, state: State) -> Result<()> {
+ self.send_tx.send(WriterAction::SetState(state))?;
+ self.craft_reader.set_state(state);
+ Ok(())
+ }
+
pub async fn read_next_packet(&mut self) -> Result<Option<Packet761>> {
- if let Some(raw) = self.connection.read_packet_async::<RawPacket761>().await? {
- println!("Client -> Server: {:?}", raw);
+ if let Some(raw) = self.craft_reader.read_packet_async::<RawPacket761>().await? {
+ // println!("Client -> Server: {:?}", raw);
Ok(Some(raw))
} else {
Ok(None)
}
}
- pub async fn send_packet(&mut self, packet: Packet761) -> Result<()> {
- println!("Server -> Client: {:?}", packet);
- self.connection.write_packet_async(packet).await?;
- Ok(())
- }
pub async fn wait_for_packet(&mut self, packet_kind: Packet761Kind) -> Result<Packet761> {
loop {
if let Some(packet) = self.read_next_packet().await? {
@@ -70,4 +112,17 @@ impl MinecraftClient {
}
}
}
+
+ pub fn send_packet(&self, packet: Packet761) -> Result<()> {
+ // println!("Server -> Client: {:?}", packet);
+ self.send_tx.send(WriterAction::Send(packet))?;
+ Ok(())
+ }
+
+ pub fn send_all_packets(&self, packets: Vec<Packet761>) -> Result<()> {
+ for x in packets {
+ self.send_packet(x)?;
+ }
+ Ok(())
+ }
}
diff --git a/src/convention.rs b/src/convention.rs
new file mode 100644
index 0000000..ae9a646
--- /dev/null
+++ b/src/convention.rs
@@ -0,0 +1,189 @@
+use mcproto_rs::nbt::Tag;
+use mcproto_rs::types::NamedNbtTag;
+use mcproto_rs::v1_19_3::{BookSettings, CommandNode, CommandNodeSpec, CommandsSpec, GameMode, LoginPlaySpec, Packet761, PreviousGameMode, RecipeBookAction, RecipeBookInitSpec, TagType, TypedTagList, UpdateRecipeBookSpec, UpdateRecipesSpec, UpdateTagsSpec};
+
+use crate::nbt;
+use crate::player::Player;
+
+pub struct Universe {
+ pub min_build_height: i32,
+ pub max_build_height: i32,
+ pub min_world_section: i32,
+ pub max_world_section: i32,
+ pub registry_codex: Tag,
+}
+
+fn convention_biome_spec() -> Tag {
+ return nbt!(
+ [
+ {
+ "id" :: (0),
+ "name" :: ("minecraft:plains"),
+ "element" :: {
+ "downfall" :: (0.5),
+ "precipitation" :: ("none"),
+ "temperature" :: (0.5),
+ "effects" :: {
+ "fog_color" :: (12638463),
+ "sky_color" :: (12638463),
+ "water_color" :: (12638463),
+ "water_fog_color" :: (12638463),
+ "mood_sound" :: {
+ "block_search_extent" :: (8),
+ "offset" :: (2),
+ "sound" :: ("minecraft:ambient.cave"),
+ "tick_delay" :: (6000),
+ },
+ }
+ }
+ }
+ ]
+ )
+}
+
+fn convention_overworld_spec(min_build_height: i32, world_height: i32) -> Tag {
+ return nbt! {
+ "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" :: (world_height),
+ "infiniburn" :: ("#minecraft:infiniburn_overworld"),
+ "logical_height" :: (world_height),
+ "min_y" :: (min_build_height),
+ "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),
+ },
+ }
+}
+
+impl Universe {
+ pub fn default_meta(
+ min_build_height: i32,
+ max_build_height: i32,
+ ) -> Universe {
+ let world_height = max_build_height - min_build_height;
+ let min_world_section = min_build_height >> 4;
+ let max_world_section = ((max_build_height - 1) >> 4) + 1;
+ let biome_spec = convention_biome_spec();
+ let overworld_spec = convention_overworld_spec(min_build_height, world_height);
+ let registry_codex = nbt! {
+ "minecraft:dimension_type" :: {
+ "type" :: ("minecraft:dimension_type"),
+ "value" :: [(overworld_spec)]
+ },
+ "minecraft:worldgen/biome" :: {
+ "type" :: ("minecraft:worldgen/biome"),
+ "value" :: (biome_spec),
+ }
+ };
+ Universe {
+ min_build_height,
+ max_build_height,
+ min_world_section,
+ max_world_section,
+ registry_codex,
+ }
+ }
+
+ pub fn create_login_play_packet(&self) -> Packet761 {
+ 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: self.registry_codex.clone().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,
+ })
+ }
+
+ pub fn init_empty_recipe_book(&self) -> Packet761 {
+ Packet761::UpdateRecipeBook(UpdateRecipeBookSpec {
+ action: RecipeBookAction::Init(RecipeBookInitSpec {
+ book_settings: BookSettings::default(),
+ known_recipes: vec![].into(),
+ highlighted_recipes: vec![].into(),
+ })
+ })
+ }
+
+ pub fn empty_commands(&self) -> Packet761 {
+ Packet761::Commands(CommandsSpec {
+ nodes: vec![
+ CommandNodeSpec {
+ children_indices: vec![].into(),
+ redirect_node: None,
+ is_executable: false,
+ node: CommandNode::Root,
+ }
+ ].into(),
+ root_index: 0.into(),
+ })
+ }
+
+ pub fn create_tags_and_recipes(&self) -> Vec<Packet761> {
+ return vec![
+ Packet761::UpdateRecipes(UpdateRecipesSpec { recipes: vec![].into() }),
+ Packet761::UpdateTags(UpdateTagsSpec {
+ tags: vec![
+ TypedTagList {
+ tag_type: TagType::Block,
+ tags: vec![].into(),
+ },
+ TypedTagList {
+ tag_type: TagType::Item,
+ tags: vec![].into(),
+ },
+ TypedTagList {
+ tag_type: TagType::EntityType,
+ tags: vec![].into(),
+ },
+ TypedTagList {
+ tag_type: TagType::GameEvent,
+ tags: vec![].into(),
+ },
+ TypedTagList {
+ tag_type: TagType::Fluid,
+ tags: vec![].into(),
+ },
+ ].into(),
+ }),
+ ]
+ }
+}
+
+
+
+
+
+
+
diff --git a/src/main.rs b/src/main.rs
index a113a30..6c1f95b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,7 +2,6 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration;
use anyhow::Result;
-use craftio_rs::CraftIo;
use mcproto_rs::nbt::Tag;
use mcproto_rs::protocol::State;
use mcproto_rs::Serializer;
@@ -14,7 +13,7 @@ use mcproto_rs::v1_19_3::{GameMode, HandshakeNextState, LoginPlaySpec, LoginSucc
use tokio;
use tokio::net::TcpStream;
use tokio::signal::unix::{signal, SignalKind};
-use tokio::sync::{broadcast};
+use tokio::sync::broadcast;
use tokio::sync::broadcast::Receiver;
use tokio::task::JoinHandle;
@@ -22,7 +21,8 @@ use crate::connect::MinecraftClient;
pub mod connect;
pub mod nbtdsl;
-
+pub mod player;
+pub mod convention;
#[tokio::main]
async fn main() -> Result<()> {
@@ -87,292 +87,169 @@ async fn listener(listener: tokio::net::TcpListener, mut receiver: Receiver<()>)
async fn handle_conn(mut client: MinecraftClient) -> Result<()> {
let hs = assert_packet!(Handshake, client, "Missing packet");
if hs.next_state == HandshakeNextState::Login {
- client.connection.set_state(State::Login);
- let loginstart = assert_packet!(
+ return handle_login(client).await;
+ } else if hs.next_state == HandshakeNextState::Status {
+ return handle_status(client).await;
+ } else {
+ anyhow::bail!("Unknown next status")
+ }
+}
+
+async fn handle_login(mut client: MinecraftClient) -> Result<()> {
+ client.set_state(State::Login)?;
+ 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 min_build_height = -64i32;
- let max_build_height = 256i32;
- let world_height = max_build_height - min_build_height;
- let min_world_section = min_build_height >> 4;
- let max_world_section = ((max_build_height - 1) >> 4) + 1;
- let section_count = max_world_section - min_world_section;
- let overworld_spec = nbt! {
- "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" :: (world_height),
- "infiniburn" :: ("#minecraft:infiniburn_overworld"),
- "logical_height" :: (world_height),
- "min_y" :: (min_build_height),
- "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),
- },
- };
- let biome_spec = nbt!(
- [
- {
- "id" :: (0),
- "name" :: ("minecraft:plains"),
- "element" :: {
- "downfall" :: (0.5),
- "precipitation" :: ("none"),
- "temperature" :: (0.5),
- "effects" :: {
- "fog_color" :: (12638463),
- "sky_color" :: (12638463),
- "water_color" :: (12638463),
- "water_fog_color" :: (12638463),
- "mood_sound" :: {
- "block_search_extent" :: (8),
- "offset" :: (2),
- "sound" :: ("minecraft:ambient.cave"),
- "tick_delay" :: (6000),
- },
- }
- }
- }
- ]
- );
- let registry_doex = nbt! {
- "minecraft:dimension_type" :: {
- "type" :: ("minecraft:dimension_type"),
- "value" :: [(overworld_spec)]
- },
- "minecraft:worldgen/biome" :: {
- "type" :: ("minecraft:worldgen/biome"),
- "value" :: (biome_spec),
+ println!("Login: {:?}", loginstart);
+ let uuid = UUID4::random();
+ client
+ .send_packet(Packet761::LoginSuccess(LoginSuccessSpec {
+ uuid,
+ username: loginstart.name.clone(),
+ properties: vec![].into(),
+ }))?;
+ client.set_state(State::Play)?;
+ let mut player = player::Player {
+ client,
+ name: loginstart.name.clone(),
+ position: Vec3 {
+ x: 0.0,
+ y: 100.0,
+ z: 0.0,
+ },
+ yaw: 0.0,
+ pitch: 0.0,
+ };
+ let universe = convention::Universe::default_meta(-64, 256);
+ player.send_packet(universe.create_login_play_packet())?;
+
+ let client_information = await_packet!(ClientInformation, player.client);
+ println!("Client Information: {:?}", client_information);
+
+ player.send_packet(Packet761::SetHeldItemClient(SetHeldItemClientSpec {
+ selected_slot: 0,
+ }))?;
+
+ player.send_all_packets(universe.create_tags_and_recipes())?;
+
+ player.send_packet(Packet761::EntityEvent(EntityEventSpec {
+ entity_id: 0,
+ entity_status: 24 /* OP permission level = 0 */,
+ }))?;
+ player.send_packet(universe.empty_commands())?;
+ player.send_packet(universe.init_empty_recipe_book())?;
+ player.send_packet(player.create_teleport_packet())?;
+ player.send_packet(Packet761::SetCenterChunk(SetCenterChunkSpec {
+ chunk_x: 0.into(),
+ chunk_z: 0.into(),
+ }))?;
+ for i in -7..7 {
+ for j in -7..7 {
+ let mut heightmap = BlobArray::new(9);
+ for cx in 0..(16 * 16) {
+ heightmap.set_entry(cx, 2)
}
- };
- 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);
- client.send_packet(Packet761::SetHeldItemClient(SetHeldItemClientSpec {
- selected_slot: 0,
- })).await?;
- client.send_packet(Packet761::UpdateRecipes(UpdateRecipesSpec { recipes: vec![].into() })).await?;
- client.send_packet(Packet761::UpdateTags(UpdateTagsSpec {
- tags: vec![
- TypedTagList {
- tag_type: TagType::Block,
- tags: vec![].into(),
- },
- TypedTagList {
- tag_type: TagType::Item,
- tags: vec![].into(),
- },
- TypedTagList {
- tag_type: TagType::EntityType,
- tags: vec![].into(),
- },
- TypedTagList {
- tag_type: TagType::GameEvent,
- tags: vec![].into(),
- },
- TypedTagList {
- tag_type: TagType::Fluid,
- tags: vec![].into(),
- },
- ].into(),
- })).await?;
- client.send_packet(Packet761::EntityEvent(EntityEventSpec {
- entity_id: 0,
- entity_status: 24 /* OP permission level = 0 */,
- })).await?;
- client.send_packet(Packet761::Commands(CommandsSpec {
- nodes: vec![
- CommandNodeSpec {
- children_indices: vec![].into(),
- redirect_node: None,
- is_executable: false,
- node: CommandNode::Root,
- }
- ].into(),
- root_index: 0.into(),
- })).await?;
- client.send_packet(Packet761::UpdateRecipeBook(UpdateRecipeBookSpec {
- action: RecipeBookAction::Init(RecipeBookInitSpec {
- book_settings: BookSettings::default(),
- known_recipes: vec![].into(),
- highlighted_recipes: vec![].into(),
- })
- })).await?;
- client.send_packet(Packet761::SynchronizePlayerPosition(SynchronizePlayerPositionSpec {
- position: Vec3 {
- x: 0f64,
- y: 100f64,
- z: 0f64,
- },
- yaw: 0.0,
- pitch: 0.0,
- flags: Default::default(),
- teleport_id: 0.into(),
- dismount_vehicle: false,
- })).await?;
- client.send_packet(Packet761::SetCenterChunk(SetCenterChunkSpec {
- chunk_x: 0.into(),
- chunk_z: 0.into(),
- })).await?;
- for i in -7..7 {
- for j in -7..7 {
- let mut heightmap = BlobArray::new(9);
- for cx in 0..(16 * 16) {
- heightmap.set_entry(cx, 2)
- }
- let mut chunk_dat_serializer = BytesSerializer::default();
- for i in 0..section_count {
- let section = ChunkSection {
- block_count: if i < 4 { 16 * 16 * 16 } else { 0 },
- block_state: PalettedContainer { singular_value: if i < 4 { 1.into() } else { 0.into() } },
- biomes: PalettedContainer { singular_value: 0.into() },
- };
- chunk_dat_serializer.serialize_other(&section)?;
- }
- let chunk_column_bytes = chunk_dat_serializer.into_bytes();
- client.send_packet(Packet761::ChunkDataAndUpdateLight(ChunkDataAndUpdateLightSpec {
- chunk_x: i,
- chunk_z: j,
- chunk_data: ChunkDataSpec {
- heightmaps: NamedNbtTag {
- root: nbt! {
+ let mut chunk_dat_serializer = BytesSerializer::default();
+ for i in 0..(universe.max_world_section - universe.min_world_section) {
+ let section = ChunkSection {
+ block_count: if i < 4 { 16 * 16 * 16 } else { 0 },
+ block_state: PalettedContainer { singular_value: if i < 4 { 1.into() } else { 0.into() } },
+ biomes: PalettedContainer { singular_value: 0.into() },
+ };
+ chunk_dat_serializer.serialize_other(&section)?;
+ }
+ let chunk_column_bytes = chunk_dat_serializer.into_bytes();
+ player.send_packet(Packet761::ChunkDataAndUpdateLight(ChunkDataAndUpdateLightSpec {
+ chunk_x: i,
+ chunk_z: j,
+ chunk_data: ChunkDataSpec {
+ heightmaps: NamedNbtTag {
+ root: nbt! {
"MOTION_BLOCKING" :: (Tag::LongArray(heightmap.data.iter().map(|n| n.to_owned() as _).collect())),
"WORLD_SURFACE" :: (Tag::LongArray(heightmap.data.iter().map(|n| n.to_owned() as _).collect())),
}.with_name("root")
- },
- buffer: chunk_column_bytes.into(),
- block_entities: vec![].into(),
},
- light_data: LightDataSpec {
- trust_edges: true,
- sky_y_mask: BitSet::empty(),
- block_y_mask: BitSet::empty(),
- empty_sky_y_mask: BitSet::empty(),
- empty_block_y_mask: BitSet::empty(),
- sky_updates: vec![].into(),
- block_updates: vec![].into(),
- },
- })).await?;
- }
+ buffer: chunk_column_bytes.into(),
+ block_entities: vec![].into(),
+ },
+ light_data: LightDataSpec {
+ trust_edges: true,
+ sky_y_mask: BitSet::empty(),
+ block_y_mask: BitSet::empty(),
+ empty_sky_y_mask: BitSet::empty(),
+ empty_block_y_mask: BitSet::empty(),
+ sky_updates: vec![].into(),
+ block_updates: vec![].into(),
+ },
+ }))?;
}
- client.send_packet(Packet761::InitializeWorldBorder(InitializeWorldBorderSpec {
- x: 0.0,
- z: 0.0,
- old_diameter: 10000000.0,
- new_diameter: 10000000.0,
- speed: 0.into(),
- portal_teleport_boundary: 29999984.into(),
- warning_blocks: 0.into(),
- warning_time: 0.into(),
- })).await?;
- client.send_packet(Packet761::SetDefaultSpawnPosition(SetDefaultSpawnPositionSpec {
- location: IntPosition {
- x: 0,
- y: 0,
- z: 0,
- },
- angle: 0.0,
- })).await?;
- client.send_packet(Packet761::SynchronizePlayerPosition(SynchronizePlayerPositionSpec {
- position: Vec3 {
- x: 0f64,
- y: 100f64,
- z: 0f64,
- },
- yaw: 0.0,
- pitch: 0.0,
- flags: Default::default(),
- teleport_id: 0.into(),
- dismount_vehicle: false,
- })).await?;
- client.send_packet(Packet761::SetContainerContent(SetContainerContentSpec {
- window_id: 0,
- state_id: 0.into(),
- slot_data: vec![Some(ItemStack {
- item_id: 1.into(),
- item_count: 1,
- nbt: None,
- }); 45].into(),
- carried_item: None,
- })).await?;
- let mut i = 0;
- loop {
- client.send_packet(Packet761::KeepAliveClientBound(KeepAliveClientBoundSpec {
- keep_alive_id: i,
- })).await?;
- if i == 2 {
- let mut brand_encoder = BytesSerializer::default();
- brand_encoder.serialize_other(&String::from("MGASI"))?;
+ }
+ player.send_packet(Packet761::InitializeWorldBorder(InitializeWorldBorderSpec {
+ x: 0.0,
+ z: 0.0,
+ old_diameter: 10000000.0,
+ new_diameter: 10000000.0,
+ speed: 0.into(),
+ portal_teleport_boundary: 29999984.into(),
+ warning_blocks: 0.into(),
+ warning_time: 0.into(),
+ }))?;
+ player.send_packet(Packet761::SetDefaultSpawnPosition(SetDefaultSpawnPositionSpec {
+ location: IntPosition {
+ x: 0,
+ y: 0,
+ z: 0,
+ },
+ angle: 0.0,
+ }))?;
+ player.send_packet(Packet761::SynchronizePlayerPosition(SynchronizePlayerPositionSpec {
+ position: Vec3 {
+ x: 0f64,
+ y: 100f64,
+ z: 0f64,
+ },
+ yaw: 0.0,
+ pitch: 0.0,
+ flags: Default::default(),
+ teleport_id: 0.into(),
+ dismount_vehicle: false,
+ }))?;
+ player.send_packet(Packet761::SetContainerContent(SetContainerContentSpec {
+ window_id: 0,
+ state_id: 0.into(),
+ slot_data: vec![Some(ItemStack {
+ item_id: 1.into(),
+ item_count: 1,
+ nbt: None,
+ }); 45].into(),
+ carried_item: None,
+ }))?;
+ let mut i = 0;
+ loop {
+ player.send_packet(Packet761::KeepAliveClientBound(KeepAliveClientBoundSpec {
+ keep_alive_id: i,
+ }))?;
+ if i == 2 {
+ let mut brand_encoder = BytesSerializer::default();
+ brand_encoder.serialize_other(&String::from("MGASI"))?;
- client.send_packet(Packet761::PluginMessage(PluginMessageSpec {
- channel: "minecraft:brand".to_string(),
- data: RemainingBytes { data: brand_encoder.into_bytes() },
- })).await?;
- }
- i += 1;
- tokio::time::sleep(Duration::from_secs(10)).await;
+ player.send_packet(Packet761::PluginMessage(PluginMessageSpec {
+ channel: "minecraft:brand".to_string(),
+ data: RemainingBytes { data: brand_encoder.into_bytes() },
+ }))?;
}
- } else if hs.next_state == HandshakeNextState::Status {
- return handle_status(client).await;
- } else {
- anyhow::bail!("Unknown next status")
+ i += 1;
+ tokio::time::sleep(Duration::from_secs(10)).await;
}
}
+
async fn handle_status(mut client: MinecraftClient) -> Result<()> {
- client.connection.set_state(State::Status);
+ client.set_state(State::Status)?;
loop {
match client
.read_next_packet()
@@ -380,28 +257,24 @@ async fn handle_status(mut client: MinecraftClient) -> Result<()> {
.ok_or(anyhow::anyhow!("Missing packet"))?
{
Packet761::StatusRequest(_) => {
- client
- .send_packet(Packet761::StatusRespone(StatusResponseSpec {
- response: StatusSpec {
- version: Some(StatusVersionSpec {
- name: "1.19.3 mgasi".to_string(),
- protocol: 761,
- }),
- players: StatusPlayersSpec {
- max: 100,
- online: 0,
- sample: vec![],
- },
- description: Chat::from_text("hehehe"),
- favicon: None,
+ client.send_packet(Packet761::StatusRespone(StatusResponseSpec {
+ response: StatusSpec {
+ version: Some(StatusVersionSpec {
+ name: "1.19.3 mgasi".to_string(),
+ protocol: 761,
+ }),
+ players: StatusPlayersSpec {
+ max: 100,
+ online: 0,
+ sample: vec![],
},
- }))
- .await?;
+ description: Chat::from_text("hehehe"),
+ favicon: None,
+ },
+ }))?;
}
Packet761::PingRequest(PingRequestSpec { payload }) => {
- client
- .send_packet(Packet761::PingResponse(PingResponseSpec { payload }))
- .await?;
+ client.send_packet(Packet761::PingResponse(PingResponseSpec { payload }))?;
return Ok(());
}
_ => anyhow::bail!("Unexpected packet during status communication"),
diff --git a/src/nbtdsl.rs b/src/nbtdsl.rs
index bfa9090..86b2116 100644
--- a/src/nbtdsl.rs
+++ b/src/nbtdsl.rs
@@ -1,6 +1,5 @@
use mcproto_rs::nbt::Tag;
-
pub trait ShittyToNbt {
fn to_nbt(self) -> Tag;
}
diff --git a/src/player.rs b/src/player.rs
new file mode 100644
index 0000000..7b99c16
--- /dev/null
+++ b/src/player.rs
@@ -0,0 +1,48 @@
+use anyhow::Result;
+use mcproto_rs::types::Vec3;
+use mcproto_rs::v1_19_3::{Packet761, SynchronizePlayerPositionSpec};
+
+use crate::connect::MinecraftClient;
+
+pub struct Player {
+ pub client: MinecraftClient,
+ pub name: String,
+ pub position: Vec3<f64>,
+ pub yaw: f32,
+ pub pitch: f32,
+}
+
+
+impl Player {
+ pub fn create_teleport_packet(&self) -> Packet761 {
+ Packet761::SynchronizePlayerPosition(SynchronizePlayerPositionSpec {
+ position: self.position.clone(),
+ yaw: self.yaw,
+ pitch: self.pitch,
+ flags: Default::default(),
+ teleport_id: 0.into(), // TODO: teleport id ack
+ dismount_vehicle: false,
+ })
+ }
+
+ pub async fn teleport(&mut self, position: Vec3<f64>, yaw: f32, pitch: f32) -> Result<()> {
+ self.position = position;
+ self.yaw = yaw;
+ self.pitch = pitch;
+ self.send_packet(self.create_teleport_packet())?;
+ Ok(())
+ }
+
+ pub fn send_packet(&self, packet: Packet761) -> Result<()> {
+ self.client.send_packet(packet)
+ }
+ pub fn send_all_packets(&self, packets: Vec<Packet761>) -> Result<(), anyhow::Error> {
+ self.client.send_all_packets(packets)
+ }
+}
+
+
+
+
+
+