diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 179 |
1 files changed, 170 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs index 80dea19..1e1c306 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,16 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream}; +use std::time::Duration; use anyhow::Result; use craftio_rs::{CraftIo, CraftSyncReader, CraftSyncWriter, CraftTcpConnection}; -use mcproto_rs::nbt::{NamedTag, Tag}; +use mcproto_rs::nbt::Tag; use mcproto_rs::protocol::{HasPacketKind, State}; +use mcproto_rs::Serializer; use mcproto_rs::status::{StatusPlayersSpec, StatusSpec, StatusVersionSpec}; -use mcproto_rs::types::{Chat, NamedNbtTag}; +use mcproto_rs::types::{BytesSerializer, Chat, IntPosition, ItemStack, NamedNbtTag, Slot, Vec3}; use mcproto_rs::uuid::UUID4; -use mcproto_rs::v1_19_3::{GameMode, HandshakeNextState, LoginPlaySpec, LoginSuccessSpec, - Packet761, Packet761Kind, PingRequestSpec, PingResponseSpec, - PreviousGameMode, RawPacket761, StatusResponseSpec}; +use mcproto_rs::v1_19_3::{BitSet, BlobArray, BookSettings, ChunkDataAndUpdateLightSpec, ChunkDataSpec, ChunkSection, CommandNode, CommandNodeSpec, CommandsSpec, EntityEventSpec, InitializeWorldBorderSpec, KeepAliveClientBoundSpec, LightDataSpec, PalettedContainer, RecipeBookAction, RecipeBookInitSpec, SetCenterChunkSpec, SetContainerContentSpec, SetDefaultSpawnPositionSpec, SynchronizePlayerPositionSpec, TagType, TypedTagList, UpdateRecipeBookSpec}; +use mcproto_rs::v1_19_3::{GameMode, HandshakeNextState, LoginPlaySpec, LoginSuccessSpec, Packet761, Packet761Kind, PingRequestSpec, PingResponseSpec, PreviousGameMode, RawPacket761, SetHeldItemClientSpec, StatusResponseSpec, UpdateRecipesSpec, UpdateTagsSpec}; use tokio; pub struct MinecraftClient { @@ -197,6 +198,12 @@ async fn handle_conn(mut client: MinecraftClient) -> Result<()> { 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"), @@ -208,10 +215,10 @@ async fn handle_conn(mut client: MinecraftClient) -> Result<()> { "has_ceiling" :: (false), "has_skylight" :: (true), "has_raids" :: (false), - "height" :: (384), + "height" :: (world_height), "infiniburn" :: ("#minecraft:infiniburn_overworld"), - "logical_height" :: (384), - "min_y" :: (-64), + "logical_height" :: (world_height), + "min_y" :: (min_build_height), "monster_spawn_block_light_limit" :: (0), "monster_spawn_light_level" :: { "type" :: ("minecraft:uniform"), @@ -287,7 +294,161 @@ async fn handle_conn(mut client: MinecraftClient) -> Result<()> { .await?; let client_information = await_packet!(ClientInformation, client); println!("Client Information: {:?}", client_information); - anyhow::bail!("TODO"); + 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(§ion)?; + } + 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! { + "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?; + } + } + 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, + })); + let mut i = 0; + loop { + client.send_packet(Packet761::KeepAliveClientBound(KeepAliveClientBoundSpec { + keep_alive_id: i, + })).await?; + i += 1; + tokio::time::sleep(Duration::from_secs(10)).await; + } } else if hs.next_state == HandshakeNextState::Status { return handle_status(client).await; } else { |