1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use binary_utils::{DataReader as _, ListDataReader as _, Error, PacketReader, Result, DataWriter};
mod handshake;
mod login;
mod status;
mod configuration;
mod playing;
use datatypes::{ImportantFunctions as _, VarInt};
pub use handshake::*;
pub use login::*;
pub use status::*;
pub use configuration::*;
pub use playing::*;
use tokio::io::{AsyncWrite, BufReader, AsyncBufReadExt};
use tokio::io::AsyncReadExt as _;
use tokio::net::tcp::ReadHalf;
/// Enum of all possible states that a connection can have while being established
#[derive(Clone, Copy)]
pub enum State {
    /// The initial handshake used to decide if `Status` or `Login` should be the next State
    Handshake,
    /// State used for Authentication, name specification and etc.
    Login,
    /// Used to get the motd or to know the ping
    Status,
    /// State between `Login` and `Playing`
    /// Used to configure basic informations and variables important for the client to play on the
    /// server
    Configuration,
    /// State the player is in most of the time, this state is active after `Configuration` and
    /// is active till the player leaves the Server.
    Playing,
}
/// An enum capable of holding every Clientbound Packet (Server to Client) to be able to store them
/// Effectively.
pub enum ClientboundPackets {
    /// Legacy Ping Response to notify Minecraft versions < 1.7 that the server is older than them
    /// and shows the player count, max players, motd and the ping.
    LegacyPong(LegacyPongPacket),
    /// The Ping Response to a Ping Request of the Current Protocol
    Pong(PongPacket),
    /// The Status Response to a status request of the current protocol
    Status(StatusResponsePacket),
    /// Packet to notify the Client that the authentication was successful and the state moves to
    /// `Configuration`
    LoginSuccess(LoginSuccess),
    /// Packet to notify the Client that the authentication was not successful and that the
    /// connection gets terminated
    LoginDisconnect(LoginDisconnect),
    /// Packet to Request an encrypted authentication
    ///
    /// # Note
    /// This Packet only gets used on servers with enabled onlinemode
    LoginEncryptionRequest(LoginEncryptionRequest),
    /// Packet for Client server communication, mostly used for mods
    ConfigurationPluginMessage(ConfigurationClientboundPluginMessage),
    /// This Packet can be used to customize registries of the Client to be able to customize the
    /// game in a better way.
    RegistryData(RegistryData),
    /// Packet to Remove a certain Texture Pack
    ///
    /// # Note
    ///
    /// This packet is also capable to request to remove all Texturepacks
    RemoveResourcePack(RemoveResoucePack),
    /// Packet to let the user add an Texturepack
    ///
    /// # Note
    ///
    /// These Texturepacks can also be forced. If the client doesn't apply those, the client should
    /// be disconnected from the server
    AddResourcePack(AddResourcePack),
    /// Packet to enable certain features, like balances introduced in later versions of the game
    ///
    /// List of the available feature flags as of version 1.20.4
    /// - minecraft:vanilla: Tells the client that the server is vanilla without mods.
    /// - minecraft:bundle: Enables bundles on the client
    /// - minecraft:trade_rebalance: enables the support for the rebalanced villager trades
    /// - minecraft:update_1_21: enables support for 1.21 features
    FeatureFlags(FeatureFlags),
    /// Packet to tell the client that the configuration is finished and should switch the state to
    /// `Playing`
    FinishConfiguration(FinishConfiguration),
}
impl DataWriter for ClientboundPackets {
    async fn write(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
        match self {
            Self::LegacyPong(packet) => packet.write(writer).await,
            Self::Pong(packet) => packet.write(writer).await,
            Self::LoginDisconnect(packet) => packet.write(writer).await,
            Self::LoginSuccess(packet) => packet.write(writer).await,
            Self::Status(packet) => packet.write(writer).await,
            Self::LoginEncryptionRequest(packet) => packet.write(writer).await,
            Self::ConfigurationPluginMessage(packet) => packet.write(writer).await,
            Self::RegistryData(packet) => packet.write(writer).await,
            Self::RemoveResourcePack(packet) => packet.write(writer).await,
            Self::AddResourcePack(packet) => packet.write(writer).await,
            Self::FeatureFlags(packet) => packet.write(writer).await,
            Self::FinishConfiguration(packet) => packet.write(writer).await,
        }
    }
}
/// A list of all Serverbound Packets that can be received (Client to Server)
pub enum ServerboundPackets {
    /// Variant when no packet was received
    None,
    /// Information that a legacy ping was received, no information stored, because none is needed
    LegacyPing,
    /// Handshake initialization Packet
    ///
    /// # Note
    ///
    /// Every Connection that is not a legacy connection has to start with this packet to tell the
    /// server if the connection is of type `Status` or `Login`
    Handshake(HandshakePacket),
    /// Packet to request the player count, max players and motd
    StatusRequest(StatusRequestPacket),
    /// Packet to measure the Ping, should be responded with the same id
    PingRequest(PingPacket),
    /// Packet to start the login procedure
    LoginStart(LoginStart),
    /// Packet that sends encrypted authentication data for the server to autheticate the minecraft
    /// account with the official authentication service
    LoginEncryptionResponse(LoginEncryptionResponse),
    /// Packet that assures the server that the `LoginSuccess` was received
    LoginAcknowledged,
    /// First packet sent to the server after switching to `Configuration`
    ClientInformation(ClientInformation),
    /// Plugin message packet for the configuration phase
    ConfigurationPluginMessage(ServerboundPluginMessage),
    /// Packet that assures the server that the `FinishConfiguration` Packet was received
    AckFinishConfiguration,
    /// Keep Alive Packet, needed to be received to not get disconnected
    KeepAlive(KeepAliveResponse),
    /// Answer to a ping packet requested in the `Configuration` phase
    Pong,
    /// Packet to respond to `AddResourcePack` or `RemoveResourcePack` to tell the server how the
    /// requested Action was processed and if it was successful
    ResoucePackResponse(ResoucePackResponse),
}
impl ServerboundPackets {
    /// A read function to read a Serverbound packet from and `BufReader<ReadHalf>`
    ///
    /// # Returns
    ///
    /// Returns a result containing the read packet. If an error occurs it gets returned
    pub async fn read<'a>(reader: &mut BufReader<ReadHalf<'a>>, state: &State) -> Result<Self> {
        if let Ok(vec) = reader.fill_buf().await {
            println!("vec: {:?}", vec);
            if vec.is_empty() {
                return Ok(ServerboundPackets::None);
            }
        } else {
            return Err(Error::NotEnoughtBytes("".to_string()))
        }
        if reader.buffer().len() == 0 { return Ok(ServerboundPackets::None) }
        match state {
            State::Handshake => Self::handshake(reader).await,
            State::Status => Self::status(reader).await,
            State::Login => Self::login(reader).await,
            State::Configuration => Self::configuration(reader).await,
            State::Playing => Self::playing(reader).await,
        }
    }
    async fn handshake<'a>(reader: &mut BufReader<ReadHalf<'a>>) -> Result<Self> {
        // let mut reader = BufReader::new(reader);
        let mut first_two_bytes = bytes::BytesMut::with_capacity(2);
        match reader.read_buf(&mut first_two_bytes).await {
            Ok(_) => /*println!("Read to the buffer")*/(),
            Err(_) => return Error::NotEnoughtBytes(format!("{}:{}", file!(), line!())).into(),
        }
        if first_two_bytes[0] == 0xFE && first_two_bytes[1] == 0x01 {
            LegacyPingPacket::read(reader).await?;
            Ok(Self::LegacyPing)
        } else {
            let length;
            let packet_id;
            if first_two_bytes[0] >= 0x80 {
                length = VarInt::new((((first_two_bytes[0] as u16) & 0x7F) | ((first_two_bytes[0] as u16) << 7)) as i32);
                packet_id = VarInt::read(reader).await?;
            } else {
                length = VarInt::new(first_two_bytes[0] as i32);
                packet_id = VarInt::new(first_two_bytes[1] as i32);
            }
            match packet_id.get_value() {
                0 => {
                    let data = HandshakePacket::read(reader, length.get_value(), packet_id.get_value()).await?;
                    Ok(ServerboundPackets::Handshake(data))
                },
                _ => Error::InvalidId.into()
            }
        }
    }
    async fn status<'a>(reader: &mut BufReader<ReadHalf<'a>>) -> Result<Self> {
        let length = VarInt::read(reader).await?.get_value();
        let packet_id = VarInt::read(reader).await?.get_value();
        match packet_id {
            0 => {
                Ok(ServerboundPackets::StatusRequest(StatusRequestPacket::read(reader, length, packet_id).await?))
            },
            1 => {
                Ok(ServerboundPackets::PingRequest(PingPacket::read(reader, length, packet_id).await?))
            },
            _ => Error::InvalidId.into(),
        }
    }
    async fn login<'a>(reader: &mut BufReader<ReadHalf<'a>>) -> Result<Self> {
        let _length = VarInt::read(reader).await?.get_value();
        let packet_id = VarInt::read(reader).await?.get_value();
        match packet_id {
            0 => Ok(Self::LoginStart(LoginStart::read(reader).await?)),
            1 => Ok(Self::LoginEncryptionResponse(LoginEncryptionResponse::read(reader).await?)),
            3 => Ok(Self::LoginAcknowledged),
            _ => todo!(),
        }
    }
    async fn configuration<'a>(reader: &mut BufReader<ReadHalf<'a>>) -> Result<Self> {
        reader.buffer();
        let length = VarInt::read(reader).await?.get_value();
        let packet_id = VarInt::read(reader).await?.get_value();
        match packet_id {
            0 => Ok(Self::ClientInformation(ClientInformation::read(reader).await?)),
            1 => Ok(Self::ConfigurationPluginMessage(ServerboundPluginMessage::read_list(reader, length as usize - 1).await?)),
            2 => Ok(Self::AckFinishConfiguration),
            3 => Ok(Self::KeepAlive(KeepAliveResponse::read(reader).await?)),
            4 => unimplemented!(),
            5 => Ok(Self::ResoucePackResponse(ResoucePackResponse::read(reader).await?)),
            i32::MIN..=-1 | 6..=i32::MAX => Err(Error::InvalidStructure),
        }
    }
    async fn playing<'a>(_reader: &mut BufReader<ReadHalf<'a>>) -> Result<Self> {
        todo!()
    }

}