Skip to content

Commit

Permalink
add initial 1.20.70 protocol codde
Browse files Browse the repository at this point in the history
  • Loading branch information
theaddonn committed Nov 1, 2024
1 parent 1dbb58d commit 72d4141
Show file tree
Hide file tree
Showing 21 changed files with 790 additions and 0 deletions.
100 changes: 100 additions & 0 deletions crates/proto/src/version/v662/gamepackets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use bedrockrs_macros::gamepackets;
use bedrockrs_proto_core::error::ProtoCodecError;
use bedrockrs_proto_core::sub_client::SubClientID;
use std::io::{Cursor, Write};
use varint_rs::{VarintReader, VarintWriter};
use crate::version::v662::packets::{DisconnectPacket, HandshakeClientToServerPacket, HandshakeServerToClientPacket, LoginPacket, PlayStatusPacket, ResourcePackClientResponsePacket, ResourcePacksInfoPacket, ResourcePacksStackPacket, SetTimePacket, TextMessagePacket};
use crate::version::v729::packets::add_player::AddPlayerPacket;

gamepackets! {
Login: LoginPacket,
PlaySatus: PlayStatusPacket,
HandshakeServerToClient: HandshakeServerToClientPacket,
HandshakeClientToServer: HandshakeClientToServerPacket,
Disconnect: DisconnectPacket,
ResourcePacksInfo: ResourcePacksInfoPacket,
ResourcePacksStack: ResourcePacksStackPacket,
ResourcePackClientResponse: ResourcePackClientResponsePacket,
TextMessage: TextMessagePacket,
SetTime: SetTimePacket,
StartGame: _, // Scary Packet
AddPlayer: AddPlayerPacket,
}

fn read_gamepacket_header(
stream: &mut Cursor<&[u8]>,
) -> Result<(u32, u16, SubClientID, SubClientID), ProtoCodecError> {
// Read the gamepacket length
let length = stream.read_u32_varint()?;

// Read the gamepacket header and parse it into an u16
// Since the (var)int is only storing 14 bytes we can treat it as an u16
// This is normally treated as u32 varint
let gamepacket_header: u16 = stream.read_u16_varint()?;

// Get the first 10 bits as the packet id
// Can never be more than a 16-bit integer due to being 10-bits big
// Gamepacket IDs through 200-299 are used for spin-offs, they are free to use for custom packets
let gamepacket_id = gamepacket_header & 0b0000_0011_1111_1111;

// Get the next 2 bits as the sub client sender id
// Can never be more than an 8-bit integer due to being 2 bits big
let subclient_sender_id =
SubClientID::try_from(((gamepacket_header & 0b0000_1100_0000_0000) >> 10) as u8)?;
// Get the next 2 bits as the sub client target id
// Never more than an 8-bit integer due to being 2 bits big
let subclient_target_id =
SubClientID::try_from(((gamepacket_header & 0b0011_0000_0000_0000) >> 12) as u8)?;

Ok((
length,
gamepacket_id,
subclient_sender_id,
subclient_target_id,
))
}

fn write_gamepacket_header(
stream: &mut Vec<u8>,
length: u32,
gamepacket_id: u16,
subclient_sender_id: SubClientID,
subclient_target_id: SubClientID,
) -> Result<(), ProtoCodecError> {
// Since the (var)int is only storing 14 bytes, we can treat it as an u16
// This is normally treated as u32 varint
let mut gamepacket_header: u16 = 0;

// Set the first 10 bits as the packet id
// Can never be more than a 16-bit integer due to being 10-bits big
// Gamepacket IDs through 200-299 are used for spin-offs, they are free to use for custom packets
gamepacket_header |= 0b0000_0011_1111_1111 & gamepacket_id;

// Set the next 2 bits as the sub client sender id
// Never more than an 8-bit integer due to being 2 bits big
gamepacket_header |= (Into::<u16>::into(subclient_sender_id) >> 10) & 0b0000_1100_0000_0000;
// Set the next 2 bits as the sub client target id
// Never more than an 8-bit integer due to being 2 bits big
gamepacket_header |= (Into::<u16>::into(subclient_target_id) >> 12) & 0b0011_0000_0000_0000;

// Since the size of the header is also included in the batched packet size,
// we need to write it to a temporary buffer
let mut gamepacket_header_buf = Vec::new();

// Write the gamepacket header into temporary buffer
gamepacket_header_buf.write_u16_varint(gamepacket_header)?;

// Write the gamepacket length and the header length
stream.write_u32_varint(length + gamepacket_header_buf.len() as u32)?;

// Write the final game packet header
stream.write_all(gamepacket_header_buf.as_slice())?;

Ok(())
}

const fn get_gamepacket_header_size_prediction() -> usize {
// 2 = gamepacket header (varint u32, only 14 bites can be treated as an u16)
// 4 = gamepacket length size (varint u32)
2 + 4
}
8 changes: 8 additions & 0 deletions crates/proto/src/version/v662/helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::helper::ProtoHelper;
use crate::version::v729::gamepackets::GamePackets;

pub struct ProtoHelperV662;

impl ProtoHelper for ProtoHelperV662 {
type GamePacketType = GamePackets;
}
1 change: 1 addition & 0 deletions crates/proto/src/version/v662/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const PROTOCOL_VERSION: i32 = 662;
6 changes: 6 additions & 0 deletions crates/proto/src/version/v662/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
//! r/20_u7
pub mod gamepackets;
pub mod helper;
pub mod packets;
pub mod types;
pub mod info;
27 changes: 27 additions & 0 deletions crates/proto/src/version/v662/packets/add_player.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use uuid::Uuid;
use bedrockrs_core::{Vec2, Vec3};
use bedrockrs_macros::{gamepacket, ProtoCodec};
use bedrockrs_shared::actor_runtime_id::ActorRuntimeID;
use bedrockrs_shared::world::gamemode::Gamemode;
use crate::version::v662::types::ItemStackDescriptor;

#[gamepacket(id = 9)]
#[derive(ProtoCodec, Debug, Clone)]
pub struct AddPlayerPacket {
pub uuid: Uuid,
pub username: String,
pub target_runtime_id: ActorRuntimeID,
pub platform_chat_id: String,
#[endianness(le)]
pub position: Vec3<f32>,
#[endianness(le)]
pub velocity: Vec3<f32>,
#[endianness(le)]
pub rotation: Vec2<f32>,
#[endianness(le)]
pub head_yaw: f32,
pub carried_item: ItemStackDescriptor,
pub gamemode: Gamemode,
pub EntityMetadata: EntityMetadata,

Check failure on line 25 in crates/proto/src/version/v662/packets/add_player.rs

View workflow job for this annotation

GitHub Actions / Check

cannot find type `EntityMetadata` in this scope

Check failure on line 25 in crates/proto/src/version/v662/packets/add_player.rs

View workflow job for this annotation

GitHub Actions / Test Suite

cannot find type `EntityMetadata` in this scope

}
163 changes: 163 additions & 0 deletions crates/proto/src/version/v662/packets/disconnect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::io::Cursor;
use bedrockrs_macros::{gamepacket, ProtoCodec};
use bedrockrs_proto_core::error::ProtoCodecError;
use bedrockrs_proto_core::ProtoCodec;

#[gamepacket(id = 5)]
#[derive(Debug, Clone)]
pub struct DisconnectPacket {
pub reason: DisconnectFailReason,
pub message: Option<String>
}

impl ProtoCodec for DisconnectPacket {
fn proto_serialize(&self, stream: &mut Vec<u8>) -> Result<(), ProtoCodecError> {
self.reason.proto_serialize(stream)?;

// Normally an optional type is prefixed by a bool indicating if the following type has a value,
// but for the message in the DisconnectPacket it is the other way around,
// indicating if the following value should be skipped
bool::proto_serialize(&self.message.is_none(), stream)?;

if let Some(ref message) = self.message {
message.proto_serialize(stream)?;
}

Ok(())
}

fn proto_deserialize(stream: &mut Cursor<&[u8]>) -> Result<Self, ProtoCodecError> {
let reason = DisconnectFailReason::proto_deserialize(stream)?;

let skip_message = bool::proto_deserialize(stream)?;

let message = if !skip_message {
Some(String::proto_deserialize(stream)?)
} else {
None
};

Ok(DisconnectPacket { reason, message })
}

fn get_size_prediction(&self) -> usize {
self.reason.get_size_prediction() + self.message.get_size_prediction()
}
}

#[derive(ProtoCodec, Clone, Debug)]
#[enum_repr(i32)]
#[enum_endianness(var)]
pub enum DisconnectFailReason {
Unknown = 0,
CantConnectNoInternet = 1,
NoPermissions = 2,
UnrecoverableError = 3,
ThirdPartyBlocked = 4,
ThirdPartyNoInternet = 5,
ThirdPartyBadIP = 6,
ThirdPartyNoServerOrServerLocked = 7,
VersionMismatch = 8,
SkinIssue = 9,
InviteSessionNotFound = 10,
EduLevelSettingsMissing = 11,
LocalServerNotFound = 12,
LegacyDisconnect = 13,
UserLeaveGameAttempted = 14,
PlatformLockedSkinsError = 15,
RealmsWorldUnassigned = 16,
RealmsServerCantConnect = 17,
RealmsServerHidden = 18,
RealmsServerDisabledBeta = 19,
RealmsServerDisabled = 20,
CrossPlatformDisabled = 21,
CantConnect = 22,
SessionNotFound = 23,
ClientSettingsIncompatibleWithServer = 24,
ServerFull = 25,
InvalidPlatformSkin = 26,
EditionVersionMismatch = 27,
EditionMismatch = 28,
LevelNewerThanExeVersion = 29,
NoFailOccurred = 30,
BannedSkin = 31,
Timeout = 32,
ServerNotFound = 33,
OutdatedServer = 34,
OutdatedClient = 35,
NoPremiumPlatform = 36,
MultiplayerDisabled = 37,
NoWiFi = 38,
WorldCorruption = 39,
NoReason = 40,
Disconnected = 41,
InvalidPlayer = 42,
LoggedInOtherLocation = 43,
ServerIdConflict = 44,
NotAllowed = 45,
NotAuthenticated = 46,
InvalidTenant = 47,
UnknownPacket = 48,
UnexpectedPacket = 49,
InvalidCommandRequestPacket = 50,
HostSuspended = 51,
LoginPacketNoRequest = 52,
LoginPacketNoCert = 53,
MissingClient = 54,
Kicked = 55,
KickedForExploit = 56,
KickedForIdle = 57,
ResourcePackProblem = 58,
IncompatiblePack = 59,
OutOfStorage = 60,
InvalidLevel = 61,
#[deprecated]
DisconnectPacket = 62,
BlockMismatch = 63,
InvalidHeights = 64,
InvalidWidths = 65,
ConnectionLost = 66,
ZombieConnection = 67,
Shutdown = 68,
#[deprecated]
ReasonNotSet = 69,
LoadingStateTimeout = 70,
ResourcePackLoadingFailed = 71,
SearchingForSessionLoadingScreenFailed = 72,
NetherNetProtocolVersion = 73,
SubsystemStatusError = 74,
EmptyAuthFromDiscovery = 75,
EmptyUrlFromDiscovery = 76,
ExpiredAuthFromDiscovery = 77,
UnknownSignalServiceSignInFailure = 78,
XBLJoinLobbyFailure = 79,
UnspecifiedClientInstanceDisconnection = 80,
NetherNetSessionNotFound = 81,
NetherNetCreatePeerConnection = 82,
NetherNetICE = 83,
NetherNetConnectRequest = 84,
NetherNetConnectResponse = 85,
NetherNetNegotiationTimeout = 86,
NetherNetInactivityTimeout = 87,
StaleConnectionBeingReplaced = 88,
RealmsSessionNotFound = 89,
BadPacket = 90,
NetherNetFailedToCreateOffer = 91,
NetherNetFailedToCreateAnswer = 92,
NetherNetFailedToSetLocalDescription = 93,
NetherNetFailedToSetRemoteDescription = 94,
NetherNetNegotiationTimeoutWaitingForResponse = 95,
NetherNetNegotiationTimeoutWaitingForAccept = 96,
NetherNetIncomingConnectionIgnored = 97,
NetherNetSignalingParsingFailure = 98,
NetherNetSignalingUnknownError = 99,
NetherNetSignalingUnicastDeliveryFailed = 100,
NetherNetSignalingBroadcastDeliveryFailed = 101,
NetherNetSignalingGenericDeliveryFailed = 102,
EditorMismatchEditorWorld = 103,
EditorMismatchVanillaWorld = 104,
WorldTransferNotPrimaryClient = 105,
RequestServerShutdown = 106,
ClientGameSetupCancelled = 107,
ClientGameSetupFailed = 108,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use bedrockrs_macros::{gamepacket, ProtoCodec};

#[gamepacket(id = 4)]
#[derive(ProtoCodec, Debug, Clone)]
pub struct HandshakeClientToServerPacket {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use bedrockrs_macros::{gamepacket, ProtoCodec};

#[gamepacket(id = 3)]
#[derive(ProtoCodec, Debug, Clone)]
pub struct HandshakeServerToClientPacket {
pub token: String,
}
10 changes: 10 additions & 0 deletions crates/proto/src/version/v662/packets/login.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use bedrockrs_macros::{gamepacket, ProtoCodec};
use crate::version::v662::types::ConnectionRequest;

#[gamepacket(id = 1)]
#[derive(ProtoCodec, Debug, Clone)]
pub struct LoginPacket {
#[endianness(be)]
pub client_network_version: i32,
pub connection_request: ConnectionRequest
}
18 changes: 18 additions & 0 deletions crates/proto/src/version/v662/packets/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
macro_rules! export {
($name:ident) => {
mod $name;
pub use $name::*;
};
}

export!(login);
export!(play_status);
export!(handshake_server_to_client);
export!(handshake_client_to_server);
export!(disconnect);
export!(resource_packs_info);
export!(resource_packs_stack);
export!(resource_pack_client_response);
export!(text_message);
export!(set_time);
export!(add_player);
34 changes: 34 additions & 0 deletions crates/proto/src/version/v662/packets/play_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use bedrockrs_macros::{gamepacket, ProtoCodec};

#[gamepacket(id = 2)]
#[derive(ProtoCodec, Debug, Clone)]
pub struct PlayStatusPacket {
pub status: PlayStatus,
}

#[derive(ProtoCodec, Debug, Copy, Clone, Eq, PartialEq)]
#[enum_repr(i32)]
#[enum_endianness(be)]
pub enum PlayStatus {
/// Sent after Login has been successfully decoded and the player has logged in
LoginSuccess = 0,
/// Displays "Could not connect: Outdated client!"
FailedClientOld = 1,
/// Displays "Could not connect: Outdated server!"
FailedServerOld = 2,
/// Sent after world data to spawn the player
PlayerSpawn = 3,
/// Displays "Unable to connect to world. Your school does not have access to this server."
FailedInvalidTenant = 4,
/// Displays "The server is not running Minecraft: Education Edition. Failed to connect."
FailedEditionMismatchEduToVanilla = 5,
/// Displays "The server is running an incompatible edition of Minecraft. Failed to connect."
FailedEditionMismatchVanillaToEdu = 6,
/// Displays "Wow this server is popular! Check back later to see if space opens up. Server Full"
FailedServerFull = 7,
/// Displays "The server is not in Editor Mode. Failed to connect."
FailedEditorMismatchEditorToVanilla = 8,
/// Displays "The server is in Editor Mode. Failed to connect."
FailedEditorMismatchVanillaToEditor = 9,
}

Loading

0 comments on commit 72d4141

Please sign in to comment.