move to tokio's bytes crate
This commit is contained in:
parent
d1e3c8eaac
commit
90f16ab977
43
Cargo.lock
generated
43
Cargo.lock
generated
@ -69,9 +69,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.6.1"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cesu8"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfb8"
|
name = "cfb8"
|
||||||
@ -123,6 +129,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crab_nbt"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469a8a59b4898974c623174c52d1c47b2e21a19e1f6f849a8046aa9ba03af5cf"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"cesu8",
|
||||||
|
"derive_more",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -177,6 +195,26 @@ dependencies = [
|
|||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "1.0.0-beta.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7abbfc297053be59290e3152f8cbcd52c8642e0728b69ee187d991d4c1af08d"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "1.0.0-beta.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -484,6 +522,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cfb8",
|
"cfb8",
|
||||||
|
"crab_nbt",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"flate2",
|
"flate2",
|
||||||
"image",
|
"image",
|
||||||
|
|||||||
11
README.md
11
README.md
@ -3,13 +3,16 @@ A Minecraft server written in pure Rust
|
|||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [x] Server Status/Ping
|
|
||||||
- [x] Encryption
|
|
||||||
- [ ] Compression
|
|
||||||
- States
|
- States
|
||||||
- [x] Handshake
|
- [x] Handshake
|
||||||
- [x] Status
|
- [x] Status
|
||||||
- [x] Login
|
- [x] Login
|
||||||
- [x] Config
|
- [x] Config
|
||||||
- [ ] Play
|
- [ ] Play
|
||||||
|
- [x] Server Status/Ping
|
||||||
|
- [x] Encryption
|
||||||
|
- [ ] Compression
|
||||||
|
- [x] Configuration
|
||||||
|
|
||||||
|
### Thanks
|
||||||
|
Big thanks to https://wiki.vg/ for providing all the Information we need for create this Project
|
||||||
@ -16,7 +16,7 @@ rsa-der = "0.3.0"
|
|||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
cfb8 = "0.8.1"
|
cfb8 = "0.8.1"
|
||||||
flate2 = "1.0.30"
|
flate2 = "1.0.30"
|
||||||
bytes = "1.6.1"
|
bytes = "1.7"
|
||||||
|
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
|
||||||
@ -30,5 +30,6 @@ mio = { version = "1.0.1", features = ["os-poll", "net"]}
|
|||||||
crossbeam-channel = "0.5.13"
|
crossbeam-channel = "0.5.13"
|
||||||
uuid = "1.10"
|
uuid = "1.10"
|
||||||
toml = "0.8.17"
|
toml = "0.8.17"
|
||||||
|
crab_nbt = "0.1.2"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
protocol::{
|
protocol::{
|
||||||
client::{
|
client::{
|
||||||
config::CFinishConfig,
|
config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry},
|
||||||
login::{CEncryptionRequest, CLoginSuccess},
|
login::{CEncryptionRequest, CLoginSuccess},
|
||||||
status::{CPingResponse, CStatusResponse},
|
status::{CPingResponse, CStatusResponse},
|
||||||
},
|
},
|
||||||
server::{
|
server::{
|
||||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
|
||||||
handshake::SHandShake,
|
handshake::SHandShake,
|
||||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||||
status::{SPingRequest, SStatusRequest},
|
status::{SPingRequest, SStatusRequest},
|
||||||
},
|
},
|
||||||
ConnectionState,
|
ConnectionState, KnownPack,
|
||||||
},
|
},
|
||||||
server::Server,
|
server::Server,
|
||||||
};
|
};
|
||||||
@ -47,6 +47,7 @@ pub trait ClientPacketProcessor {
|
|||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
client_information: SClientInformation,
|
client_information: SClientInformation,
|
||||||
);
|
);
|
||||||
|
fn handle_known_packs(&mut self, server: &mut Server, config_acknowledged: SKnownPacks);
|
||||||
fn handle_config_acknowledged(
|
fn handle_config_acknowledged(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
@ -120,10 +121,19 @@ impl ClientPacketProcessor for Client {
|
|||||||
fn handle_login_acknowledged(
|
fn handle_login_acknowledged(
|
||||||
&mut self,
|
&mut self,
|
||||||
_server: &mut Server,
|
_server: &mut Server,
|
||||||
login_acknowledged: SLoginAcknowledged,
|
_login_acknowledged: SLoginAcknowledged,
|
||||||
) {
|
) {
|
||||||
let _ = login_acknowledged;
|
|
||||||
self.connection_state = ConnectionState::Config;
|
self.connection_state = ConnectionState::Config;
|
||||||
|
Server::send_brand(self);
|
||||||
|
// known data packs
|
||||||
|
self.send_packet(CKnownPacks::new(
|
||||||
|
1,
|
||||||
|
&[KnownPack {
|
||||||
|
namespace: "minecraft".to_string(),
|
||||||
|
id: "core".to_string(),
|
||||||
|
version: "1.21".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
dbg!("login achnowlaged");
|
dbg!("login achnowlaged");
|
||||||
}
|
}
|
||||||
fn handle_client_information(
|
fn handle_client_information(
|
||||||
@ -141,7 +151,34 @@ impl ClientPacketProcessor for Client {
|
|||||||
text_filtering: client_information.text_filtering,
|
text_filtering: client_information.text_filtering,
|
||||||
server_listing: client_information.server_listing,
|
server_listing: client_information.server_listing,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_known_packs(&mut self, server: &mut Server, config_acknowledged: SKnownPacks) {
|
||||||
|
self.send_packet(CRegistryData::new(
|
||||||
|
"0".into(),
|
||||||
|
1,
|
||||||
|
vec![
|
||||||
|
Entry {
|
||||||
|
entry_id: "minecraft:dimension_type".into(),
|
||||||
|
has_data: true,
|
||||||
|
},
|
||||||
|
/* Entry {
|
||||||
|
entry_id: "minecraft:worldgen/biome".into(),
|
||||||
|
has_data: true,
|
||||||
|
},
|
||||||
|
Entry {
|
||||||
|
entry_id: "minecraft:chat_type".into(),
|
||||||
|
has_data: true,
|
||||||
|
},
|
||||||
|
Entry {
|
||||||
|
entry_id: "minecraft:damage_type".into(),
|
||||||
|
has_data: true,
|
||||||
|
}, */
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
// We are done with configuring
|
// We are done with configuring
|
||||||
|
dbg!("finish config");
|
||||||
self.send_packet(CFinishConfig::new());
|
self.send_packet(CFinishConfig::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::{
|
|||||||
protocol::{
|
protocol::{
|
||||||
client::{config::CConfigDisconnect, login::CLoginDisconnect},
|
client::{config::CConfigDisconnect, login::CLoginDisconnect},
|
||||||
server::{
|
server::{
|
||||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
|
||||||
handshake::SHandShake,
|
handshake::SHandShake,
|
||||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||||
status::{SPingRequest, SStatusRequest},
|
status::{SPingRequest, SStatusRequest},
|
||||||
@ -168,6 +168,9 @@ impl Client {
|
|||||||
SAcknowledgeFinishConfig::PACKET_ID => {
|
SAcknowledgeFinishConfig::PACKET_ID => {
|
||||||
self.handle_config_acknowledged(server, SAcknowledgeFinishConfig::read(bytebuf))
|
self.handle_config_acknowledged(server, SAcknowledgeFinishConfig::read(bytebuf))
|
||||||
}
|
}
|
||||||
|
SKnownPacks::PACKET_ID => {
|
||||||
|
self.handle_known_packs(server, SKnownPacks::read(bytebuf))
|
||||||
|
}
|
||||||
_ => log::error!(
|
_ => log::error!(
|
||||||
"Failed to handle packet id {} while in Config state",
|
"Failed to handle packet id {} while in Config state",
|
||||||
packet.id
|
packet.id
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use bytes::{Buf, BytesMut};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::MAX_PACKET_SIZE,
|
client::MAX_PACKET_SIZE,
|
||||||
protocol::{bytebuf::buffer::ByteBuffer, RawPacket, VarInt32, VarIntDecodeError},
|
protocol::{bytebuf::ByteBuffer, RawPacket, VarInt32, VarIntDecodeError},
|
||||||
};
|
};
|
||||||
|
|
||||||
type Cipher = cfb8::Decryptor<aes::Aes128>;
|
type Cipher = cfb8::Decryptor<aes::Aes128>;
|
||||||
@ -56,7 +56,7 @@ impl PacketDecoder {
|
|||||||
Ok(Some(RawPacket {
|
Ok(Some(RawPacket {
|
||||||
len: packet_len,
|
len: packet_len,
|
||||||
id: packet_id,
|
id: packet_id,
|
||||||
bytebuf: ByteBuffer::from_bytes(&data),
|
bytebuf: ByteBuffer::new(BytesMut::from(data)),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use bytes::{BufMut, BytesMut};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::MAX_PACKET_SIZE,
|
client::MAX_PACKET_SIZE,
|
||||||
protocol::{bytebuf::buffer::ByteBuffer, ClientPacket, VarInt32},
|
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt32},
|
||||||
};
|
};
|
||||||
|
|
||||||
type Cipher = cfb8::Encryptor<aes::Aes128>;
|
type Cipher = cfb8::Encryptor<aes::Aes128>;
|
||||||
@ -25,13 +25,13 @@ impl PacketEncoder {
|
|||||||
|
|
||||||
let mut writer = (&mut self.buf).writer();
|
let mut writer = (&mut self.buf).writer();
|
||||||
|
|
||||||
let mut packet_buf = ByteBuffer::new();
|
let mut packet_buf = ByteBuffer::empty();
|
||||||
VarInt32(P::PACKET_ID)
|
VarInt32(P::PACKET_ID)
|
||||||
.encode(&mut writer)
|
.encode(&mut writer)
|
||||||
.context("failed to encode packet ID")?;
|
.context("failed to encode packet ID")?;
|
||||||
packet.write(&mut packet_buf);
|
packet.write(&mut packet_buf);
|
||||||
|
|
||||||
writer.write(packet_buf.as_bytes()).unwrap();
|
writer.write(packet_buf.buf()).unwrap();
|
||||||
|
|
||||||
let data_len = self.buf.len() - start_len;
|
let data_len = self.buf.len() - start_len;
|
||||||
|
|
||||||
|
|||||||
@ -1,168 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io::{self, Read},
|
|
||||||
string::FromUtf8Error,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
|
||||||
use mio::{net::TcpStream, Token, Waker};
|
|
||||||
|
|
||||||
use crate::player::JoinInfo;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
protocol::{clientbound, read::MessageReader, serverbound},
|
|
||||||
ProtocolVersion,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Connection {
|
|
||||||
stream: TcpStream,
|
|
||||||
ver: Option<ProtocolVersion>,
|
|
||||||
|
|
||||||
tx: Sender<clientbound::Packet>,
|
|
||||||
rx: Receiver<clientbound::Packet>,
|
|
||||||
|
|
||||||
token: Token,
|
|
||||||
|
|
||||||
incoming: Vec<u8>,
|
|
||||||
outgoing: Vec<u8>,
|
|
||||||
garbage: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ConnSender {
|
|
||||||
tx: Sender<cb::Packet>,
|
|
||||||
wake: Sender<WakeEvent>,
|
|
||||||
waker: Arc<Waker>,
|
|
||||||
tok: Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NewConn {
|
|
||||||
pub sender: ConnSender,
|
|
||||||
pub info: JoinInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ParseError {
|
|
||||||
InvalidType(i32),
|
|
||||||
CannotHandleOutput,
|
|
||||||
InvalidLength,
|
|
||||||
NotLoggedIn,
|
|
||||||
AlreadyLoggedIn,
|
|
||||||
InvalidPassword,
|
|
||||||
IO(io::Error),
|
|
||||||
InvalidMessage(FromUtf8Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Connection {
|
|
||||||
fn new(stream: TcpStream, token: Token) -> Self {
|
|
||||||
// For a 10 chunk render distance, we need to send 441 packets at once. So a
|
|
||||||
// limit of 512 means we don't block very much.
|
|
||||||
let (tx, rx) = crossbeam_channel::bounded(512);
|
|
||||||
Self {
|
|
||||||
stream,
|
|
||||||
rx,
|
|
||||||
tx,
|
|
||||||
token,
|
|
||||||
incoming: Vec::with_capacity(1024),
|
|
||||||
outgoing: Vec::with_capacity(1024),
|
|
||||||
garbage: vec![0; 256 * 1024],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn send(&self, p: clientbound::Packet) {
|
|
||||||
if let Ok(()) = self.tx.send(p) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self) -> io::Result<(bool, Option<NewConn>, Vec<serverbound::Packet>)> {
|
|
||||||
let mut out = vec![];
|
|
||||||
loop {
|
|
||||||
let n = match self.stream.read(&mut self.garbage) {
|
|
||||||
Ok(0) => return Ok((true, None, out)),
|
|
||||||
Ok(n) => n,
|
|
||||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Ok((false, None, out)),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
self.incoming.extend_from_slice(&self.garbage[..n]);
|
|
||||||
let (new_conn, packets) = self.read_incoming()?;
|
|
||||||
if new_conn.is_some() {
|
|
||||||
return Ok((false, new_conn, packets));
|
|
||||||
}
|
|
||||||
out.extend(packets);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_incoming(&mut self) -> io::Result<(Option<NewConn>, Vec<serverbound::Packet>)> {
|
|
||||||
let mut out = vec![];
|
|
||||||
while !self.incoming.is_empty() {
|
|
||||||
let mut m = MessageReader::new(&self.incoming);
|
|
||||||
match m.read_u32() {
|
|
||||||
Ok(len) => {
|
|
||||||
let len = len as usize;
|
|
||||||
if len + m.index() <= self.incoming.len() {
|
|
||||||
// Remove the length varint at the start
|
|
||||||
let idx = m.index();
|
|
||||||
self.incoming.drain(0..idx);
|
|
||||||
// We already handshaked
|
|
||||||
if self.ver.is_some() {
|
|
||||||
let mut m = MessageReader::new(&self.incoming[..len]);
|
|
||||||
let p = serverbound::Packet::read(&mut m).map_err(|err| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
format!("while reading packet got err: {err}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let n = m.index();
|
|
||||||
self.incoming.drain(0..n);
|
|
||||||
if n != len {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
format!("packet did not parse enough bytes (expected {len}, only parsed {n})"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
out.push(p);
|
|
||||||
} else {
|
|
||||||
// This is the first packet, so it must be a login packet.
|
|
||||||
let mut m = MessageReader::new(&self.incoming[..len]);
|
|
||||||
let info: JoinInfo = m.read().map_err(|e| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
format!("error reading handshake: {e}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let n = m.index();
|
|
||||||
self.incoming.drain(0..n);
|
|
||||||
if n != len {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
format!("handshake did not parse enough bytes (expected {len}, only parsed {n})"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
self.ver = Some(ProtocolVersion::from(info.ver as i32));
|
|
||||||
// We rely on the caller to set the player using this value.
|
|
||||||
return Ok((
|
|
||||||
Some(NewConn {
|
|
||||||
sender: self.sender(),
|
|
||||||
info,
|
|
||||||
}),
|
|
||||||
out,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If this is an EOF, then we have a partial varint, so we are done reading.
|
|
||||||
Err(e) => {
|
|
||||||
if matches!(e, ReadError::Invalid(InvalidReadError::EOF)) {
|
|
||||||
return Ok((None, out));
|
|
||||||
} else {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
format!("error reading packet id: {e}"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok((None, out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,877 +0,0 @@
|
|||||||
use crate::protocol::{nbt::nbt::NBT, VarInt, VarLong};
|
|
||||||
|
|
||||||
use super::{Endian, CONTINUE_BIT, SEGMENT_BITS};
|
|
||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
|
||||||
use std::{
|
|
||||||
fmt::Debug,
|
|
||||||
io::{Error, ErrorKind, Read, Result, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A byte buffer object specifically turned to easily read and write binary values
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ByteBuffer {
|
|
||||||
data: Vec<u8>,
|
|
||||||
wpos: usize,
|
|
||||||
rpos: usize,
|
|
||||||
wbit: usize,
|
|
||||||
rbit: usize,
|
|
||||||
endian: Endian,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for ByteBuffer {
|
|
||||||
fn from(val: &[u8]) -> Self {
|
|
||||||
ByteBuffer::from_bytes(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for ByteBuffer {
|
|
||||||
fn from(val: Vec<u8>) -> Self {
|
|
||||||
ByteBuffer::from_vec(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ByteBuffer> for Vec<u8> {
|
|
||||||
fn from(val: ByteBuffer) -> Self {
|
|
||||||
val.into_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ByteBuffer {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for ByteBuffer {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
|
||||||
self.flush_bits();
|
|
||||||
let read_len = std::cmp::min(self.data.len() - self.rpos, buf.len());
|
|
||||||
let range = self.rpos..self.rpos + read_len;
|
|
||||||
for (i, val) in self.data[range].iter().enumerate() {
|
|
||||||
buf[i] = *val;
|
|
||||||
}
|
|
||||||
self.rpos += read_len;
|
|
||||||
Ok(read_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for ByteBuffer {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
|
||||||
self.write_bytes(buf);
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ByteBuffer {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
let rpos = if self.rbit > 0 {
|
|
||||||
self.rpos + 1
|
|
||||||
} else {
|
|
||||||
self.rpos
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_len = self.data.len() - rpos;
|
|
||||||
let mut remaining_data = vec![0; read_len];
|
|
||||||
let range = rpos..rpos + read_len;
|
|
||||||
for (i, val) in self.data[range].iter().enumerate() {
|
|
||||||
remaining_data[i] = *val;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"ByteBuffer {{ remaining_data: {:?}, total_data: {:?}, wpos: {:?}, rpos: {:?}, endian: {:?} }}",
|
|
||||||
remaining_data, self.data, self.wpos, self.rpos, self.endian
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! read_number {
|
|
||||||
($self:ident, $name:ident, $offset:expr) => {{
|
|
||||||
$self.flush_bits();
|
|
||||||
if $self.rpos + $offset > $self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let range = $self.rpos..$self.rpos + $offset;
|
|
||||||
$self.rpos += $offset;
|
|
||||||
|
|
||||||
Ok(match $self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::$name(&$self.data[range]),
|
|
||||||
Endian::LittleEndian => LittleEndian::$name(&$self.data[range]),
|
|
||||||
})
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ByteBuffer {
|
|
||||||
/// Construct a new, empty, ByteBuffer
|
|
||||||
pub fn new() -> ByteBuffer {
|
|
||||||
ByteBuffer {
|
|
||||||
data: vec![],
|
|
||||||
wpos: 0,
|
|
||||||
rpos: 0,
|
|
||||||
rbit: 0,
|
|
||||||
wbit: 0,
|
|
||||||
endian: Endian::BigEndian,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a new ByteBuffer filled with the data array.
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> ByteBuffer {
|
|
||||||
let mut buffer = ByteBuffer::new();
|
|
||||||
buffer.write_bytes(bytes);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new ByteBuffer from an existing vector. This
|
|
||||||
/// function takes ownership of the vector
|
|
||||||
pub fn from_vec(vec: Vec<u8>) -> ByteBuffer {
|
|
||||||
let len = vec.len();
|
|
||||||
ByteBuffer {
|
|
||||||
data: vec,
|
|
||||||
wpos: len,
|
|
||||||
rpos: 0,
|
|
||||||
rbit: 0,
|
|
||||||
wbit: 0,
|
|
||||||
endian: Endian::BigEndian,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the buffer size
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.data.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear the buffer and reinitialize the reading and writing cursors
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.data.clear();
|
|
||||||
self.reset_cursors();
|
|
||||||
self.reset_bits_cursors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reinitialize the reading and writing cursor
|
|
||||||
pub fn reset_cursors(&mut self) {
|
|
||||||
self.wpos = 0;
|
|
||||||
self.rpos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reinitialize the bit reading and bit writing cursor
|
|
||||||
pub fn reset_bits_cursors(&mut self) {
|
|
||||||
self.rbit = 0;
|
|
||||||
self.wbit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change the buffer size to size.
|
|
||||||
///
|
|
||||||
/// _Note_: You cannot shrink a buffer with this method
|
|
||||||
pub fn resize(&mut self, size: usize) {
|
|
||||||
let diff = size - self.data.len();
|
|
||||||
if diff > 0 {
|
|
||||||
self.data.extend(std::iter::repeat(0).take(diff))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the byte order of the buffer
|
|
||||||
///
|
|
||||||
/// _Note_: By default the buffer uses big endian order
|
|
||||||
pub fn set_endian(&mut self, endian: Endian) {
|
|
||||||
self.endian = endian;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current byte order of the buffer
|
|
||||||
pub fn endian(&self) -> Endian {
|
|
||||||
self.endian
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write operations
|
|
||||||
|
|
||||||
/// Append a byte array to the buffer. The buffer is automatically extended if needed
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_bytes(&vec![0x1, 0xFF, 0x45]); // buffer contains [0x1, 0xFF, 0x45]
|
|
||||||
/// ```
|
|
||||||
pub fn write_bytes(&mut self, bytes: &[u8]) {
|
|
||||||
self.flush_bits();
|
|
||||||
|
|
||||||
let size = bytes.len() + self.wpos;
|
|
||||||
|
|
||||||
if size > self.data.len() {
|
|
||||||
self.resize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in bytes {
|
|
||||||
self.data[self.wpos] = *v;
|
|
||||||
self.wpos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_bool(&mut self, v: bool) {
|
|
||||||
if v {
|
|
||||||
self.write_u8(1);
|
|
||||||
} else {
|
|
||||||
self.write_u8(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_bytes_len(&mut self, bytes: &[u8], max_len: usize) {
|
|
||||||
self.flush_bits();
|
|
||||||
|
|
||||||
let size = bytes.len() + self.wpos;
|
|
||||||
if size > max_len {
|
|
||||||
eprintln!("Write: size > max size");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if size > self.data.len() {
|
|
||||||
self.resize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in bytes {
|
|
||||||
self.data[self.wpos] = *v;
|
|
||||||
self.wpos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a byte (8 bits value) to the buffer
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_u8(1) // buffer contains [0x1]
|
|
||||||
/// ```
|
|
||||||
pub fn write_u8(&mut self, val: u8) {
|
|
||||||
self.write_bytes(&[val]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `write_u8()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn write_i8(&mut self, val: i8) {
|
|
||||||
self.write_u8(val as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a word (16 bits value) to the buffer
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_u16(1) // buffer contains [0x00, 0x1] if little endian
|
|
||||||
/// ```
|
|
||||||
pub fn write_u16(&mut self, val: u16) {
|
|
||||||
let mut buf = [0; 2];
|
|
||||||
|
|
||||||
match self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::write_u16(&mut buf, val),
|
|
||||||
Endian::LittleEndian => LittleEndian::write_u16(&mut buf, val),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.write_bytes(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `write_u16()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn write_i16(&mut self, val: i16) {
|
|
||||||
self.write_u16(val as u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a double word (32 bits value) to the buffer
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_u32(1) // buffer contains [0x00, 0x00, 0x00, 0x1] if little endian
|
|
||||||
/// ```
|
|
||||||
pub fn write_u32(&mut self, val: u32) {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
|
|
||||||
match self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::write_u32(&mut buf, val),
|
|
||||||
Endian::LittleEndian => LittleEndian::write_u32(&mut buf, val),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.write_bytes(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `write_u32()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn write_i32(&mut self, val: i32) {
|
|
||||||
self.write_u32(val as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_var_int(&mut self, value: VarInt) {
|
|
||||||
let mut val = value as u32;
|
|
||||||
for _ in 0..5 {
|
|
||||||
let mut b: u8 = val as u8 & 0b01111111;
|
|
||||||
val >>= 7;
|
|
||||||
if val != 0 {
|
|
||||||
b |= 0b10000000;
|
|
||||||
}
|
|
||||||
self.write_u8(b);
|
|
||||||
if val == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a quaddruple word (64 bits value) to the buffer
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_u64(1) // buffer contains [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1] if little endian
|
|
||||||
/// ```
|
|
||||||
pub fn write_u64(&mut self, val: u64) {
|
|
||||||
let mut buf = [0; 8];
|
|
||||||
match self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::write_u64(&mut buf, val),
|
|
||||||
Endian::LittleEndian => LittleEndian::write_u64(&mut buf, val),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.write_bytes(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `write_u64()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn write_i64(&mut self, val: i64) {
|
|
||||||
self.write_u64(val as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a 32 bits floating point number to the buffer.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_f32(0.1)
|
|
||||||
/// ```
|
|
||||||
pub fn write_f32(&mut self, val: f32) {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
|
|
||||||
match self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::write_f32(&mut buf, val),
|
|
||||||
Endian::LittleEndian => LittleEndian::write_f32(&mut buf, val),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.write_bytes(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a 64 bits floating point number to the buffer.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_f64(0.1)
|
|
||||||
/// ```
|
|
||||||
pub fn write_f64(&mut self, val: f64) {
|
|
||||||
let mut buf = [0; 8];
|
|
||||||
|
|
||||||
match self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::write_f64(&mut buf, val),
|
|
||||||
Endian::LittleEndian => LittleEndian::write_f64(&mut buf, val),
|
|
||||||
};
|
|
||||||
self.write_bytes(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append a string to the buffer.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// *Format* The format is `(u32)size + size * (u8)characters`
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_string("Hello")
|
|
||||||
/// ```
|
|
||||||
pub fn write_string(&mut self, val: &str) {
|
|
||||||
self.write_string_len(val, 32767)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_string_len(&mut self, val: &str, max_len: usize) {
|
|
||||||
self.write_var_int(val.len() as VarInt);
|
|
||||||
self.write_bytes_len(val.as_bytes(), max_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_string_array(&mut self, array: &[String]) {
|
|
||||||
for string in array {
|
|
||||||
self.write_string(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read operations
|
|
||||||
|
|
||||||
/// Read a defined amount of raw bytes, or return an IO error if not enough bytes are
|
|
||||||
/// available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>> {
|
|
||||||
self.flush_bits();
|
|
||||||
if self.rpos + size > self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bytes from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let range = self.rpos..self.rpos + size;
|
|
||||||
let mut res = Vec::<u8>::new();
|
|
||||||
res.write_all(&self.data[range])?;
|
|
||||||
self.rpos += size;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read one byte, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![0x1]);
|
|
||||||
/// let value = buffer.read_u8().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u8(&mut self) -> Result<u8> {
|
|
||||||
self.flush_bits();
|
|
||||||
if self.rpos >= self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let pos = self.rpos;
|
|
||||||
self.rpos += 1;
|
|
||||||
Ok(self.data[pos])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_bool(&mut self) -> Result<bool> {
|
|
||||||
Ok(self.read_u8()? != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u8()` but for signed values
|
|
||||||
pub fn read_i8(&mut self) -> Result<i8> {
|
|
||||||
Ok(self.read_u8()? as i8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 2-bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x1]);
|
|
||||||
/// let value = buffer.read_u16().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u16(&mut self) -> Result<u16> {
|
|
||||||
read_number!(self, read_u16, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u16()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i16(&mut self) -> Result<i16> {
|
|
||||||
Ok(self.read_u16()? as i16)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a four-bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x0, 0x0, 0x1]);
|
|
||||||
/// let value = buffer.read_u32().unwrap(); // Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u32(&mut self) -> Result<u32> {
|
|
||||||
read_number!(self, read_u32, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u32()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i32(&mut self) -> Result<i32> {
|
|
||||||
Ok(self.read_u32()? as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read an eight bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1]);
|
|
||||||
/// let value = buffer.read_u64().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u64(&mut self) -> Result<u64> {
|
|
||||||
read_number!(self, read_u64, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a boolean. If true, the closure is called, and the returned value is
|
|
||||||
/// wrapped in Some. Otherwise, this returns None.
|
|
||||||
pub fn read_option<T>(&mut self, val: impl FnOnce(&mut ByteBuffer) -> T) -> Result<Option<T>> {
|
|
||||||
if self.read_bool()? {
|
|
||||||
Ok(Some(val(self)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Writes `true` if the option is Some, or `false` if None. If the option is
|
|
||||||
/// some, then it also calls the `write` closure.
|
|
||||||
pub fn write_option<T>(&mut self, val: &Option<T>, write: impl FnOnce(&mut ByteBuffer, &T)) {
|
|
||||||
self.write_bool(val.is_some());
|
|
||||||
match val {
|
|
||||||
Some(v) => write(self, v),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_var_int(&mut self) -> Result<VarInt> {
|
|
||||||
let mut value: i32 = 0;
|
|
||||||
let mut position: i32 = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let read = self.read_u8()?;
|
|
||||||
|
|
||||||
value |= ((read & SEGMENT_BITS) as i32) << position;
|
|
||||||
|
|
||||||
if read & CONTINUE_BIT == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
position += 7;
|
|
||||||
|
|
||||||
if position >= 32 {
|
|
||||||
return Err(Error::new(ErrorKind::InvalidData, "VarInt is too big"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_var_long(&mut self) -> Result<VarLong> {
|
|
||||||
let mut value: i64 = 0;
|
|
||||||
let mut position: i64 = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let read = self.read_u8()?;
|
|
||||||
|
|
||||||
value |= ((read & SEGMENT_BITS) as i64) << position;
|
|
||||||
|
|
||||||
if read & CONTINUE_BIT == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
position += 7;
|
|
||||||
|
|
||||||
if position >= 64 {
|
|
||||||
return Err(Error::new(ErrorKind::InvalidData, "VarLong is too big"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u64()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i64(&mut self) -> Result<i64> {
|
|
||||||
Ok(self.read_u64()? as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 32 bits floating point value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_f32(&mut self) -> Result<f32> {
|
|
||||||
read_number!(self, read_f32, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 64 bits floating point value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_f64(&mut self) -> Result<f64> {
|
|
||||||
read_number!(self, read_f64, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_list<T>(&mut self, val: impl Fn(&mut ByteBuffer) -> Result<T>) -> Result<Vec<T>> {
|
|
||||||
let len = self.read_var_int()?.try_into().unwrap();
|
|
||||||
let mut list = Vec::with_capacity(len);
|
|
||||||
for _ in 0..len {
|
|
||||||
list.push(val(self)?);
|
|
||||||
}
|
|
||||||
Ok(list)
|
|
||||||
}
|
|
||||||
/// Writes a list to the buffer.
|
|
||||||
pub fn write_list<T>(&mut self, list: &[T], write: impl Fn(&mut ByteBuffer, &T)) {
|
|
||||||
self.write_var_int(list.len().try_into().unwrap());
|
|
||||||
for v in list {
|
|
||||||
write(self, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_varint_arr(&mut self) -> Result<Vec<i32>> {
|
|
||||||
self.read_list(|buf| buf.read_var_int())
|
|
||||||
}
|
|
||||||
pub fn write_varint_arr(&mut self, v: &[i32]) {
|
|
||||||
self.write_list(v, |p, &v| p.write_var_int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_nbt(&mut self) -> Result<NBT> {
|
|
||||||
match NBT::deserialize_buf(self) {
|
|
||||||
Ok(v) => Ok(v),
|
|
||||||
Err(err) => {
|
|
||||||
return Err(Error::new(ErrorKind::InvalidData, "Failed read nbt"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a string.
|
|
||||||
///
|
|
||||||
/// _Note_: First it reads a 32 bits value representing the size, then 'size' raw bytes
|
|
||||||
/// that must be encoded as UTF8.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_string(&mut self) -> Result<String> {
|
|
||||||
self.read_string_len(32767)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_string_len(&mut self, max_size: usize) -> Result<String> {
|
|
||||||
let size = self.read_var_int()?;
|
|
||||||
if size as usize > max_size {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::InvalidData,
|
|
||||||
"String length is bigger than max size",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let data = self.read_bytes(size as usize)?;
|
|
||||||
if data.len() > max_size {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::InvalidData,
|
|
||||||
"String is bigger than max size",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
match String::from_utf8(data) {
|
|
||||||
Ok(string_result) => Ok(string_result),
|
|
||||||
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads 16 bytes from the buffer, and returns that as a big endian UUID.
|
|
||||||
pub fn read_uuid(&mut self) -> Result<uuid::Uuid> {
|
|
||||||
let mut bytes = [0u8; 16];
|
|
||||||
self.read_exact(&mut bytes)?;
|
|
||||||
uuid::Uuid::from_slice(&bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This writes a UUID into the buffer (in big endian format).
|
|
||||||
pub fn write_uuid(&mut self, v: uuid::Uuid) {
|
|
||||||
self.write_bytes(v.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other
|
|
||||||
|
|
||||||
/// Dump the byte buffer to a string.
|
|
||||||
pub fn to_hex_dump(&self) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
for b in &self.data {
|
|
||||||
str = str + &format!("0x{:01$x} ", b, 2);
|
|
||||||
}
|
|
||||||
str.pop();
|
|
||||||
str
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the position of the reading cursor
|
|
||||||
pub fn get_rpos(&self) -> usize {
|
|
||||||
self.rpos
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the reading cursor position.
|
|
||||||
/// _Note_: Sets the reading cursor to `min(newPosition, self.len())` to prevent overflow
|
|
||||||
pub fn set_rpos(&mut self, rpos: usize) {
|
|
||||||
self.rpos = std::cmp::min(rpos, self.data.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the writing cursor position
|
|
||||||
pub fn get_wpos(&self) -> usize {
|
|
||||||
self.wpos
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the writing cursor position.
|
|
||||||
/// _Note_: Sets the writing cursor to `min(newPosition, self.len())` to prevent overflow
|
|
||||||
pub fn set_wpos(&mut self, wpos: usize) {
|
|
||||||
self.wpos = std::cmp::min(wpos, self.data.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the raw byte buffer bytes.
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the raw byte buffer as a Vec<u8>.
|
|
||||||
#[deprecated(
|
|
||||||
since = "2.1.0",
|
|
||||||
note = "use `as_bytes().to_vec()` or `into_vec()` instead"
|
|
||||||
)]
|
|
||||||
pub fn into_bytes(&self) -> Vec<u8> {
|
|
||||||
self.data.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the raw byte buffer as a Vec<u8>.
|
|
||||||
pub fn into_vec(self) -> Vec<u8> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
//Bit manipulation functions
|
|
||||||
|
|
||||||
/// Read 1 bit. Return true if the bit is set to 1, otherwhise, return false.
|
|
||||||
///
|
|
||||||
/// _Note_: Bits are read from left to right
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![128]); // 10000000b
|
|
||||||
/// let value1 = buffer.read_bit().unwrap(); //value1 contains true (eg: bit is 1)
|
|
||||||
/// let value2 = buffer.read_bit().unwrap(); //value2 contains false (eg: bit is 0)
|
|
||||||
/// ```
|
|
||||||
pub fn read_bit(&mut self) -> Result<bool> {
|
|
||||||
if self.rpos >= self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let bit = self.data[self.rpos] & (1 << (7 - self.rbit)) != 0;
|
|
||||||
self.rbit += 1;
|
|
||||||
if self.rbit > 7 {
|
|
||||||
self.flush_rbits();
|
|
||||||
}
|
|
||||||
Ok(bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read n bits. an return the corresponding value an u64.
|
|
||||||
///
|
|
||||||
/// _Note_: We cannot read more than 64 bits
|
|
||||||
///
|
|
||||||
/// _Note_: Bits are read from left to right
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::from_bytes(&vec![128]); // 10000000b
|
|
||||||
/// let value = buffer.read_bits(3).unwrap(); // value contains 4 (eg: 100b)
|
|
||||||
/// ```
|
|
||||||
pub fn read_bits(&mut self, n: u8) -> Result<u64> {
|
|
||||||
if n > 64 {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::InvalidInput,
|
|
||||||
"cannot read more than 64 bits",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
Ok(0)
|
|
||||||
} else {
|
|
||||||
Ok((u64::from(self.read_bit()?) << (n - 1)) | self.read_bits(n - 1)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Discard all the pending bits available for reading or writing and place the corresponding cursor to the next byte.
|
|
||||||
///
|
|
||||||
/// _Note_: If no bits are currently read or written, this function does nothing.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// 10010010 | 00000001
|
|
||||||
/// ^
|
|
||||||
/// 10010010 | 00000001 // read_bit called
|
|
||||||
/// ^
|
|
||||||
/// 10010010 | 00000001 // flush_bit() called
|
|
||||||
/// ^
|
|
||||||
/// ```
|
|
||||||
pub fn flush_bits(&mut self) {
|
|
||||||
if self.rbit > 0 {
|
|
||||||
self.flush_rbits();
|
|
||||||
}
|
|
||||||
if self.wbit > 0 {
|
|
||||||
self.flush_wbits();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_rbits(&mut self) {
|
|
||||||
self.rpos += 1;
|
|
||||||
self.rbit = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_wbits(&mut self) {
|
|
||||||
self.wpos += 1;
|
|
||||||
self.wbit = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append 1 bit value to the buffer.
|
|
||||||
/// The bit is appended like this :
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// ...| XXXXXXXX | 10000000 |....
|
|
||||||
/// ```
|
|
||||||
pub fn write_bit(&mut self, bit: bool) {
|
|
||||||
let size = self.wpos + 1;
|
|
||||||
if size > self.data.len() {
|
|
||||||
self.resize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if bit {
|
|
||||||
self.data[self.wpos] |= 1 << (7 - self.wbit);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.wbit += 1;
|
|
||||||
|
|
||||||
if self.wbit > 7 {
|
|
||||||
self.wbit = 0;
|
|
||||||
self.wpos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the given value as a sequence of n bits
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let mut buffer = ByteBuffer::new();
|
|
||||||
/// buffer.write_bits(4, 3); // append 100b
|
|
||||||
/// ```
|
|
||||||
pub fn write_bits(&mut self, value: u64, n: u8) {
|
|
||||||
if n > 0 {
|
|
||||||
self.write_bit((value >> (n - 1)) & 1 != 0);
|
|
||||||
self.write_bits(value, n - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,279 @@
|
|||||||
pub mod buffer;
|
use core::str;
|
||||||
pub mod reader;
|
use std::io::{self, Error, ErrorKind, Read};
|
||||||
|
|
||||||
|
use bytes::{Buf, BufMut, BytesMut};
|
||||||
|
|
||||||
|
use crate::protocol::{VarInt, VarLong};
|
||||||
|
|
||||||
const SEGMENT_BITS: u8 = 0x7F;
|
const SEGMENT_BITS: u8 = 0x7F;
|
||||||
const CONTINUE_BIT: u8 = 0x80;
|
const CONTINUE_BIT: u8 = 0x80;
|
||||||
/// An enum to represent the byte order of the ByteBuffer object
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
pub struct ByteBuffer {
|
||||||
pub enum Endian {
|
buffer: BytesMut,
|
||||||
BigEndian,
|
}
|
||||||
LittleEndian,
|
|
||||||
|
impl ByteBuffer {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: BytesMut::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(buffer: BytesMut) -> Self {
|
||||||
|
Self { buffer }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_var_int(&mut self) -> VarInt {
|
||||||
|
let mut value: i32 = 0;
|
||||||
|
let mut position: i32 = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let read = self.buffer.get_u8();
|
||||||
|
|
||||||
|
value |= ((read & SEGMENT_BITS) as i32) << position;
|
||||||
|
|
||||||
|
if read & CONTINUE_BIT == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += 7;
|
||||||
|
|
||||||
|
if position >= 32 {
|
||||||
|
panic!("VarInt is too big");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_var_long(&mut self) -> VarLong {
|
||||||
|
let mut value: i64 = 0;
|
||||||
|
let mut position: i64 = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let read = self.buffer.get_u8();
|
||||||
|
|
||||||
|
value |= ((read & SEGMENT_BITS) as i64) << position;
|
||||||
|
|
||||||
|
if read & CONTINUE_BIT == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += 7;
|
||||||
|
|
||||||
|
if position >= 64 {
|
||||||
|
panic!("VarInt is too big");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string(&mut self) -> Result<String, io::Error> {
|
||||||
|
self.get_string_len(32767)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string_len(&mut self, max_size: usize) -> Result<String, io::Error> {
|
||||||
|
let size = self.get_var_int();
|
||||||
|
if size as usize > max_size {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
"String length is bigger than max size",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let data = self.buffer.copy_to_bytes(size as usize);
|
||||||
|
if data.len() > max_size {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
"String is bigger than max size",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
match str::from_utf8(&data) {
|
||||||
|
Ok(string_result) => Ok(string_result.to_string()),
|
||||||
|
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bool(&mut self) -> bool {
|
||||||
|
self.buffer.get_u8() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_uuid(&mut self) -> uuid::Uuid {
|
||||||
|
let mut bytes = [0u8; 16];
|
||||||
|
self.buffer.copy_to_slice(&mut bytes);
|
||||||
|
uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_bool(&mut self, v: bool) {
|
||||||
|
if v {
|
||||||
|
self.buffer.put_u8(1);
|
||||||
|
} else {
|
||||||
|
self.buffer.put_u8(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_uuid(&mut self, v: uuid::Uuid) {
|
||||||
|
self.buffer.put_slice(v.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_string(&mut self, val: &str) {
|
||||||
|
self.put_var_int(val.len() as VarInt);
|
||||||
|
self.buffer.put(val.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_string_array(&mut self, array: &[String]) {
|
||||||
|
for string in array {
|
||||||
|
self.put_string(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_var_int(&mut self, value: VarInt) {
|
||||||
|
let mut val = value as u32;
|
||||||
|
for _ in 0..5 {
|
||||||
|
let mut b: u8 = val as u8 & 0b01111111;
|
||||||
|
val >>= 7;
|
||||||
|
if val != 0 {
|
||||||
|
b |= 0b10000000;
|
||||||
|
}
|
||||||
|
self.buffer.put_u8(b);
|
||||||
|
if val == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a boolean. If true, the closure is called, and the returned value is
|
||||||
|
/// wrapped in Some. Otherwise, this returns None.
|
||||||
|
pub fn get_option<T>(&mut self, val: impl FnOnce(&mut Self) -> T) -> Option<T> {
|
||||||
|
if self.get_bool() {
|
||||||
|
Some(val(self))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Writes `true` if the option is Some, or `false` if None. If the option is
|
||||||
|
/// some, then it also calls the `write` closure.
|
||||||
|
pub fn put_option<T>(&mut self, val: &Option<T>, write: impl FnOnce(&mut Self, &T)) {
|
||||||
|
self.put_bool(val.is_some());
|
||||||
|
match val {
|
||||||
|
Some(v) => write(self, v),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_list<T>(&mut self, val: impl Fn(&mut Self) -> T) -> Vec<T> {
|
||||||
|
let len = self.get_var_int().try_into().unwrap();
|
||||||
|
let mut list = Vec::with_capacity(len);
|
||||||
|
for _ in 0..len {
|
||||||
|
list.push(val(self));
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
/// Writes a list to the buffer.
|
||||||
|
pub fn put_list<T>(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) {
|
||||||
|
self.put_var_int(list.len().try_into().unwrap());
|
||||||
|
for v in list {
|
||||||
|
write(self, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_varint_arr(&mut self, v: &[i32]) {
|
||||||
|
self.put_list(v, |p, &v| p.put_var_int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nbt(&mut self) -> Option<crab_nbt::NbtTag> {
|
||||||
|
match crab_nbt::NbtTag::deserialize(self.buf()) {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(err) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buf(&mut self) -> &mut BytesMut {
|
||||||
|
&mut self.buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trait
|
||||||
|
impl ByteBuffer {
|
||||||
|
pub fn get_u8(&mut self) -> u8 {
|
||||||
|
self.buffer.get_u8()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_i8(&mut self) -> i8 {
|
||||||
|
self.buffer.get_i8()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_u16(&mut self) -> u16 {
|
||||||
|
self.buffer.get_u16()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_i16(&mut self) -> i16 {
|
||||||
|
self.buffer.get_i16()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_u32(&mut self) -> u32 {
|
||||||
|
self.buffer.get_u32()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_i32(&mut self) -> i32 {
|
||||||
|
self.buffer.get_i32()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_u64(&mut self) -> u64 {
|
||||||
|
self.buffer.get_u64()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_i64(&mut self) -> i64 {
|
||||||
|
self.buffer.get_i64()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_f32(&mut self) -> f32 {
|
||||||
|
self.buffer.get_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_f64(&mut self) -> f64 {
|
||||||
|
self.buffer.get_f64()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_u8(&mut self, n: u8) {
|
||||||
|
self.buffer.put_u8(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_i8(&mut self, n: i8) {
|
||||||
|
self.buffer.put_i8(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_u16(&mut self, n: u16) {
|
||||||
|
self.buffer.put_u16(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_i16(&mut self, n: i16) {
|
||||||
|
self.buffer.put_i16(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_u32(&mut self, n: u32) {
|
||||||
|
self.buffer.put_u32(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_i32(&mut self, n: i32) {
|
||||||
|
self.buffer.put_i32(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_u64(&mut self, n: u64) {
|
||||||
|
self.buffer.put_u64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_i64(&mut self, n: i64) {
|
||||||
|
self.buffer.put_i64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_f32(&mut self, n: f32) {
|
||||||
|
self.buffer.put_f32(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes {
|
||||||
|
self.buffer.copy_to_bytes(len)
|
||||||
|
}
|
||||||
|
pub fn put_slice(&mut self, src: &[u8]) {
|
||||||
|
self.buffer.put_slice(src)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,368 +0,0 @@
|
|||||||
use super::Endian;
|
|
||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
|
||||||
use std::{
|
|
||||||
fmt::Debug,
|
|
||||||
io::{Error, ErrorKind, Read, Result, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A byte buffer object specifically turned to easily read and write binary values
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ByteReader<'a> {
|
|
||||||
data: &'a [u8],
|
|
||||||
rpos: usize,
|
|
||||||
rbit: usize,
|
|
||||||
endian: Endian,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a [u8]> for ByteReader<'a> {
|
|
||||||
fn from(val: &'a [u8]) -> Self {
|
|
||||||
ByteReader::from_bytes(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Read for ByteReader<'a> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
|
||||||
self.flush_bits();
|
|
||||||
let read_len = std::cmp::min(self.data.len() - self.rpos, buf.len());
|
|
||||||
let range = self.rpos..self.rpos + read_len;
|
|
||||||
for (i, val) in self.data[range].iter().enumerate() {
|
|
||||||
buf[i] = *val;
|
|
||||||
}
|
|
||||||
self.rpos += read_len;
|
|
||||||
Ok(read_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Debug for ByteReader<'a> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
let rpos = if self.rbit > 0 {
|
|
||||||
self.rpos + 1
|
|
||||||
} else {
|
|
||||||
self.rpos
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_len = self.data.len() - rpos;
|
|
||||||
let mut remaining_data = vec![0; read_len];
|
|
||||||
let range = rpos..rpos + read_len;
|
|
||||||
for (i, val) in self.data[range].iter().enumerate() {
|
|
||||||
remaining_data[i] = *val;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"ByteReader {{ remaining_data: {:?}, total_data: {:?}, rpos: {:?}, endian: {:?} }}",
|
|
||||||
remaining_data, self.data, self.rpos, self.endian
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! read_number {
|
|
||||||
($self:ident, $name:ident, $offset:expr) => {{
|
|
||||||
$self.flush_bits();
|
|
||||||
if $self.rpos + $offset > $self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let range = $self.rpos..$self.rpos + $offset;
|
|
||||||
$self.rpos += $offset;
|
|
||||||
|
|
||||||
Ok(match $self.endian {
|
|
||||||
Endian::BigEndian => BigEndian::$name(&$self.data[range]),
|
|
||||||
Endian::LittleEndian => LittleEndian::$name(&$self.data[range]),
|
|
||||||
})
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ByteReader<'a> {
|
|
||||||
/// Construct a new ByteReader filled with the data array.
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> ByteReader {
|
|
||||||
ByteReader {
|
|
||||||
data: bytes,
|
|
||||||
rpos: 0,
|
|
||||||
rbit: 0,
|
|
||||||
endian: Endian::BigEndian,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the buffer size
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.data.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reinitialize the reading cursor
|
|
||||||
pub fn reset_cursors(&mut self) {
|
|
||||||
self.rpos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reinitialize the bit reading cursor
|
|
||||||
pub fn reset_bits_cursors(&mut self) {
|
|
||||||
self.rbit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the byte order of the buffer
|
|
||||||
///
|
|
||||||
/// _Note_: By default the buffer uses big endian order
|
|
||||||
pub fn set_endian(&mut self, endian: Endian) {
|
|
||||||
self.endian = endian;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current byte order of the buffer
|
|
||||||
pub fn endian(&self) -> Endian {
|
|
||||||
self.endian
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read operations
|
|
||||||
|
|
||||||
/// Read a defined amount of raw bytes, or return an IO error if not enough bytes are
|
|
||||||
/// available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>> {
|
|
||||||
self.flush_bits();
|
|
||||||
if self.rpos + size > self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bytes from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let range = self.rpos..self.rpos + size;
|
|
||||||
let mut res = Vec::<u8>::new();
|
|
||||||
res.write_all(&self.data[range])?;
|
|
||||||
self.rpos += size;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read one byte, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![0x1];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data);
|
|
||||||
/// let value = buffer.read_u8().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u8(&mut self) -> Result<u8> {
|
|
||||||
self.flush_bits();
|
|
||||||
if self.rpos >= self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let pos = self.rpos;
|
|
||||||
self.rpos += 1;
|
|
||||||
Ok(self.data[pos])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u8()` but for signed values
|
|
||||||
pub fn read_i8(&mut self) -> Result<i8> {
|
|
||||||
Ok(self.read_u8()? as i8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 2-bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![0x0, 0x1];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data);
|
|
||||||
/// let value = buffer.read_u16().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u16(&mut self) -> Result<u16> {
|
|
||||||
read_number!(self, read_u16, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u16()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i16(&mut self) -> Result<i16> {
|
|
||||||
Ok(self.read_u16()? as i16)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a four-bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![0x0, 0x0, 0x0, 0x1];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data);
|
|
||||||
/// let value = buffer.read_u32().unwrap(); // Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u32(&mut self) -> Result<u32> {
|
|
||||||
read_number!(self, read_u32, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u32()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i32(&mut self) -> Result<i32> {
|
|
||||||
Ok(self.read_u32()? as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read an eight bytes long value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data);
|
|
||||||
/// let value = buffer.read_u64().unwrap(); //Value contains 1
|
|
||||||
/// ```
|
|
||||||
pub fn read_u64(&mut self) -> Result<u64> {
|
|
||||||
read_number!(self, read_u64, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `read_u64()` but for signed values
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_i64(&mut self) -> Result<i64> {
|
|
||||||
Ok(self.read_u64()? as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 32 bits floating point value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_f32(&mut self) -> Result<f32> {
|
|
||||||
read_number!(self, read_f32, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a 64 bits floating point value, or return an IO error if not enough bytes are available.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_f64(&mut self) -> Result<f64> {
|
|
||||||
read_number!(self, read_f64, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a string.
|
|
||||||
///
|
|
||||||
/// _Note_: First it reads a 32 bits value representing the size, then 'size' raw bytes
|
|
||||||
/// that must be encoded as UTF8.
|
|
||||||
/// _Note_: This method resets the read and write cursor for bitwise reading.
|
|
||||||
pub fn read_string(&mut self) -> Result<String> {
|
|
||||||
let size = self.read_u32()?;
|
|
||||||
match String::from_utf8(self.read_bytes(size as usize)?) {
|
|
||||||
Ok(string_result) => Ok(string_result),
|
|
||||||
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other
|
|
||||||
|
|
||||||
/// Dump the byte buffer to a string.
|
|
||||||
pub fn to_hex_dump(&self) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
for b in self.data {
|
|
||||||
str = str + &format!("0x{:01$x} ", b, 2);
|
|
||||||
}
|
|
||||||
str.pop();
|
|
||||||
str
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the position of the reading cursor
|
|
||||||
pub fn get_rpos(&self) -> usize {
|
|
||||||
self.rpos
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the reading cursor position.
|
|
||||||
/// _Note_: Sets the reading cursor to `min(newPosition, self.len())` to prevent overflow
|
|
||||||
pub fn set_rpos(&mut self, rpos: usize) {
|
|
||||||
self.rpos = std::cmp::min(rpos, self.data.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the raw byte buffer bytes.
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
//Bit manipulation functions
|
|
||||||
|
|
||||||
/// Read 1 bit. Return true if the bit is set to 1, otherwhise, return false.
|
|
||||||
///
|
|
||||||
/// _Note_: Bits are read from left to right
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![128];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data); // 10000000b
|
|
||||||
/// let value1 = buffer.read_bit().unwrap(); //value1 contains true (eg: bit is 1)
|
|
||||||
/// let value2 = buffer.read_bit().unwrap(); //value2 contains false (eg: bit is 0)
|
|
||||||
/// ```
|
|
||||||
pub fn read_bit(&mut self) -> Result<bool> {
|
|
||||||
if self.rpos >= self.data.len() {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
"could not read enough bits from buffer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let bit = self.data[self.rpos] & (1 << (7 - self.rbit)) != 0;
|
|
||||||
self.rbit += 1;
|
|
||||||
if self.rbit > 7 {
|
|
||||||
self.flush_rbits();
|
|
||||||
}
|
|
||||||
Ok(bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read n bits. an return the corresponding value an u64.
|
|
||||||
///
|
|
||||||
/// _Note_: We cannot read more than 64 bits
|
|
||||||
///
|
|
||||||
/// _Note_: Bits are read from left to right
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bytebuffer::*;
|
|
||||||
/// let data = vec![128];
|
|
||||||
/// let mut buffer = ByteReader::from_bytes(&data); // 10000000b
|
|
||||||
/// let value = buffer.read_bits(3).unwrap(); // value contains 4 (eg: 100b)
|
|
||||||
/// ```
|
|
||||||
pub fn read_bits(&mut self, n: u8) -> Result<u64> {
|
|
||||||
if n > 64 {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::InvalidInput,
|
|
||||||
"cannot read more than 64 bits",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
Ok(0)
|
|
||||||
} else {
|
|
||||||
Ok((u64::from(self.read_bit()?) << (n - 1)) | self.read_bits(n - 1)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Discard all the pending bits available for reading and place the corresponding cursor to the next byte.
|
|
||||||
///
|
|
||||||
/// _Note_: If no bits are currently read, this function does nothing.
|
|
||||||
///
|
|
||||||
/// #Example
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// 10010010 | 00000001
|
|
||||||
/// ^
|
|
||||||
/// 10010010 | 00000001 // read_bit called
|
|
||||||
/// ^
|
|
||||||
/// 10010010 | 00000001 // flush_bit() called
|
|
||||||
/// ^
|
|
||||||
/// ```
|
|
||||||
pub fn flush_bits(&mut self) {
|
|
||||||
if self.rbit > 0 {
|
|
||||||
self.flush_rbits();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_rbits(&mut self) {
|
|
||||||
self.rpos += 1;
|
|
||||||
self.rbit = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +1,33 @@
|
|||||||
use crate::protocol::{registry, ClientPacket, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, registry, ClientPacket, KnownPack, VarInt};
|
||||||
|
|
||||||
pub struct CCookieRequest {
|
pub struct CCookieRequest {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CCookieRequest {
|
impl ClientPacket for CCookieRequest {
|
||||||
const PACKET_ID: crate::protocol::VarInt = 0;
|
const PACKET_ID: crate::protocol::VarInt = 0x00;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
fn write(&self, bytebuf: &mut ByteBuffer) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CPluginMessage<'a> {
|
||||||
|
channel: String,
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CPluginMessage<'a> {
|
||||||
|
pub fn new(channel: String, data: &'a [u8]) -> Self {
|
||||||
|
Self { channel, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClientPacket for CPluginMessage<'a> {
|
||||||
|
const PACKET_ID: VarInt = 0x01;
|
||||||
|
|
||||||
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
|
bytebuf.put_string(&self.channel);
|
||||||
|
bytebuf.put_slice(&self.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CConfigDisconnect {
|
pub struct CConfigDisconnect {
|
||||||
@ -21,10 +41,10 @@ impl CConfigDisconnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CConfigDisconnect {
|
impl ClientPacket for CConfigDisconnect {
|
||||||
const PACKET_ID: crate::protocol::VarInt = 2;
|
const PACKET_ID: crate::protocol::VarInt = 0x02;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_string(&self.reason);
|
bytebuf.put_string(&self.reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,37 +63,31 @@ impl CFinishConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CFinishConfig {
|
impl ClientPacket for CFinishConfig {
|
||||||
const PACKET_ID: crate::protocol::VarInt = 3;
|
const PACKET_ID: crate::protocol::VarInt = 0x03;
|
||||||
|
|
||||||
fn write(&self, _bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
fn write(&self, _bytebuf: &mut ByteBuffer) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CKnownPacks {
|
pub struct CKnownPacks<'a> {
|
||||||
count: VarInt,
|
count: VarInt,
|
||||||
known_packs: Vec<KnownPack>,
|
known_packs: &'a [KnownPack],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CKnownPacks {
|
impl<'a> CKnownPacks<'a> {
|
||||||
pub fn new(count: VarInt, known_packs: Vec<KnownPack>) -> Self {
|
pub fn new(count: VarInt, known_packs: &'a [KnownPack]) -> Self {
|
||||||
Self { count, known_packs }
|
Self { count, known_packs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KnownPack {
|
impl<'a> ClientPacket for CKnownPacks<'a> {
|
||||||
pub namespace: String,
|
|
||||||
pub id: String,
|
|
||||||
pub version: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientPacket for CKnownPacks {
|
|
||||||
const PACKET_ID: VarInt = 0x0E;
|
const PACKET_ID: VarInt = 0x0E;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_var_int(self.count);
|
// bytebuf.write_var_int(self.count);
|
||||||
bytebuf.write_list::<KnownPack>(&self.known_packs, |p, v| {
|
bytebuf.put_list::<KnownPack>(&self.known_packs, |p, v| {
|
||||||
p.write_string(&v.namespace);
|
p.put_string(&v.namespace);
|
||||||
p.write_string(&v.id);
|
p.put_string(&v.id);
|
||||||
p.write_string(&v.version);
|
p.put_string(&v.version);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,13 +117,13 @@ pub struct Entry {
|
|||||||
impl ClientPacket for CRegistryData {
|
impl ClientPacket for CRegistryData {
|
||||||
const PACKET_ID: VarInt = 0x07;
|
const PACKET_ID: VarInt = 0x07;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_string(&self.registry_id);
|
bytebuf.put_string(&self.registry_id);
|
||||||
bytebuf.write_var_int(self.entry_count);
|
bytebuf.put_var_int(self.entry_count);
|
||||||
bytebuf.write_list::<Entry>(&self.entries, |p, v| {
|
bytebuf.put_list::<Entry>(&self.entries, |p, v| {
|
||||||
p.write_string(&v.entry_id);
|
p.put_string(&v.entry_id);
|
||||||
p.write_bool(v.has_data);
|
p.put_bool(v.has_data);
|
||||||
registry::write_codec(p, -64, 320);
|
registry::write_single_dimension(p, -64, 320);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, ClientPacket, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt};
|
||||||
|
|
||||||
pub struct CLoginDisconnect {
|
pub struct CLoginDisconnect {
|
||||||
reason: String,
|
reason: String,
|
||||||
@ -11,10 +11,10 @@ impl CLoginDisconnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CLoginDisconnect {
|
impl ClientPacket for CLoginDisconnect {
|
||||||
const PACKET_ID: VarInt = 0;
|
const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_string(&serde_json::to_string_pretty(&self.reason).unwrap());
|
bytebuf.put_string(&serde_json::to_string_pretty(&self.reason).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,15 +48,15 @@ impl<'a> CEncryptionRequest<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ClientPacket for CEncryptionRequest<'a> {
|
impl<'a> ClientPacket for CEncryptionRequest<'a> {
|
||||||
const PACKET_ID: VarInt = 1;
|
const PACKET_ID: VarInt = 0x01;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_string_len(self.server_id.as_str(), 20);
|
bytebuf.put_string(self.server_id.as_str());
|
||||||
bytebuf.write_var_int(self.public_key_length);
|
bytebuf.put_var_int(self.public_key_length);
|
||||||
bytebuf.write_bytes(self.public_key);
|
bytebuf.put_slice(self.public_key);
|
||||||
bytebuf.write_var_int(self.verify_token_length);
|
bytebuf.put_var_int(self.verify_token_length);
|
||||||
bytebuf.write_bytes(self.verify_token);
|
bytebuf.put_slice(self.verify_token);
|
||||||
bytebuf.write_bool(self.should_authenticate);
|
bytebuf.put_bool(self.should_authenticate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,14 +96,14 @@ pub struct Property {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CLoginSuccess {
|
impl ClientPacket for CLoginSuccess {
|
||||||
const PACKET_ID: VarInt = 2;
|
const PACKET_ID: VarInt = 0x02;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_uuid(self.uuid);
|
bytebuf.put_uuid(self.uuid);
|
||||||
bytebuf.write_string(&self.username);
|
bytebuf.put_string(&self.username);
|
||||||
bytebuf.write_var_int(self.num_of_props);
|
bytebuf.put_var_int(self.num_of_props);
|
||||||
// Todo
|
// Todo
|
||||||
bytebuf.write_bool(self.strict_error_handling);
|
bytebuf.put_bool(self.strict_error_handling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +114,9 @@ impl CSetCompression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CSetCompression {
|
impl ClientPacket for CSetCompression {
|
||||||
const PACKET_ID: VarInt = 3;
|
const PACKET_ID: VarInt = 0x03;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_var_int(self.threshold);
|
bytebuf.put_var_int(self.threshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::player::GameMode,
|
entity::player::GameMode,
|
||||||
protocol::{ClientPacket, VarInt},
|
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SetHeldItem {
|
pub struct SetHeldItem {
|
||||||
@ -16,8 +16,8 @@ impl SetHeldItem {
|
|||||||
impl ClientPacket for SetHeldItem {
|
impl ClientPacket for SetHeldItem {
|
||||||
const PACKET_ID: VarInt = 0x53;
|
const PACKET_ID: VarInt = 0x53;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_i8(self.slot);
|
bytebuf.put_i8(self.slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,10 +40,10 @@ impl CPlayerAbilities {
|
|||||||
impl ClientPacket for CPlayerAbilities {
|
impl ClientPacket for CPlayerAbilities {
|
||||||
const PACKET_ID: VarInt = 0x38;
|
const PACKET_ID: VarInt = 0x38;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_i8(self.flags);
|
bytebuf.put_i8(self.flags);
|
||||||
bytebuf.write_f32(self.flying_speed);
|
bytebuf.put_f32(self.flying_speed);
|
||||||
bytebuf.write_f32(self.field_of_view);
|
bytebuf.put_f32(self.field_of_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,9 +61,9 @@ impl CChangeDifficulty {
|
|||||||
impl ClientPacket for CChangeDifficulty {
|
impl ClientPacket for CChangeDifficulty {
|
||||||
const PACKET_ID: VarInt = 0x0B;
|
const PACKET_ID: VarInt = 0x0B;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_u8(self.difficulty);
|
bytebuf.put_u8(self.difficulty);
|
||||||
bytebuf.write_bool(self.locked);
|
bytebuf.put_bool(self.locked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct CLogin {
|
pub struct CLogin {
|
||||||
@ -147,28 +147,28 @@ impl CLogin {
|
|||||||
impl ClientPacket for CLogin {
|
impl ClientPacket for CLogin {
|
||||||
const PACKET_ID: VarInt = 0x2B;
|
const PACKET_ID: VarInt = 0x2B;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_i32(self.entity_id);
|
bytebuf.put_i32(self.entity_id);
|
||||||
bytebuf.write_bool(self.is_hardcore);
|
bytebuf.put_bool(self.is_hardcore);
|
||||||
bytebuf.write_var_int(self.dimension_count);
|
bytebuf.put_var_int(self.dimension_count);
|
||||||
bytebuf.write_string_array(self.dimension_names.as_slice());
|
bytebuf.put_string_array(self.dimension_names.as_slice());
|
||||||
bytebuf.write_var_int(self.max_players);
|
bytebuf.put_var_int(self.max_players);
|
||||||
bytebuf.write_var_int(self.view_distance);
|
bytebuf.put_var_int(self.view_distance);
|
||||||
bytebuf.write_var_int(self.simulated_distance);
|
bytebuf.put_var_int(self.simulated_distance);
|
||||||
bytebuf.write_bool(self.reduced_debug_info);
|
bytebuf.put_bool(self.reduced_debug_info);
|
||||||
bytebuf.write_bool(self.enabled_respawn_screen);
|
bytebuf.put_bool(self.enabled_respawn_screen);
|
||||||
bytebuf.write_bool(self.limited_crafting);
|
bytebuf.put_bool(self.limited_crafting);
|
||||||
bytebuf.write_var_int(self.dimension_type);
|
bytebuf.put_var_int(self.dimension_type);
|
||||||
bytebuf.write_string(&self.dimension_name);
|
bytebuf.put_string(&self.dimension_name);
|
||||||
bytebuf.write_i64(self.hashed_seed);
|
bytebuf.put_i64(self.hashed_seed);
|
||||||
bytebuf.write_u8(self.game_mode.to_byte() as u8);
|
bytebuf.put_u8(self.game_mode.to_byte() as u8);
|
||||||
bytebuf.write_i8(self.previous_gamemode.to_byte());
|
bytebuf.put_i8(self.previous_gamemode.to_byte());
|
||||||
bytebuf.write_bool(self.debug);
|
bytebuf.put_bool(self.debug);
|
||||||
bytebuf.write_bool(self.is_flat);
|
bytebuf.put_bool(self.is_flat);
|
||||||
bytebuf.write_bool(self.has_death_loc);
|
bytebuf.put_bool(self.has_death_loc);
|
||||||
bytebuf.write_option(&self.death_dimension_name, |buf, v| buf.write_string(v));
|
bytebuf.put_option(&self.death_dimension_name, |buf, v| buf.put_string(v));
|
||||||
bytebuf.write_option(&self.death_loc, |buf, v| buf.write_string(v));
|
bytebuf.put_option(&self.death_loc, |buf, v| buf.put_string(v));
|
||||||
bytebuf.write_var_int(self.portal_cooldown);
|
bytebuf.put_var_int(self.portal_cooldown);
|
||||||
bytebuf.write_bool(self.enforce_secure_chat);
|
bytebuf.put_bool(self.enforce_secure_chat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, ClientPacket, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt};
|
||||||
|
|
||||||
pub struct CPingResponse {
|
pub struct CPingResponse {
|
||||||
payload: i64, // must responde with the same as in `SPingRequest`
|
payload: i64, // must responde with the same as in `SPingRequest`
|
||||||
@ -11,10 +11,10 @@ impl CPingResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CPingResponse {
|
impl ClientPacket for CPingResponse {
|
||||||
const PACKET_ID: VarInt = 1;
|
const PACKET_ID: VarInt = 0x01;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_i64(self.payload);
|
bytebuf.put_i64(self.payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,9 +29,9 @@ impl CStatusResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CStatusResponse {
|
impl ClientPacket for CStatusResponse {
|
||||||
const PACKET_ID: VarInt = 0;
|
const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||||
bytebuf.write_string(self.json_response.as_str());
|
bytebuf.put_string(self.json_response.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use bytebuf::buffer::ByteBuffer;
|
use bytebuf::ByteBuffer;
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod bytebuf;
|
pub mod bytebuf;
|
||||||
pub mod nbt;
|
|
||||||
mod registry;
|
mod registry;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
@ -113,7 +112,6 @@ impl ConnectionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RawPacket {
|
pub struct RawPacket {
|
||||||
pub len: VarInt,
|
pub len: VarInt,
|
||||||
pub id: VarInt,
|
pub id: VarInt,
|
||||||
@ -152,3 +150,9 @@ pub struct Sample {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub id: String, // uuid
|
pub id: String, // uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct KnownPack {
|
||||||
|
pub namespace: String,
|
||||||
|
pub id: String,
|
||||||
|
pub version: String,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,132 +0,0 @@
|
|||||||
use flate2::read::{GzDecoder, ZlibDecoder};
|
|
||||||
use std::{collections::HashMap, error::Error, fmt, io, io::Read, string::FromUtf8Error};
|
|
||||||
|
|
||||||
use crate::protocol::bytebuf::buffer::ByteBuffer;
|
|
||||||
|
|
||||||
use super::{nbt::ParseError, Tag, NBT};
|
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidType(ty) => write!(f, "invalid tag type: {ty}"),
|
|
||||||
Self::InvalidString(e) => write!(f, "invalid string: {e}"),
|
|
||||||
Self::IO(e) => write!(f, "io error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FromUtf8Error> for ParseError {
|
|
||||||
fn from(e: FromUtf8Error) -> ParseError {
|
|
||||||
ParseError::InvalidString(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<io::Error> for ParseError {
|
|
||||||
fn from(e: io::Error) -> ParseError {
|
|
||||||
ParseError::IO(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Error for ParseError {}
|
|
||||||
|
|
||||||
impl NBT {
|
|
||||||
pub fn deserialize_file(buf: Vec<u8>) -> Result<Self, ParseError> {
|
|
||||||
if buf.len() >= 2 && buf[0] == 0x1f && buf[1] == 0x8b {
|
|
||||||
// This means its gzipped
|
|
||||||
let mut d: GzDecoder<&[u8]> = GzDecoder::new(buf.as_ref());
|
|
||||||
let mut buf = vec![];
|
|
||||||
d.read_to_end(&mut buf)?;
|
|
||||||
Self::deserialize(buf)
|
|
||||||
} else {
|
|
||||||
// It could be zlib compressed or not compressed
|
|
||||||
let mut d: ZlibDecoder<&[u8]> = ZlibDecoder::new(buf.as_ref());
|
|
||||||
let mut decompressed = vec![];
|
|
||||||
match d.read_to_end(&mut decompressed) {
|
|
||||||
Ok(_) => Self::deserialize(decompressed),
|
|
||||||
Err(_) => Self::deserialize(buf),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Deserializes the given byte array as nbt data.
|
|
||||||
pub fn deserialize(mut buf: Vec<u8>) -> Result<Self, ParseError> {
|
|
||||||
Self::deserialize_buf(&mut ByteBuffer::from_vec(buf))
|
|
||||||
}
|
|
||||||
/// Deserializes the given buffer as nbt data. This will continue reading
|
|
||||||
/// where this buffer is currently placed, and will advance the reader to be
|
|
||||||
/// right after the nbt data. If this function returns an error, then the
|
|
||||||
/// buffer will be in an undefined state (it will still be safe, but there are
|
|
||||||
/// no guarantees as too how far ahead the buffer will have been advanced).
|
|
||||||
pub fn deserialize_buf(buf: &mut ByteBuffer) -> Result<Self, ParseError> {
|
|
||||||
let ty = buf.read_u8()?;
|
|
||||||
if ty == 0 {
|
|
||||||
Ok(NBT::empty())
|
|
||||||
} else {
|
|
||||||
let len = buf.read_u16()?;
|
|
||||||
let name = String::from_utf8(buf.read_bytes(len as usize)?)?;
|
|
||||||
Ok(NBT::new(&name, Tag::deserialize(ty, buf)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tag {
|
|
||||||
fn deserialize(ty: u8, buf: &mut ByteBuffer) -> Result<Self, ParseError> {
|
|
||||||
match ty {
|
|
||||||
0 => Ok(Self::End),
|
|
||||||
1 => Ok(Self::Byte(buf.read_i8()?)),
|
|
||||||
2 => Ok(Self::Short(buf.read_i16()?)),
|
|
||||||
3 => Ok(Self::Int(buf.read_i32()?)),
|
|
||||||
4 => Ok(Self::Long(buf.read_i64()?)),
|
|
||||||
5 => Ok(Self::Float(buf.read_f32()?)),
|
|
||||||
6 => Ok(Self::Double(buf.read_f64()?)),
|
|
||||||
7 => {
|
|
||||||
let len = buf.read_i32()?;
|
|
||||||
Ok(Self::ByteArr(buf.read_bytes(len as usize)?))
|
|
||||||
}
|
|
||||||
8 => {
|
|
||||||
let len = buf.read_u16()?;
|
|
||||||
match String::from_utf8(buf.read_bytes(len as usize)?) {
|
|
||||||
Ok(v) => Ok(Self::String(v)),
|
|
||||||
Err(e) => Err(ParseError::InvalidString(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9 => {
|
|
||||||
let inner_ty = buf.read_u8()?;
|
|
||||||
let len = buf.read_i32()?;
|
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
|
||||||
for _ in 0..len {
|
|
||||||
inner.push(Tag::deserialize(inner_ty, buf)?);
|
|
||||||
}
|
|
||||||
Ok(Self::List(inner))
|
|
||||||
}
|
|
||||||
10 => {
|
|
||||||
let mut inner = HashMap::new();
|
|
||||||
loop {
|
|
||||||
let ty = buf.read_u8()?;
|
|
||||||
if ty == Self::End.ty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let len = buf.read_u16()?;
|
|
||||||
let name = String::from_utf8(buf.read_bytes(len as usize)?).unwrap();
|
|
||||||
let tag = Tag::deserialize(ty, buf)?;
|
|
||||||
inner.insert(name, tag);
|
|
||||||
}
|
|
||||||
Ok(inner.into())
|
|
||||||
}
|
|
||||||
11 => {
|
|
||||||
let len = buf.read_i32()?;
|
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
|
||||||
for _ in 0..len {
|
|
||||||
inner.push(buf.read_i32()?);
|
|
||||||
}
|
|
||||||
Ok(Self::IntArray(inner))
|
|
||||||
}
|
|
||||||
12 => {
|
|
||||||
let len = buf.read_i32()?;
|
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
|
||||||
for _ in 0..len {
|
|
||||||
inner.push(buf.read_i64()?);
|
|
||||||
}
|
|
||||||
Ok(Self::LongArray(inner))
|
|
||||||
}
|
|
||||||
_ => Err(ParseError::InvalidType(ty)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
use super::Tag;
|
|
||||||
use serde::{de, ser};
|
|
||||||
use std::{fmt, fmt::Display, num::TryFromIntError};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
Message(String),
|
|
||||||
Eof,
|
|
||||||
|
|
||||||
TryFromInt(TryFromIntError),
|
|
||||||
ListType(Tag, Tag),
|
|
||||||
MapKey(Tag),
|
|
||||||
CannotSerializeNone,
|
|
||||||
Enum,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
impl ser::Error for Error {
|
|
||||||
fn custom<T: Display>(msg: T) -> Self {
|
|
||||||
Error::Message(msg.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl de::Error for Error {
|
|
||||||
fn custom<T: Display>(msg: T) -> Self {
|
|
||||||
Error::Message(msg.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::Message(msg) => write!(f, "{msg}"),
|
|
||||||
Error::Eof => write!(f, "unexpected end of input"),
|
|
||||||
Error::TryFromInt(e) => write!(f, "invalid integer: {e}"),
|
|
||||||
Error::ListType(expected, got) => {
|
|
||||||
write!(f, "expected type in list: {expected:?}, got: {got:?}")
|
|
||||||
}
|
|
||||||
Error::MapKey(got) => {
|
|
||||||
write!(f, "expected a string for map key, got: {got:?}")
|
|
||||||
}
|
|
||||||
Error::CannotSerializeNone => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"cannot serialize `None` or `()` (use `#[serde(skip_serializing_if = \"Option::is_none\")]`)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Error::Enum => {
|
|
||||||
write!(f, "enums are not supported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
@ -1,445 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
use error::Result;
|
|
||||||
use nbt::Tag;
|
|
||||||
use nbt::NBT;
|
|
||||||
use serde::{ser, Serialize};
|
|
||||||
|
|
||||||
mod deserialize;
|
|
||||||
mod error;
|
|
||||||
pub mod nbt;
|
|
||||||
mod serialize;
|
|
||||||
|
|
||||||
pub fn to_nbt<T>(name: &str, value: &T) -> anyhow::Result<NBT>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
Ok(NBT::new(name, to_tag(value)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_tag<T>(value: &T) -> anyhow::Result<Tag>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
let mut serializer = Serializer { tag: Tag::End };
|
|
||||||
value.serialize(&mut serializer)?;
|
|
||||||
Ok(serializer.tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Serializer {
|
|
||||||
tag: Tag,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SeqSerializer<'a> {
|
|
||||||
ser: &'a mut Serializer,
|
|
||||||
items: Vec<Tag>,
|
|
||||||
}
|
|
||||||
pub struct MapSerializer<'a> {
|
|
||||||
ser: &'a mut Serializer,
|
|
||||||
key: Option<String>,
|
|
||||||
items: HashMap<String, Tag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ser::Serializer for &'a mut Serializer {
|
|
||||||
// The output type produced by this `Serializer` during successful
|
|
||||||
// serialization. Most serializers that produce text or binary output should
|
|
||||||
// set `Ok = ()` and serialize into an `io::Write` or buffer contained
|
|
||||||
// within the `Serializer` instance, as happens here. Serializers that build
|
|
||||||
// in-memory data structures may be simplified by using `Ok` to propagate
|
|
||||||
// the data structure around.
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
// Associated types for keeping track of additional state while serializing
|
|
||||||
// compound data structures like sequences and maps. In this case no
|
|
||||||
// additional state is required beyond what is already stored in the
|
|
||||||
// Serializer struct.
|
|
||||||
type SerializeSeq = SeqSerializer<'a>;
|
|
||||||
type SerializeTuple = SeqSerializer<'a>;
|
|
||||||
type SerializeTupleStruct = SeqSerializer<'a>;
|
|
||||||
type SerializeTupleVariant = SeqSerializer<'a>;
|
|
||||||
type SerializeMap = MapSerializer<'a>;
|
|
||||||
type SerializeStruct = MapSerializer<'a>;
|
|
||||||
type SerializeStructVariant = MapSerializer<'a>;
|
|
||||||
|
|
||||||
// Here we go with the simple methods. The following 12 methods receive one
|
|
||||||
// of the primitive types of the data model and map it to JSON by appending
|
|
||||||
// into the output string.
|
|
||||||
fn serialize_bool(self, v: bool) -> Result<()> {
|
|
||||||
self.tag = Tag::Byte(v as i8);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON does not distinguish between different sizes of integers, so all
|
|
||||||
// signed integers will be serialized the same and all unsigned integers
|
|
||||||
// will be serialized the same. Other formats, especially compact binary
|
|
||||||
// formats, may need independent logic for the different sizes.
|
|
||||||
fn serialize_i8(self, v: i8) -> Result<()> {
|
|
||||||
self.tag = Tag::Byte(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_i16(self, v: i16) -> Result<()> {
|
|
||||||
self.tag = Tag::Short(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_i32(self, v: i32) -> Result<()> {
|
|
||||||
self.tag = Tag::Int(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_i64(self, v: i64) -> Result<()> {
|
|
||||||
self.tag = Tag::Long(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_u8(self, v: u8) -> Result<()> {
|
|
||||||
self.serialize_i8(v as i8)
|
|
||||||
}
|
|
||||||
fn serialize_u16(self, v: u16) -> Result<()> {
|
|
||||||
self.serialize_i16(v as i16)
|
|
||||||
}
|
|
||||||
fn serialize_u32(self, v: u32) -> Result<()> {
|
|
||||||
self.serialize_i32(v as i32)
|
|
||||||
}
|
|
||||||
fn serialize_u64(self, v: u64) -> Result<()> {
|
|
||||||
self.serialize_i64(v as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_f32(self, v: f32) -> Result<()> {
|
|
||||||
self.tag = Tag::Float(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_f64(self, v: f64) -> Result<()> {
|
|
||||||
self.tag = Tag::Double(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_char(self, v: char) -> Result<()> {
|
|
||||||
self.serialize_str(&v.to_string())
|
|
||||||
}
|
|
||||||
fn serialize_str(self, v: &str) -> Result<()> {
|
|
||||||
self.tag = Tag::String(v.into());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
|
|
||||||
self.tag = Tag::ByteArr(v.into());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// There isn't really a `None` in NBT. There is `Tag::End`, which could be
|
|
||||||
// searched and removed if this was in a struct or map, which would essentially
|
|
||||||
// skip this value if it's `None`. However, I don't care enough, so I'm just
|
|
||||||
// going to produce an error.
|
|
||||||
fn serialize_none(self) -> Result<()> {
|
|
||||||
Err(Error::CannotSerializeNone)
|
|
||||||
}
|
|
||||||
fn serialize_some<T>(self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit(self) -> Result<()> {
|
|
||||||
Err(Error::CannotSerializeNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unit struct means a named value containing no data. Again, since there is
|
|
||||||
// no data, map this to JSON as `null`. There is no need to serialize the
|
|
||||||
// name in most formats.
|
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
|
|
||||||
self.serialize_unit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// As is done here, serializers are encouraged to treat newtype structs as
|
|
||||||
// insignificant wrappers around the data they contain.
|
|
||||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we get to the serialization of compound types.
|
|
||||||
//
|
|
||||||
// The start of the sequence, each value, and the end are three separate
|
|
||||||
// method calls. This one is responsible only for serializing the start,
|
|
||||||
// which in JSON is `[`.
|
|
||||||
//
|
|
||||||
// The length of the sequence may or may not be known ahead of time. This
|
|
||||||
// doesn't make a difference in JSON because the length is not represented
|
|
||||||
// explicitly in the serialized form. Some serializers may only be able to
|
|
||||||
// support sequences for which the length is known up front.
|
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
|
||||||
Ok(SeqSerializer {
|
|
||||||
ser: self,
|
|
||||||
items: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tuples look just like sequences in JSON. Some formats may be able to
|
|
||||||
// represent tuples more efficiently by omitting the length, since tuple
|
|
||||||
// means that the corresponding `Deserialize implementation will know the
|
|
||||||
// length without needing to look at the serialized data.
|
|
||||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
|
|
||||||
self.serialize_seq(Some(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tuple structs look just like sequences in JSON.
|
|
||||||
fn serialize_tuple_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleStruct> {
|
|
||||||
self.serialize_seq(Some(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps are represented in JSON as `{ K: V, K: V, ... }`.
|
|
||||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
|
||||||
Ok(MapSerializer {
|
|
||||||
ser: self,
|
|
||||||
key: None,
|
|
||||||
items: HashMap::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Structs look just like maps in JSON. In particular, JSON requires that we
|
|
||||||
// serialize the field names of the struct. Other formats may be able to
|
|
||||||
// omit the field names when serializing structs because the corresponding
|
|
||||||
// Deserialize implementation is required to know what the keys are without
|
|
||||||
// looking at the serialized data.
|
|
||||||
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
|
||||||
self.serialize_map(Some(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't support enums (they don't really make sense)
|
|
||||||
fn serialize_unit_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
) -> Result<()> {
|
|
||||||
Err(Error::Enum)
|
|
||||||
}
|
|
||||||
fn serialize_newtype_variant<T>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_value: &T,
|
|
||||||
) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
Err(Error::Enum)
|
|
||||||
}
|
|
||||||
fn serialize_tuple_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleVariant> {
|
|
||||||
Err(Error::Enum)
|
|
||||||
}
|
|
||||||
fn serialize_struct_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStructVariant> {
|
|
||||||
Err(Error::Enum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following 7 impls deal with the serialization of compound types like
|
|
||||||
// sequences and maps. Serialization of such types is begun by a Serializer
|
|
||||||
// method and followed by zero or more calls to serialize individual elements of
|
|
||||||
// the compound type and one call to end the compound type.
|
|
||||||
//
|
|
||||||
// This impl is SerializeSeq so these methods are called after `serialize_seq`
|
|
||||||
// is called on the Serializer.
|
|
||||||
impl<'a> ser::SerializeSeq for SeqSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
// Serialize a single element of the sequence.
|
|
||||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(&mut *self.ser)?;
|
|
||||||
let tag = std::mem::replace(&mut self.ser.tag, Tag::End);
|
|
||||||
if let Some(first) = self.items.first() {
|
|
||||||
let expected_ty = first.ty();
|
|
||||||
let actual_ty = tag.ty();
|
|
||||||
if expected_ty != actual_ty {
|
|
||||||
return Err(Error::ListType(first.clone(), tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.items.push(tag);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
self.ser.tag = Tag::List(self.items);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same thing but for tuples.
|
|
||||||
impl<'a> ser::SerializeTuple for SeqSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
<Self as ser::SerializeSeq>::serialize_element(self, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
<Self as ser::SerializeSeq>::end(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same thing but for tuple structs.
|
|
||||||
impl<'a> ser::SerializeTupleStruct for SeqSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
<Self as ser::SerializeSeq>::serialize_element(self, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
<Self as ser::SerializeSeq>::end(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tuple variants are a little different. Refer back to the
|
|
||||||
// `serialize_tuple_variant` method above:
|
|
||||||
//
|
|
||||||
// self.output += "{";
|
|
||||||
// variant.serialize(&mut *self)?;
|
|
||||||
// self.output += ":[";
|
|
||||||
//
|
|
||||||
// So the `end` method in this impl is responsible for closing both the `]` and
|
|
||||||
// the `}`.
|
|
||||||
impl<'a> ser::SerializeTupleVariant for SeqSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
<Self as ser::SerializeSeq>::serialize_element(self, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
<Self as ser::SerializeSeq>::end(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some `Serialize` types are not able to hold a key and value in memory at the
|
|
||||||
// same time so `SerializeMap` implementations are required to support
|
|
||||||
// `serialize_key` and `serialize_value` individually.
|
|
||||||
//
|
|
||||||
// There is a third optional method on the `SerializeMap` trait. The
|
|
||||||
// `serialize_entry` method allows serializers to optimize for the case where
|
|
||||||
// key and value are both available simultaneously. In JSON it doesn't make a
|
|
||||||
// difference so the default behavior for `serialize_entry` is fine.
|
|
||||||
impl<'a> ser::SerializeMap for MapSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
// The Serde data model allows map keys to be any serializable type. JSON
|
|
||||||
// only allows string keys so the implementation below will produce invalid
|
|
||||||
// JSON if the key serializes as something other than a string.
|
|
||||||
//
|
|
||||||
// A real JSON serializer would need to validate that map keys are strings.
|
|
||||||
// This can be done by using a different Serializer to serialize the key
|
|
||||||
// (instead of `&mut **self`) and having that other serializer only
|
|
||||||
// implement `serialize_str` and return an error on any other data type.
|
|
||||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
key.serialize(&mut *self.ser)?;
|
|
||||||
let tag = std::mem::replace(&mut self.ser.tag, Tag::End);
|
|
||||||
self.key = match tag {
|
|
||||||
Tag::String(key) => Some(key),
|
|
||||||
other => return Err(Error::MapKey(other)),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// It doesn't make a difference whether the colon is printed at the end of
|
|
||||||
// `serialize_key` or at the beginning of `serialize_value`. In this case
|
|
||||||
// the code is a bit simpler having it here.
|
|
||||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(&mut *self.ser)?;
|
|
||||||
let tag = std::mem::replace(&mut self.ser.tag, Tag::End);
|
|
||||||
self.items.insert(self.key.take().unwrap(), tag);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
self.ser.tag = self.items.into();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Structs are like maps in which the keys are constrained to be compile-time
|
|
||||||
// constant strings.
|
|
||||||
impl<'a> ser::SerializeStruct for MapSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(&mut *self.ser)?;
|
|
||||||
let tag = std::mem::replace(&mut self.ser.tag, Tag::End);
|
|
||||||
self.items.insert(key.into(), tag);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
self.ser.tag = self.items.into();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
|
|
||||||
// closing both of the curly braces opened by `serialize_struct_variant`.
|
|
||||||
impl<'a> ser::SerializeStructVariant for MapSerializer<'a> {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
value.serialize(&mut *self.ser)?;
|
|
||||||
let tag = std::mem::replace(&mut self.ser.tag, Tag::End);
|
|
||||||
self.items.insert(key.into(), tag);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<()> {
|
|
||||||
self.ser.tag = self.items.into();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,283 +0,0 @@
|
|||||||
use std::{collections::HashMap, fmt, io, ops::Index, string::FromUtf8Error};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ParseError {
|
|
||||||
InvalidType(u8),
|
|
||||||
InvalidString(FromUtf8Error),
|
|
||||||
IO(io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct WrongTag(Tag);
|
|
||||||
|
|
||||||
impl fmt::Display for WrongTag {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "wrong tag: {:?}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for WrongTag {}
|
|
||||||
|
|
||||||
/// This is an nbt tag. It has a name, and any amount of data. This can be used
|
|
||||||
/// to store item data, entity data, level data, and more.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct NBT {
|
|
||||||
pub tag: Tag,
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for NBT {
|
|
||||||
fn default() -> Self {
|
|
||||||
NBT::new("", Tag::new_compound(&[]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is a single tag. It does not contain a name, but has the actual data
|
|
||||||
/// for any of the nbt tags.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Tag {
|
|
||||||
End,
|
|
||||||
Byte(i8),
|
|
||||||
Short(i16),
|
|
||||||
Int(i32),
|
|
||||||
Long(i64),
|
|
||||||
Float(f32),
|
|
||||||
Double(f64),
|
|
||||||
ByteArr(Vec<u8>),
|
|
||||||
String(String),
|
|
||||||
List(Vec<Tag>), // All elements must be the same type, and un-named.
|
|
||||||
Compound(Compound), // Types can be any kind, and are named. Order is not defined.
|
|
||||||
IntArray(Vec<i32>),
|
|
||||||
LongArray(Vec<i64>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An NBT Compound tag. This is essentially a map, with some extra helper
|
|
||||||
/// functions.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Compound {
|
|
||||||
pub inner: HashMap<String, Tag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Compound {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Compound {
|
|
||||||
inner: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Tag>) {
|
|
||||||
self.inner.insert(key.into(), value.into());
|
|
||||||
}
|
|
||||||
pub fn get_or_create_compound(&mut self, key: impl Into<String>) -> &mut Compound {
|
|
||||||
self.inner
|
|
||||||
.entry(key.into())
|
|
||||||
.or_insert_with(|| Tag::Compound(Compound::new()))
|
|
||||||
.compound_mut()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_key(&self, key: impl AsRef<str>) -> bool {
|
|
||||||
self.inner.contains_key(key.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> std::collections::hash_map::Iter<String, Tag> {
|
|
||||||
self.inner.iter()
|
|
||||||
}
|
|
||||||
pub fn iter_mut(&mut self) -> std::collections::hash_map::IterMut<String, Tag> {
|
|
||||||
self.inner.iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl IntoIterator for Compound {
|
|
||||||
type Item = (String, Tag);
|
|
||||||
type IntoIter = std::collections::hash_map::IntoIter<String, Tag>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.inner.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> IntoIterator for &'a Compound {
|
|
||||||
type Item = (&'a String, &'a Tag);
|
|
||||||
type IntoIter = std::collections::hash_map::Iter<'a, String, Tag>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.inner.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> IntoIterator for &'a mut Compound {
|
|
||||||
type Item = (&'a String, &'a mut Tag);
|
|
||||||
type IntoIter = std::collections::hash_map::IterMut<'a, String, Tag>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.inner.iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<HashMap<String, Tag>> for Compound {
|
|
||||||
fn from(v: HashMap<String, Tag>) -> Self {
|
|
||||||
Compound { inner: v }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<&str> for Compound {
|
|
||||||
type Output = Tag;
|
|
||||||
fn index(&self, index: &str) -> &Tag {
|
|
||||||
&self.inner[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for Tag {
|
|
||||||
fn from(v: bool) -> Self {
|
|
||||||
Tag::Byte(v as i8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<&str> for Tag {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Tag::String(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<String> for Tag {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Tag::String(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<HashMap<String, Tag>> for Tag {
|
|
||||||
fn from(v: HashMap<String, Tag>) -> Self {
|
|
||||||
Tag::Compound(Compound::from(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<Vec<T>> for Tag
|
|
||||||
where
|
|
||||||
Tag: From<T>,
|
|
||||||
{
|
|
||||||
fn from(list: Vec<T>) -> Self {
|
|
||||||
Tag::List(list.into_iter().map(|it| it.into()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NBT {
|
|
||||||
/// Creates a new nbt tag. The tag value can be anything.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// This will panic if the tag is a list, and the values within that list
|
|
||||||
/// contain multiple types. This is a limitation with the nbt data format:
|
|
||||||
/// lists can only contain one type of data.
|
|
||||||
pub fn new(name: &str, tag: Tag) -> Self {
|
|
||||||
if let Tag::List(inner) = &tag {
|
|
||||||
if let Some(v) = inner.get(0) {
|
|
||||||
let ty = v.ty();
|
|
||||||
for v in inner {
|
|
||||||
if v.ty() != ty {
|
|
||||||
panic!("the given list contains multiple types: {inner:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NBT {
|
|
||||||
tag,
|
|
||||||
name: name.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an empty nbt tag.
|
|
||||||
pub const fn empty() -> Self {
|
|
||||||
NBT {
|
|
||||||
tag: Tag::End,
|
|
||||||
name: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends the given element to the list. This will panic if self is not a
|
|
||||||
/// list, or if tag does not match the type of the existing elements.
|
|
||||||
pub fn list_add(&mut self, tag: Tag) {
|
|
||||||
if let Tag::List(inner) = &mut self.tag {
|
|
||||||
if let Some(v) = inner.get(0) {
|
|
||||||
if tag.ty() != v.ty() {
|
|
||||||
panic!("cannot add different types to list. current: {inner:?}, new: {tag:?}");
|
|
||||||
} else {
|
|
||||||
inner.push(tag);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No elements yet, so we add this no matter what type it is.
|
|
||||||
inner.push(tag);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("called list_add on non-list type: {self:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends the given element to the compound. This will panic if self is not
|
|
||||||
/// a compound tag.
|
|
||||||
pub fn compound_add(&mut self, name: String, value: Tag) {
|
|
||||||
if let Tag::Compound(inner) = &mut self.tag {
|
|
||||||
inner.insert(name, value);
|
|
||||||
} else {
|
|
||||||
panic!("called compound_add on non-compound type: {self:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this is a compound tag, this returns the inner data of the tag.
|
|
||||||
/// Otherwise, this panics.
|
|
||||||
pub fn compound(&self) -> Option<&Compound> {
|
|
||||||
if let Tag::Compound(inner) = &self.tag {
|
|
||||||
Some(inner)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// If this is a compound tag, this returns the inner data of the tag.
|
|
||||||
/// Otherwise, this panics.
|
|
||||||
pub fn compound_mut(&mut self) -> Option<&mut Compound> {
|
|
||||||
if let Tag::Compound(inner) = &mut self.tag {
|
|
||||||
Some(inner)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tag(&self) -> &Tag {
|
|
||||||
&self.tag
|
|
||||||
}
|
|
||||||
pub fn into_tag(self) -> Tag {
|
|
||||||
self.tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! getter {
|
|
||||||
( $(: $conv:tt)? $name:ident -> $variant:ident ( $ty:ty ) ) => {
|
|
||||||
pub fn $name(&self) -> Result<$ty, WrongTag> {
|
|
||||||
match self {
|
|
||||||
Self::$variant(v) => Ok($($conv)? v),
|
|
||||||
_ => Err(WrongTag(self.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tag {
|
|
||||||
/// A simpler way to construct compound tags inline.
|
|
||||||
pub fn new_compound(value: &[(&str, Tag)]) -> Self {
|
|
||||||
let mut inner = HashMap::new();
|
|
||||||
for (name, tag) in value {
|
|
||||||
inner.insert(name.to_string(), tag.clone());
|
|
||||||
}
|
|
||||||
inner.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
getter!(:*byte -> Byte(i8));
|
|
||||||
getter!(:*short -> Short(i16));
|
|
||||||
getter!(:*int -> Int(i32));
|
|
||||||
getter!(:*long -> Long(i64));
|
|
||||||
getter!(:*float -> Float(f32));
|
|
||||||
getter!(:*double -> Double(f64));
|
|
||||||
getter!(string -> String(&str));
|
|
||||||
getter!(byte_arr -> ByteArr(&[u8]));
|
|
||||||
getter!(list -> List(&Vec<Tag>));
|
|
||||||
getter!(compound -> Compound(&Compound));
|
|
||||||
getter!(long_arr -> LongArray(&Vec<i64>));
|
|
||||||
|
|
||||||
pub fn compound_mut(&mut self) -> Result<&mut Compound, WrongTag> {
|
|
||||||
match self {
|
|
||||||
Self::Compound(v) => Ok(v),
|
|
||||||
_ => Err(WrongTag(self.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
use crate::protocol::bytebuf::buffer::ByteBuffer;
|
|
||||||
|
|
||||||
use super::{Tag, NBT};
|
|
||||||
|
|
||||||
impl NBT {
|
|
||||||
pub fn serialize_buf(&self, out: &mut ByteBuffer) {
|
|
||||||
out.write_u8(self.tag.ty());
|
|
||||||
if matches!(self.tag, Tag::End) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out.write_u16(self.name.len() as u16);
|
|
||||||
out.write_bytes(self.name.as_bytes());
|
|
||||||
self.tag.serialize(out);
|
|
||||||
}
|
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
|
||||||
let mut out = ByteBuffer::new();
|
|
||||||
self.serialize_buf(&mut out);
|
|
||||||
out.into_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tag {
|
|
||||||
/// Returns the type of the tag.
|
|
||||||
pub fn ty(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::End => 0,
|
|
||||||
Self::Byte(_) => 1,
|
|
||||||
Self::Short(_) => 2,
|
|
||||||
Self::Int(_) => 3,
|
|
||||||
Self::Long(_) => 4,
|
|
||||||
Self::Float(_) => 5,
|
|
||||||
Self::Double(_) => 6,
|
|
||||||
Self::ByteArr(_) => 7,
|
|
||||||
Self::String(_) => 8,
|
|
||||||
Self::List(_) => 9,
|
|
||||||
Self::Compound(_) => 10,
|
|
||||||
Self::IntArray(_) => 11,
|
|
||||||
Self::LongArray(_) => 12,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serializes the data of the tag. Does not add type byte.
|
|
||||||
fn serialize(&self, out: &mut ByteBuffer) {
|
|
||||||
match self {
|
|
||||||
Self::End => (),
|
|
||||||
Self::Byte(v) => out.write_i8(*v),
|
|
||||||
Self::Short(v) => out.write_i16(*v),
|
|
||||||
Self::Int(v) => out.write_i32(*v),
|
|
||||||
Self::Long(v) => out.write_i64(*v),
|
|
||||||
Self::Float(v) => out.write_f32(*v),
|
|
||||||
Self::Double(v) => out.write_f64(*v),
|
|
||||||
Self::ByteArr(v) => {
|
|
||||||
out.write_i32(v.len() as i32);
|
|
||||||
out.write_bytes(v);
|
|
||||||
}
|
|
||||||
Self::String(v) => {
|
|
||||||
out.write_u16(v.len() as u16);
|
|
||||||
out.write_bytes(v.as_bytes());
|
|
||||||
}
|
|
||||||
Self::List(v) => {
|
|
||||||
out.write_u8(v.get(0).unwrap_or(&Self::End).ty());
|
|
||||||
out.write_i32(v.len() as i32);
|
|
||||||
for tag in v {
|
|
||||||
tag.serialize(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Compound(v) => {
|
|
||||||
for (name, tag) in &v.inner {
|
|
||||||
// Each element in the HashMap is essentially a NBT, but we store it in a
|
|
||||||
// separated form, so we have a manual implementation of serialize() here.
|
|
||||||
out.write_u8(tag.ty());
|
|
||||||
if tag.ty() == Self::End.ty() {
|
|
||||||
// End tags don't have a name, so we stop early.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out.write_u16(name.len() as u16);
|
|
||||||
out.write_bytes(name.as_bytes());
|
|
||||||
tag.serialize(out);
|
|
||||||
}
|
|
||||||
out.write_u8(Self::End.ty());
|
|
||||||
}
|
|
||||||
Self::IntArray(v) => {
|
|
||||||
out.write_i32(v.len() as i32);
|
|
||||||
for elem in v {
|
|
||||||
out.write_i32(*elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::LongArray(v) => {
|
|
||||||
out.write_i32(v.len() as i32);
|
|
||||||
for elem in v {
|
|
||||||
out.write_i64(*elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +1,54 @@
|
|||||||
|
use crate::protocol::VarInt;
|
||||||
|
|
||||||
use super::CodecItem;
|
use super::CodecItem;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct Biome {
|
pub struct Biome {
|
||||||
category: String,
|
has_precipitation: bool,
|
||||||
depth: f32,
|
temperature: f32,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
temperature_modifier: Option<String>,
|
||||||
downfall: f32,
|
downfall: f32,
|
||||||
effects: BiomeEffects,
|
effects: BiomeEffects,
|
||||||
precipitation: String,
|
|
||||||
scale: f32,
|
|
||||||
temperature: f32,
|
|
||||||
has_precipitation: bool,
|
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct BiomeEffects {
|
struct BiomeEffects {
|
||||||
sky_color: i32,
|
|
||||||
fog_color: i32,
|
fog_color: i32,
|
||||||
water_fog_color: i32,
|
|
||||||
water_color: i32,
|
water_color: i32,
|
||||||
|
water_fog_color: i32,
|
||||||
|
sky_color: i32,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
foliage_color: Option<i32>,
|
foliage_color: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
grass_color: Option<i32>,
|
grass_color: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
mood_sound: Option<MoodSound>, // 1.18.2+
|
grass_color_modifier: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
particle: Option<Particle>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
ambient_sound: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
mood_sound: Option<MoodSound>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
additions_sound: Option<AdditionsSound>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
music: Option<Music>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct Particle {
|
||||||
|
options: ParticleOptions,
|
||||||
|
probability: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct ParticleOptions {
|
||||||
|
typee: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
value: Option<VarInt>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct MoodSound {
|
struct MoodSound {
|
||||||
block_search_extent: i32,
|
block_search_extent: i32,
|
||||||
@ -33,35 +57,45 @@ struct MoodSound {
|
|||||||
tick_delay: i32,
|
tick_delay: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct AdditionsSound {
|
||||||
|
sound: String,
|
||||||
|
tick_chance: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct Music {
|
||||||
|
sound: String,
|
||||||
|
min_delay: i32,
|
||||||
|
max_delay: i32,
|
||||||
|
replace_current_music: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.20.6 default https://gist.github.com/WinX64/ab8c7a8df797c273b32d3a3b66522906
|
||||||
pub(super) fn all() -> Vec<CodecItem<Biome>> {
|
pub(super) fn all() -> Vec<CodecItem<Biome>> {
|
||||||
let biome = Biome {
|
let biome = Biome {
|
||||||
precipitation: "rain".into(),
|
has_precipitation: false,
|
||||||
depth: 1.0,
|
|
||||||
temperature: 1.0,
|
temperature: 1.0,
|
||||||
scale: 1.0,
|
temperature_modifier: None,
|
||||||
downfall: 1.0,
|
downfall: 0.0,
|
||||||
category: "none".into(),
|
|
||||||
has_precipitation: true,
|
|
||||||
effects: BiomeEffects {
|
effects: BiomeEffects {
|
||||||
sky_color: 0x78a7ff,
|
fog_color: 0x7FA1FF,
|
||||||
fog_color: 0xc0d8ff,
|
water_color: 0x7FA1FF,
|
||||||
water_fog_color: 0x050533,
|
water_fog_color: 0x7FA1FF,
|
||||||
water_color: 0x3f76e4,
|
sky_color: 0x7FA1FF,
|
||||||
foliage_color: None,
|
foliage_color: Some(0x7FA1FF),
|
||||||
grass_color: None,
|
grass_color: Some(0x7FA1FF),
|
||||||
|
grass_color_modifier: None,
|
||||||
|
particle: None,
|
||||||
|
ambient_sound: None,
|
||||||
mood_sound: Some(MoodSound {
|
mood_sound: Some(MoodSound {
|
||||||
block_search_extent: 8,
|
block_search_extent: 8,
|
||||||
offset: 2.0,
|
offset: 2.0,
|
||||||
sound: "minecraft:ambient.cave".into(),
|
sound: "minecraft:ambient.cave".into(),
|
||||||
tick_delay: 6000,
|
tick_delay: 6000,
|
||||||
}),
|
}),
|
||||||
// sky_color: 0xff00ff,
|
additions_sound: None,
|
||||||
// water_color: 0xff00ff,
|
music: None,
|
||||||
// fog_color: 0xff00ff,
|
|
||||||
// water_fog_color: 0xff00ff,
|
|
||||||
// grass_color: 0xff00ff,
|
|
||||||
// foliage_color: 0x00ffe5,
|
|
||||||
// grass_color: 0xff5900,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,14 @@ use serde::Serialize;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct ChatType {
|
pub struct ChatType {
|
||||||
chat: ChatParams,
|
chat: Decoration,
|
||||||
narration: ChatParams,
|
narration: Decoration,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct ChatParams {
|
struct Decoration {
|
||||||
parameters: Vec<String>,
|
|
||||||
translation_key: String,
|
translation_key: String,
|
||||||
|
// style: Option<>
|
||||||
|
parameters: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
||||||
@ -17,11 +18,11 @@ pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
|||||||
name: "minecraft:chat".into(),
|
name: "minecraft:chat".into(),
|
||||||
id: 0,
|
id: 0,
|
||||||
element: ChatType {
|
element: ChatType {
|
||||||
chat: ChatParams {
|
chat: Decoration {
|
||||||
parameters: vec!["sender".into(), "content".into()],
|
parameters: vec!["sender".into(), "content".into()],
|
||||||
translation_key: "chat.type.text".into(),
|
translation_key: "chat.type.text".into(),
|
||||||
},
|
},
|
||||||
narration: ChatParams {
|
narration: Decoration {
|
||||||
parameters: vec!["sender".into(), "content".into()],
|
parameters: vec!["sender".into(), "content".into()],
|
||||||
translation_key: "chat.type.text.narrate".into(),
|
translation_key: "chat.type.text.narrate".into(),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,38 +3,61 @@ use serde::Serialize;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct DamageType {
|
pub struct DamageType {
|
||||||
exhaustion: f32,
|
|
||||||
message_id: String,
|
message_id: String,
|
||||||
scaling: String,
|
scaling: String,
|
||||||
|
exhaustion: f32,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
effects: Option<String>,
|
effects: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
death_message_type: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const NAMES: &[&str] = &[
|
const NAMES: &[&str] = &[
|
||||||
"in_fire",
|
"arrow",
|
||||||
"lightning_bolt",
|
"bad_respawn_point",
|
||||||
"on_fire",
|
|
||||||
"lava",
|
|
||||||
"hot_floor",
|
|
||||||
"in_wall",
|
|
||||||
"cramming",
|
|
||||||
"drown",
|
|
||||||
"starve",
|
|
||||||
"cactus",
|
"cactus",
|
||||||
"fall",
|
"cramming",
|
||||||
"fly_into_wall",
|
|
||||||
"out_of_world",
|
|
||||||
"generic",
|
|
||||||
"magic",
|
|
||||||
"wither",
|
|
||||||
"dragon_breath",
|
"dragon_breath",
|
||||||
|
"drown",
|
||||||
"dry_out",
|
"dry_out",
|
||||||
"sweet_berry_bush",
|
"explosion",
|
||||||
|
"fall",
|
||||||
|
"falling_anvil",
|
||||||
|
"falling_block",
|
||||||
|
"falling_stalactite",
|
||||||
|
"fireball",
|
||||||
|
"fireworks",
|
||||||
|
"fly_into_wall",
|
||||||
"freeze",
|
"freeze",
|
||||||
"stalagmite",
|
"generic",
|
||||||
// 1.20+
|
|
||||||
"outside_border",
|
|
||||||
"generic_kill",
|
"generic_kill",
|
||||||
|
"hot_floor",
|
||||||
|
"in_fire",
|
||||||
|
"in_wall",
|
||||||
|
"indirect_magic",
|
||||||
|
"lava",
|
||||||
|
"lightning_bolt",
|
||||||
|
"magic",
|
||||||
|
"mob_attack",
|
||||||
|
"mob_attack_no_aggro",
|
||||||
|
"mob_projectile",
|
||||||
|
"on_fire",
|
||||||
|
"out_of_world",
|
||||||
|
"outside_border",
|
||||||
|
"player_attack",
|
||||||
|
"player_explosion",
|
||||||
|
"sonic_boom",
|
||||||
|
"spit",
|
||||||
|
"stalagmite",
|
||||||
|
"starve",
|
||||||
|
"sting",
|
||||||
|
"sweet_berry_bush",
|
||||||
|
"thorns",
|
||||||
|
"thrown",
|
||||||
|
"trident",
|
||||||
|
"unattributed_fireball",
|
||||||
|
"wither",
|
||||||
|
"wither_skull",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(super) fn all() -> Vec<CodecItem<DamageType>> {
|
pub(super) fn all() -> Vec<CodecItem<DamageType>> {
|
||||||
@ -48,6 +71,7 @@ pub(super) fn all() -> Vec<CodecItem<DamageType>> {
|
|||||||
message_id: "inFire".into(),
|
message_id: "inFire".into(),
|
||||||
scaling: "when_caused_by_living_non_player".into(),
|
scaling: "when_caused_by_living_non_player".into(),
|
||||||
effects: None,
|
effects: None,
|
||||||
|
death_message_type: Some("default".into()),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@ -2,24 +2,23 @@ use serde::Serialize;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct Dimension {
|
pub struct Dimension {
|
||||||
ambient_light: f32,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
bed_works: bool,
|
fixed_time: Option<f64>,
|
||||||
coordinate_scale: f32,
|
|
||||||
effects: String,
|
|
||||||
has_ceiling: bool,
|
|
||||||
has_raids: bool,
|
|
||||||
has_skylight: bool,
|
has_skylight: bool,
|
||||||
height: i32, // 1.17+
|
has_ceiling: bool,
|
||||||
infiniburn: String,
|
|
||||||
logical_height: i32,
|
|
||||||
min_y: i32, // 1.17+
|
|
||||||
natural: bool,
|
|
||||||
piglin_safe: bool,
|
|
||||||
fixed_time: i64,
|
|
||||||
respawn_anchor_works: bool,
|
|
||||||
ultrawarm: bool,
|
ultrawarm: bool,
|
||||||
|
natural: bool,
|
||||||
// 1.19+
|
coordinate_scale: f64,
|
||||||
|
bed_works: bool,
|
||||||
|
respawn_anchor_works: bool,
|
||||||
|
min_y: i32,
|
||||||
|
height: i32,
|
||||||
|
logical_height: i32,
|
||||||
|
infiniburn: String,
|
||||||
|
effects: String,
|
||||||
|
ambient_light: f32,
|
||||||
|
piglin_safe: bool,
|
||||||
|
has_raids: bool,
|
||||||
monster_spawn_light_level: i32,
|
monster_spawn_light_level: i32,
|
||||||
monster_spawn_block_light_limit: i32,
|
monster_spawn_block_light_limit: i32,
|
||||||
}
|
}
|
||||||
@ -29,14 +28,14 @@ pub fn overworld(world_min_y: i32, world_height: u32) -> Dimension {
|
|||||||
piglin_safe: false,
|
piglin_safe: false,
|
||||||
natural: true,
|
natural: true,
|
||||||
ambient_light: 0.0,
|
ambient_light: 0.0,
|
||||||
fixed_time: 6000,
|
fixed_time: Some(6000.0),
|
||||||
infiniburn: "#minecraft:infiniburn_overworld".into(),
|
infiniburn: "#minecraft:infiniburn_overworld".into(),
|
||||||
respawn_anchor_works: false,
|
respawn_anchor_works: false,
|
||||||
has_skylight: true,
|
has_skylight: true,
|
||||||
bed_works: true,
|
bed_works: true,
|
||||||
effects: "minecraft:overworld".into(),
|
effects: "minecraft:overworld".into(),
|
||||||
has_raids: false,
|
has_raids: false,
|
||||||
logical_height: 128,
|
logical_height: 384,
|
||||||
coordinate_scale: 1.0,
|
coordinate_scale: 1.0,
|
||||||
ultrawarm: false,
|
ultrawarm: false,
|
||||||
has_ceiling: false,
|
has_ceiling: false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{bytebuf::buffer::ByteBuffer, nbt};
|
use super::bytebuf::ByteBuffer;
|
||||||
|
|
||||||
mod biomes;
|
mod biomes;
|
||||||
mod chat_type;
|
mod chat_type;
|
||||||
@ -32,12 +32,9 @@ struct CodecItem<T> {
|
|||||||
element: T,
|
element: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_single_dimension<T>(out: &mut ByteBuffer, world_min_y: i32, world_height: u32)
|
pub fn write_single_dimension(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
||||||
where
|
|
||||||
std::io::Cursor<T>: std::io::Write,
|
|
||||||
{
|
|
||||||
let dimension = dimensions::overworld(world_min_y, world_height);
|
let dimension = dimensions::overworld(world_min_y, world_height);
|
||||||
out.write_bytes(&nbt::to_nbt("", &dimension).unwrap().serialize());
|
// out.put_slice(&crab_nbt::nbt!("", &dimension).unwrap().serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
||||||
@ -67,9 +64,9 @@ pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Dimension codec
|
// Dimension codec
|
||||||
out.write_bytes(&nbt::to_nbt("", &info).unwrap().serialize());
|
// out.put_slice(&nbt::to_nbt("", &info).unwrap().serialize());
|
||||||
// Current dimension type (key in dimension codec)
|
// Current dimension type (key in dimension codec)
|
||||||
out.write_string("minecraft:overworld");
|
out.put_string("minecraft:overworld");
|
||||||
// Current world
|
// Current world
|
||||||
out.write_string("minecraft:overworld");
|
out.put_string("minecraft:overworld");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::player::{ChatMode, Hand},
|
entity::player::{ChatMode, Hand},
|
||||||
protocol::{bytebuf::buffer::ByteBuffer, VarInt},
|
protocol::{bytebuf::ByteBuffer, VarInt},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SClientInformation {
|
pub struct SClientInformation {
|
||||||
@ -15,18 +15,18 @@ pub struct SClientInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SClientInformation {
|
impl SClientInformation {
|
||||||
pub const PACKET_ID: VarInt = 0;
|
pub const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
locale: bytebuf.read_string_len(16).unwrap(),
|
locale: bytebuf.get_string_len(16).unwrap(),
|
||||||
view_distance: bytebuf.read_i8().unwrap(),
|
view_distance: bytebuf.get_i8(),
|
||||||
chat_mode: ChatMode::from_varint(bytebuf.read_var_int().unwrap()),
|
chat_mode: ChatMode::from_varint(bytebuf.get_var_int()),
|
||||||
chat_colors: bytebuf.read_bool().unwrap(),
|
chat_colors: bytebuf.get_bool(),
|
||||||
skin_parts: bytebuf.read_u8().unwrap(),
|
skin_parts: bytebuf.get_u8(),
|
||||||
main_hand: Hand::from_varint(bytebuf.read_var_int().unwrap()),
|
main_hand: Hand::from_varint(bytebuf.get_var_int()),
|
||||||
text_filtering: bytebuf.read_bool().unwrap(),
|
text_filtering: bytebuf.get_bool(),
|
||||||
server_listing: bytebuf.read_bool().unwrap(),
|
server_listing: bytebuf.get_bool(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,9 +34,24 @@ impl SClientInformation {
|
|||||||
pub struct SAcknowledgeFinishConfig {}
|
pub struct SAcknowledgeFinishConfig {}
|
||||||
|
|
||||||
impl SAcknowledgeFinishConfig {
|
impl SAcknowledgeFinishConfig {
|
||||||
pub const PACKET_ID: VarInt = 3;
|
pub const PACKET_ID: VarInt = 0x03;
|
||||||
|
|
||||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SKnownPacks {
|
||||||
|
known_pack_count: VarInt,
|
||||||
|
// known_packs: &'a [KnownPack]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SKnownPacks {
|
||||||
|
pub const PACKET_ID: VarInt = 0x07;
|
||||||
|
|
||||||
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
|
Self {
|
||||||
|
known_pack_count: bytebuf.get_var_int(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, ConnectionState, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, ConnectionState, VarInt};
|
||||||
|
|
||||||
pub struct SHandShake {
|
pub struct SHandShake {
|
||||||
pub protocol_version: VarInt,
|
pub protocol_version: VarInt,
|
||||||
@ -8,14 +8,14 @@ pub struct SHandShake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SHandShake {
|
impl SHandShake {
|
||||||
pub const PACKET_ID: VarInt = 0;
|
pub const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
protocol_version: bytebuf.read_var_int().unwrap(),
|
protocol_version: bytebuf.get_var_int(),
|
||||||
server_address: bytebuf.read_string_len(255).unwrap(),
|
server_address: bytebuf.get_string_len(255).unwrap(),
|
||||||
server_port: bytebuf.read_u16().unwrap(),
|
server_port: bytebuf.get_u16(),
|
||||||
next_state: ConnectionState::from_varint(bytebuf.read_var_int().unwrap()),
|
next_state: ConnectionState::from_varint(bytebuf.get_var_int()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, VarInt};
|
||||||
|
|
||||||
pub struct SLoginStart {
|
pub struct SLoginStart {
|
||||||
pub name: String, // 16
|
pub name: String, // 16
|
||||||
@ -6,12 +6,12 @@ pub struct SLoginStart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SLoginStart {
|
impl SLoginStart {
|
||||||
pub const PACKET_ID: VarInt = 0;
|
pub const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: bytebuf.read_string_len(16).unwrap(),
|
name: bytebuf.get_string_len(16).unwrap(),
|
||||||
uuid: bytebuf.read_uuid().unwrap(),
|
uuid: bytebuf.get_uuid(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,18 +24,18 @@ pub struct SEncryptionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SEncryptionResponse {
|
impl SEncryptionResponse {
|
||||||
pub const PACKET_ID: VarInt = 1;
|
pub const PACKET_ID: VarInt = 0x01;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
let shared_secret_length = bytebuf.read_var_int().unwrap();
|
let shared_secret_length = bytebuf.get_var_int();
|
||||||
let shared_secret = bytebuf.read_bytes(shared_secret_length as usize).unwrap();
|
let shared_secret = bytebuf.copy_to_bytes(shared_secret_length as usize);
|
||||||
let verify_token_length = bytebuf.read_var_int().unwrap();
|
let verify_token_length = bytebuf.get_var_int();
|
||||||
let verify_token = bytebuf.read_bytes(shared_secret_length as usize).unwrap();
|
let verify_token = bytebuf.copy_to_bytes(shared_secret_length as usize);
|
||||||
Self {
|
Self {
|
||||||
shared_secret_length,
|
shared_secret_length,
|
||||||
shared_secret,
|
shared_secret: shared_secret.to_vec(),
|
||||||
verify_token_length,
|
verify_token_length,
|
||||||
verify_token,
|
verify_token: verify_token.to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,12 +47,12 @@ pub struct SLoginPluginResponse<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SLoginPluginResponse<'a> {
|
impl<'a> SLoginPluginResponse<'a> {
|
||||||
pub const PACKET_ID: VarInt = 2;
|
pub const PACKET_ID: VarInt = 0x02;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
message_id: bytebuf.read_var_int().unwrap(),
|
message_id: bytebuf.get_var_int(),
|
||||||
successful: bytebuf.read_bool().unwrap(),
|
successful: bytebuf.get_bool(),
|
||||||
data: None, // TODO
|
data: None, // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ pub struct SLoginAcknowledged {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SLoginAcknowledged {
|
impl SLoginAcknowledged {
|
||||||
pub const PACKET_ID: VarInt = 3;
|
pub const PACKET_ID: VarInt = 0x03;
|
||||||
|
|
||||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, VarInt};
|
use crate::protocol::{bytebuf::ByteBuffer, VarInt};
|
||||||
|
|
||||||
pub struct SStatusRequest {
|
pub struct SStatusRequest {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SStatusRequest {
|
impl SStatusRequest {
|
||||||
pub const PACKET_ID: VarInt = 0;
|
pub const PACKET_ID: VarInt = 0x00;
|
||||||
|
|
||||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
@ -17,11 +17,11 @@ pub struct SPingRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SPingRequest {
|
impl SPingRequest {
|
||||||
pub const PACKET_ID: VarInt = 1;
|
pub const PACKET_ID: VarInt = 0x01;
|
||||||
|
|
||||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
payload: bytebuf.read_i64().unwrap(),
|
payload: bytebuf.get_i64(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,10 +17,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
protocol::{
|
protocol::{
|
||||||
client::{
|
client::{
|
||||||
config::{CKnownPacks, CRegistryData, Entry, KnownPack},
|
config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry},
|
||||||
play::CLogin,
|
play::CLogin,
|
||||||
},
|
},
|
||||||
Players, Sample, StatusResponse, VarInt, Version,
|
Players, Sample, StatusResponse, VarInt, VarInt32, Version,
|
||||||
},
|
},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
@ -94,23 +94,6 @@ impl Server {
|
|||||||
entity_id: self.new_entity_id(),
|
entity_id: self.new_entity_id(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// known data packs
|
|
||||||
client.send_packet(CKnownPacks::new(
|
|
||||||
1,
|
|
||||||
vec![KnownPack {
|
|
||||||
namespace: "minecraft".to_string(),
|
|
||||||
id: "core".to_string(),
|
|
||||||
version: "1.21".to_string(),
|
|
||||||
}],
|
|
||||||
));
|
|
||||||
client.send_packet(CRegistryData::new(
|
|
||||||
"0".into(),
|
|
||||||
1,
|
|
||||||
vec![Entry {
|
|
||||||
entry_id: "minecraft:dimension_type".into(),
|
|
||||||
has_data: true,
|
|
||||||
}],
|
|
||||||
));
|
|
||||||
|
|
||||||
client.send_packet(CLogin::new(
|
client.send_packet(CLogin::new(
|
||||||
player.entity_id(),
|
player.entity_id(),
|
||||||
@ -145,6 +128,18 @@ impl Server {
|
|||||||
self.entity_id.fetch_add(1, Ordering::SeqCst)
|
self.entity_id.fetch_add(1, Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_brand(client: &mut Client) {
|
||||||
|
// send server brand
|
||||||
|
let brand = "pumpkin";
|
||||||
|
let mut buf = vec![];
|
||||||
|
let _ = VarInt32(brand.len() as i32).encode(&mut buf);
|
||||||
|
buf.extend_from_slice(brand.as_bytes());
|
||||||
|
client.send_packet(CPluginMessage::new(
|
||||||
|
"minecraft:brand".to_string(),
|
||||||
|
buf.as_slice(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn default_response(
|
pub fn default_response(
|
||||||
config: &(BasicConfiguration, AdvancedConfiguration),
|
config: &(BasicConfiguration, AdvancedConfiguration),
|
||||||
) -> StatusResponse {
|
) -> StatusResponse {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user