mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
move to tokio's bytes crate
This commit is contained in:
43
Cargo.lock
generated
43
Cargo.lock
generated
@@ -69,9 +69,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
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]]
|
||||
name = "cfb8"
|
||||
@@ -123,6 +129,18 @@ dependencies = [
|
||||
"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]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
@@ -177,6 +195,26 @@ dependencies = [
|
||||
"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]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -484,6 +522,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"cfb8",
|
||||
"crab_nbt",
|
||||
"crossbeam-channel",
|
||||
"flate2",
|
||||
"image",
|
||||
|
||||
11
README.md
11
README.md
@@ -3,13 +3,16 @@ A Minecraft server written in pure Rust
|
||||
|
||||
|
||||
### Features
|
||||
- [x] Server Status/Ping
|
||||
- [x] Encryption
|
||||
- [ ] Compression
|
||||
- States
|
||||
- [x] Handshake
|
||||
- [x] Status
|
||||
- [x] Login
|
||||
- [x] Config
|
||||
- [ ] 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"
|
||||
cfb8 = "0.8.1"
|
||||
flate2 = "1.0.30"
|
||||
bytes = "1.6.1"
|
||||
bytes = "1.7"
|
||||
|
||||
anyhow = "1.0.86"
|
||||
|
||||
@@ -30,5 +30,6 @@ mio = { version = "1.0.1", features = ["os-poll", "net"]}
|
||||
crossbeam-channel = "0.5.13"
|
||||
uuid = "1.10"
|
||||
toml = "0.8.17"
|
||||
crab_nbt = "0.1.2"
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::{
|
||||
protocol::{
|
||||
client::{
|
||||
config::CFinishConfig,
|
||||
config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry},
|
||||
login::{CEncryptionRequest, CLoginSuccess},
|
||||
status::{CPingResponse, CStatusResponse},
|
||||
},
|
||||
server::{
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||
status::{SPingRequest, SStatusRequest},
|
||||
},
|
||||
ConnectionState,
|
||||
ConnectionState, KnownPack,
|
||||
},
|
||||
server::Server,
|
||||
};
|
||||
@@ -47,6 +47,7 @@ pub trait ClientPacketProcessor {
|
||||
server: &mut Server,
|
||||
client_information: SClientInformation,
|
||||
);
|
||||
fn handle_known_packs(&mut self, server: &mut Server, config_acknowledged: SKnownPacks);
|
||||
fn handle_config_acknowledged(
|
||||
&mut self,
|
||||
server: &mut Server,
|
||||
@@ -120,10 +121,19 @@ impl ClientPacketProcessor for Client {
|
||||
fn handle_login_acknowledged(
|
||||
&mut self,
|
||||
_server: &mut Server,
|
||||
login_acknowledged: SLoginAcknowledged,
|
||||
_login_acknowledged: SLoginAcknowledged,
|
||||
) {
|
||||
let _ = login_acknowledged;
|
||||
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");
|
||||
}
|
||||
fn handle_client_information(
|
||||
@@ -141,7 +151,34 @@ impl ClientPacketProcessor for Client {
|
||||
text_filtering: client_information.text_filtering,
|
||||
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
|
||||
dbg!("finish config");
|
||||
self.send_packet(CFinishConfig::new());
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
protocol::{
|
||||
client::{config::CConfigDisconnect, login::CLoginDisconnect},
|
||||
server::{
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||
status::{SPingRequest, SStatusRequest},
|
||||
@@ -168,6 +168,9 @@ impl Client {
|
||||
SAcknowledgeFinishConfig::PACKET_ID => {
|
||||
self.handle_config_acknowledged(server, SAcknowledgeFinishConfig::read(bytebuf))
|
||||
}
|
||||
SKnownPacks::PACKET_ID => {
|
||||
self.handle_known_packs(server, SKnownPacks::read(bytebuf))
|
||||
}
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Config state",
|
||||
packet.id
|
||||
|
||||
@@ -4,7 +4,7 @@ use bytes::{Buf, BytesMut};
|
||||
|
||||
use crate::{
|
||||
client::MAX_PACKET_SIZE,
|
||||
protocol::{bytebuf::buffer::ByteBuffer, RawPacket, VarInt32, VarIntDecodeError},
|
||||
protocol::{bytebuf::ByteBuffer, RawPacket, VarInt32, VarIntDecodeError},
|
||||
};
|
||||
|
||||
type Cipher = cfb8::Decryptor<aes::Aes128>;
|
||||
@@ -56,7 +56,7 @@ impl PacketDecoder {
|
||||
Ok(Some(RawPacket {
|
||||
len: packet_len,
|
||||
id: packet_id,
|
||||
bytebuf: ByteBuffer::from_bytes(&data),
|
||||
bytebuf: ByteBuffer::new(BytesMut::from(data)),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use bytes::{BufMut, BytesMut};
|
||||
|
||||
use crate::{
|
||||
client::MAX_PACKET_SIZE,
|
||||
protocol::{bytebuf::buffer::ByteBuffer, ClientPacket, VarInt32},
|
||||
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt32},
|
||||
};
|
||||
|
||||
type Cipher = cfb8::Encryptor<aes::Aes128>;
|
||||
@@ -25,13 +25,13 @@ impl PacketEncoder {
|
||||
|
||||
let mut writer = (&mut self.buf).writer();
|
||||
|
||||
let mut packet_buf = ByteBuffer::new();
|
||||
let mut packet_buf = ByteBuffer::empty();
|
||||
VarInt32(P::PACKET_ID)
|
||||
.encode(&mut writer)
|
||||
.context("failed to encode packet ID")?;
|
||||
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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
pub mod reader;
|
||||
use core::str;
|
||||
use std::io::{self, Error, ErrorKind, Read};
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
|
||||
use crate::protocol::{VarInt, VarLong};
|
||||
|
||||
const SEGMENT_BITS: u8 = 0x7F;
|
||||
const CONTINUE_BIT: u8 = 0x80;
|
||||
/// An enum to represent the byte order of the ByteBuffer object
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Endian {
|
||||
BigEndian,
|
||||
LittleEndian,
|
||||
|
||||
pub struct ByteBuffer {
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -21,10 +41,10 @@ impl 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) {
|
||||
bytebuf.write_string(&self.reason);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_string(&self.reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,37 +63,31 @@ impl 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,
|
||||
known_packs: Vec<KnownPack>,
|
||||
known_packs: &'a [KnownPack],
|
||||
}
|
||||
|
||||
impl CKnownPacks {
|
||||
pub fn new(count: VarInt, known_packs: Vec<KnownPack>) -> Self {
|
||||
impl<'a> CKnownPacks<'a> {
|
||||
pub fn new(count: VarInt, known_packs: &'a [KnownPack]) -> Self {
|
||||
Self { count, known_packs }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KnownPack {
|
||||
pub namespace: String,
|
||||
pub id: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl ClientPacket for CKnownPacks {
|
||||
impl<'a> ClientPacket for CKnownPacks<'a> {
|
||||
const PACKET_ID: VarInt = 0x0E;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_var_int(self.count);
|
||||
bytebuf.write_list::<KnownPack>(&self.known_packs, |p, v| {
|
||||
p.write_string(&v.namespace);
|
||||
p.write_string(&v.id);
|
||||
p.write_string(&v.version);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
// bytebuf.write_var_int(self.count);
|
||||
bytebuf.put_list::<KnownPack>(&self.known_packs, |p, v| {
|
||||
p.put_string(&v.namespace);
|
||||
p.put_string(&v.id);
|
||||
p.put_string(&v.version);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -103,13 +117,13 @@ pub struct Entry {
|
||||
impl ClientPacket for CRegistryData {
|
||||
const PACKET_ID: VarInt = 0x07;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_string(&self.registry_id);
|
||||
bytebuf.write_var_int(self.entry_count);
|
||||
bytebuf.write_list::<Entry>(&self.entries, |p, v| {
|
||||
p.write_string(&v.entry_id);
|
||||
p.write_bool(v.has_data);
|
||||
registry::write_codec(p, -64, 320);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_string(&self.registry_id);
|
||||
bytebuf.put_var_int(self.entry_count);
|
||||
bytebuf.put_list::<Entry>(&self.entries, |p, v| {
|
||||
p.put_string(&v.entry_id);
|
||||
p.put_bool(v.has_data);
|
||||
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 {
|
||||
reason: String,
|
||||
@@ -11,10 +11,10 @@ impl CLoginDisconnect {
|
||||
}
|
||||
|
||||
impl ClientPacket for CLoginDisconnect {
|
||||
const PACKET_ID: VarInt = 0;
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
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> {
|
||||
const PACKET_ID: VarInt = 1;
|
||||
const PACKET_ID: VarInt = 0x01;
|
||||
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.write_string_len(self.server_id.as_str(), 20);
|
||||
bytebuf.write_var_int(self.public_key_length);
|
||||
bytebuf.write_bytes(self.public_key);
|
||||
bytebuf.write_var_int(self.verify_token_length);
|
||||
bytebuf.write_bytes(self.verify_token);
|
||||
bytebuf.write_bool(self.should_authenticate);
|
||||
bytebuf.put_string(self.server_id.as_str());
|
||||
bytebuf.put_var_int(self.public_key_length);
|
||||
bytebuf.put_slice(self.public_key);
|
||||
bytebuf.put_var_int(self.verify_token_length);
|
||||
bytebuf.put_slice(self.verify_token);
|
||||
bytebuf.put_bool(self.should_authenticate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,14 +96,14 @@ pub struct Property {
|
||||
}
|
||||
|
||||
impl ClientPacket for CLoginSuccess {
|
||||
const PACKET_ID: VarInt = 2;
|
||||
const PACKET_ID: VarInt = 0x02;
|
||||
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.write_uuid(self.uuid);
|
||||
bytebuf.write_string(&self.username);
|
||||
bytebuf.write_var_int(self.num_of_props);
|
||||
bytebuf.put_uuid(self.uuid);
|
||||
bytebuf.put_string(&self.username);
|
||||
bytebuf.put_var_int(self.num_of_props);
|
||||
// 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 {
|
||||
const PACKET_ID: VarInt = 3;
|
||||
const PACKET_ID: VarInt = 0x03;
|
||||
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.write_var_int(self.threshold);
|
||||
bytebuf.put_var_int(self.threshold);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
entity::player::GameMode,
|
||||
protocol::{ClientPacket, VarInt},
|
||||
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt},
|
||||
};
|
||||
|
||||
pub struct SetHeldItem {
|
||||
@@ -16,8 +16,8 @@ impl SetHeldItem {
|
||||
impl ClientPacket for SetHeldItem {
|
||||
const PACKET_ID: VarInt = 0x53;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_i8(self.slot);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_i8(self.slot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ impl CPlayerAbilities {
|
||||
impl ClientPacket for CPlayerAbilities {
|
||||
const PACKET_ID: VarInt = 0x38;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_i8(self.flags);
|
||||
bytebuf.write_f32(self.flying_speed);
|
||||
bytebuf.write_f32(self.field_of_view);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_i8(self.flags);
|
||||
bytebuf.put_f32(self.flying_speed);
|
||||
bytebuf.put_f32(self.field_of_view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ impl CChangeDifficulty {
|
||||
impl ClientPacket for CChangeDifficulty {
|
||||
const PACKET_ID: VarInt = 0x0B;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_u8(self.difficulty);
|
||||
bytebuf.write_bool(self.locked);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_u8(self.difficulty);
|
||||
bytebuf.put_bool(self.locked);
|
||||
}
|
||||
}
|
||||
pub struct CLogin {
|
||||
@@ -147,28 +147,28 @@ impl CLogin {
|
||||
impl ClientPacket for CLogin {
|
||||
const PACKET_ID: VarInt = 0x2B;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_i32(self.entity_id);
|
||||
bytebuf.write_bool(self.is_hardcore);
|
||||
bytebuf.write_var_int(self.dimension_count);
|
||||
bytebuf.write_string_array(self.dimension_names.as_slice());
|
||||
bytebuf.write_var_int(self.max_players);
|
||||
bytebuf.write_var_int(self.view_distance);
|
||||
bytebuf.write_var_int(self.simulated_distance);
|
||||
bytebuf.write_bool(self.reduced_debug_info);
|
||||
bytebuf.write_bool(self.enabled_respawn_screen);
|
||||
bytebuf.write_bool(self.limited_crafting);
|
||||
bytebuf.write_var_int(self.dimension_type);
|
||||
bytebuf.write_string(&self.dimension_name);
|
||||
bytebuf.write_i64(self.hashed_seed);
|
||||
bytebuf.write_u8(self.game_mode.to_byte() as u8);
|
||||
bytebuf.write_i8(self.previous_gamemode.to_byte());
|
||||
bytebuf.write_bool(self.debug);
|
||||
bytebuf.write_bool(self.is_flat);
|
||||
bytebuf.write_bool(self.has_death_loc);
|
||||
bytebuf.write_option(&self.death_dimension_name, |buf, v| buf.write_string(v));
|
||||
bytebuf.write_option(&self.death_loc, |buf, v| buf.write_string(v));
|
||||
bytebuf.write_var_int(self.portal_cooldown);
|
||||
bytebuf.write_bool(self.enforce_secure_chat);
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_i32(self.entity_id);
|
||||
bytebuf.put_bool(self.is_hardcore);
|
||||
bytebuf.put_var_int(self.dimension_count);
|
||||
bytebuf.put_string_array(self.dimension_names.as_slice());
|
||||
bytebuf.put_var_int(self.max_players);
|
||||
bytebuf.put_var_int(self.view_distance);
|
||||
bytebuf.put_var_int(self.simulated_distance);
|
||||
bytebuf.put_bool(self.reduced_debug_info);
|
||||
bytebuf.put_bool(self.enabled_respawn_screen);
|
||||
bytebuf.put_bool(self.limited_crafting);
|
||||
bytebuf.put_var_int(self.dimension_type);
|
||||
bytebuf.put_string(&self.dimension_name);
|
||||
bytebuf.put_i64(self.hashed_seed);
|
||||
bytebuf.put_u8(self.game_mode.to_byte() as u8);
|
||||
bytebuf.put_i8(self.previous_gamemode.to_byte());
|
||||
bytebuf.put_bool(self.debug);
|
||||
bytebuf.put_bool(self.is_flat);
|
||||
bytebuf.put_bool(self.has_death_loc);
|
||||
bytebuf.put_option(&self.death_dimension_name, |buf, v| buf.put_string(v));
|
||||
bytebuf.put_option(&self.death_loc, |buf, v| buf.put_string(v));
|
||||
bytebuf.put_var_int(self.portal_cooldown);
|
||||
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 {
|
||||
payload: i64, // must responde with the same as in `SPingRequest`
|
||||
@@ -11,10 +11,10 @@ impl CPingResponse {
|
||||
}
|
||||
|
||||
impl ClientPacket for CPingResponse {
|
||||
const PACKET_ID: VarInt = 1;
|
||||
const PACKET_ID: VarInt = 0x01;
|
||||
|
||||
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 {
|
||||
const PACKET_ID: VarInt = 0;
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
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 anyhow::bail;
|
||||
use bytebuf::buffer::ByteBuffer;
|
||||
use bytebuf::ByteBuffer;
|
||||
use byteorder::ReadBytesExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod bytebuf;
|
||||
pub mod nbt;
|
||||
mod registry;
|
||||
|
||||
pub mod client;
|
||||
@@ -113,7 +112,6 @@ impl ConnectionState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RawPacket {
|
||||
pub len: VarInt,
|
||||
pub id: VarInt,
|
||||
@@ -152,3 +150,9 @@ pub struct Sample {
|
||||
pub name: String,
|
||||
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 serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Biome {
|
||||
category: String,
|
||||
depth: f32,
|
||||
has_precipitation: bool,
|
||||
temperature: f32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature_modifier: Option<String>,
|
||||
downfall: f32,
|
||||
effects: BiomeEffects,
|
||||
precipitation: String,
|
||||
scale: f32,
|
||||
temperature: f32,
|
||||
has_precipitation: bool,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct BiomeEffects {
|
||||
sky_color: i32,
|
||||
fog_color: i32,
|
||||
water_fog_color: i32,
|
||||
water_color: i32,
|
||||
water_fog_color: i32,
|
||||
sky_color: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
foliage_color: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
grass_color: Option<i32>,
|
||||
#[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)]
|
||||
struct MoodSound {
|
||||
block_search_extent: i32,
|
||||
@@ -33,35 +57,45 @@ struct MoodSound {
|
||||
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>> {
|
||||
let biome = Biome {
|
||||
precipitation: "rain".into(),
|
||||
depth: 1.0,
|
||||
has_precipitation: false,
|
||||
temperature: 1.0,
|
||||
scale: 1.0,
|
||||
downfall: 1.0,
|
||||
category: "none".into(),
|
||||
has_precipitation: true,
|
||||
temperature_modifier: None,
|
||||
downfall: 0.0,
|
||||
effects: BiomeEffects {
|
||||
sky_color: 0x78a7ff,
|
||||
fog_color: 0xc0d8ff,
|
||||
water_fog_color: 0x050533,
|
||||
water_color: 0x3f76e4,
|
||||
foliage_color: None,
|
||||
grass_color: None,
|
||||
fog_color: 0x7FA1FF,
|
||||
water_color: 0x7FA1FF,
|
||||
water_fog_color: 0x7FA1FF,
|
||||
sky_color: 0x7FA1FF,
|
||||
foliage_color: Some(0x7FA1FF),
|
||||
grass_color: Some(0x7FA1FF),
|
||||
grass_color_modifier: None,
|
||||
particle: None,
|
||||
ambient_sound: None,
|
||||
mood_sound: Some(MoodSound {
|
||||
block_search_extent: 8,
|
||||
offset: 2.0,
|
||||
sound: "minecraft:ambient.cave".into(),
|
||||
tick_delay: 6000,
|
||||
}),
|
||||
// sky_color: 0xff00ff,
|
||||
// water_color: 0xff00ff,
|
||||
// fog_color: 0xff00ff,
|
||||
// water_fog_color: 0xff00ff,
|
||||
// grass_color: 0xff00ff,
|
||||
// foliage_color: 0x00ffe5,
|
||||
// grass_color: 0xff5900,
|
||||
additions_sound: None,
|
||||
music: None,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ChatType {
|
||||
chat: ChatParams,
|
||||
narration: ChatParams,
|
||||
chat: Decoration,
|
||||
narration: Decoration,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct ChatParams {
|
||||
parameters: Vec<String>,
|
||||
struct Decoration {
|
||||
translation_key: String,
|
||||
// style: Option<>
|
||||
parameters: Vec<String>,
|
||||
}
|
||||
|
||||
pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
||||
@@ -17,11 +18,11 @@ pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
||||
name: "minecraft:chat".into(),
|
||||
id: 0,
|
||||
element: ChatType {
|
||||
chat: ChatParams {
|
||||
chat: Decoration {
|
||||
parameters: vec!["sender".into(), "content".into()],
|
||||
translation_key: "chat.type.text".into(),
|
||||
},
|
||||
narration: ChatParams {
|
||||
narration: Decoration {
|
||||
parameters: vec!["sender".into(), "content".into()],
|
||||
translation_key: "chat.type.text.narrate".into(),
|
||||
},
|
||||
|
||||
@@ -3,38 +3,61 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DamageType {
|
||||
exhaustion: f32,
|
||||
message_id: String,
|
||||
scaling: String,
|
||||
exhaustion: f32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
effects: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
death_message_type: Option<String>,
|
||||
}
|
||||
|
||||
const NAMES: &[&str] = &[
|
||||
"in_fire",
|
||||
"lightning_bolt",
|
||||
"on_fire",
|
||||
"lava",
|
||||
"hot_floor",
|
||||
"in_wall",
|
||||
"cramming",
|
||||
"drown",
|
||||
"starve",
|
||||
"arrow",
|
||||
"bad_respawn_point",
|
||||
"cactus",
|
||||
"fall",
|
||||
"fly_into_wall",
|
||||
"out_of_world",
|
||||
"generic",
|
||||
"magic",
|
||||
"wither",
|
||||
"cramming",
|
||||
"dragon_breath",
|
||||
"drown",
|
||||
"dry_out",
|
||||
"sweet_berry_bush",
|
||||
"explosion",
|
||||
"fall",
|
||||
"falling_anvil",
|
||||
"falling_block",
|
||||
"falling_stalactite",
|
||||
"fireball",
|
||||
"fireworks",
|
||||
"fly_into_wall",
|
||||
"freeze",
|
||||
"stalagmite",
|
||||
// 1.20+
|
||||
"outside_border",
|
||||
"generic",
|
||||
"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>> {
|
||||
@@ -48,6 +71,7 @@ pub(super) fn all() -> Vec<CodecItem<DamageType>> {
|
||||
message_id: "inFire".into(),
|
||||
scaling: "when_caused_by_living_non_player".into(),
|
||||
effects: None,
|
||||
death_message_type: Some("default".into()),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -2,24 +2,23 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Dimension {
|
||||
ambient_light: f32,
|
||||
bed_works: bool,
|
||||
coordinate_scale: f32,
|
||||
effects: String,
|
||||
has_ceiling: bool,
|
||||
has_raids: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
fixed_time: Option<f64>,
|
||||
has_skylight: bool,
|
||||
height: i32, // 1.17+
|
||||
infiniburn: String,
|
||||
logical_height: i32,
|
||||
min_y: i32, // 1.17+
|
||||
natural: bool,
|
||||
piglin_safe: bool,
|
||||
fixed_time: i64,
|
||||
respawn_anchor_works: bool,
|
||||
has_ceiling: bool,
|
||||
ultrawarm: bool,
|
||||
|
||||
// 1.19+
|
||||
natural: bool,
|
||||
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_block_light_limit: i32,
|
||||
}
|
||||
@@ -29,14 +28,14 @@ pub fn overworld(world_min_y: i32, world_height: u32) -> Dimension {
|
||||
piglin_safe: false,
|
||||
natural: true,
|
||||
ambient_light: 0.0,
|
||||
fixed_time: 6000,
|
||||
fixed_time: Some(6000.0),
|
||||
infiniburn: "#minecraft:infiniburn_overworld".into(),
|
||||
respawn_anchor_works: false,
|
||||
has_skylight: true,
|
||||
bed_works: true,
|
||||
effects: "minecraft:overworld".into(),
|
||||
has_raids: false,
|
||||
logical_height: 128,
|
||||
logical_height: 384,
|
||||
coordinate_scale: 1.0,
|
||||
ultrawarm: false,
|
||||
has_ceiling: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{bytebuf::buffer::ByteBuffer, nbt};
|
||||
use super::bytebuf::ByteBuffer;
|
||||
|
||||
mod biomes;
|
||||
mod chat_type;
|
||||
@@ -32,12 +32,9 @@ struct CodecItem<T> {
|
||||
element: T,
|
||||
}
|
||||
|
||||
pub fn write_single_dimension<T>(out: &mut ByteBuffer, world_min_y: i32, world_height: u32)
|
||||
where
|
||||
std::io::Cursor<T>: std::io::Write,
|
||||
{
|
||||
pub fn write_single_dimension(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
||||
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) {
|
||||
@@ -67,9 +64,9 @@ pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
||||
};
|
||||
|
||||
// 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)
|
||||
out.write_string("minecraft:overworld");
|
||||
out.put_string("minecraft:overworld");
|
||||
// Current world
|
||||
out.write_string("minecraft:overworld");
|
||||
out.put_string("minecraft:overworld");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
entity::player::{ChatMode, Hand},
|
||||
protocol::{bytebuf::buffer::ByteBuffer, VarInt},
|
||||
protocol::{bytebuf::ByteBuffer, VarInt},
|
||||
};
|
||||
|
||||
pub struct SClientInformation {
|
||||
@@ -15,18 +15,18 @@ pub struct SClientInformation {
|
||||
}
|
||||
|
||||
impl SClientInformation {
|
||||
pub const PACKET_ID: VarInt = 0;
|
||||
pub const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
locale: bytebuf.read_string_len(16).unwrap(),
|
||||
view_distance: bytebuf.read_i8().unwrap(),
|
||||
chat_mode: ChatMode::from_varint(bytebuf.read_var_int().unwrap()),
|
||||
chat_colors: bytebuf.read_bool().unwrap(),
|
||||
skin_parts: bytebuf.read_u8().unwrap(),
|
||||
main_hand: Hand::from_varint(bytebuf.read_var_int().unwrap()),
|
||||
text_filtering: bytebuf.read_bool().unwrap(),
|
||||
server_listing: bytebuf.read_bool().unwrap(),
|
||||
locale: bytebuf.get_string_len(16).unwrap(),
|
||||
view_distance: bytebuf.get_i8(),
|
||||
chat_mode: ChatMode::from_varint(bytebuf.get_var_int()),
|
||||
chat_colors: bytebuf.get_bool(),
|
||||
skin_parts: bytebuf.get_u8(),
|
||||
main_hand: Hand::from_varint(bytebuf.get_var_int()),
|
||||
text_filtering: bytebuf.get_bool(),
|
||||
server_listing: bytebuf.get_bool(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,24 @@ impl SClientInformation {
|
||||
pub struct SAcknowledgeFinishConfig {}
|
||||
|
||||
impl SAcknowledgeFinishConfig {
|
||||
pub const PACKET_ID: VarInt = 3;
|
||||
pub const PACKET_ID: VarInt = 0x03;
|
||||
|
||||
pub fn read(_bytebuf: &mut ByteBuffer) -> 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 protocol_version: VarInt,
|
||||
@@ -8,14 +8,14 @@ pub struct SHandShake {
|
||||
}
|
||||
|
||||
impl SHandShake {
|
||||
pub const PACKET_ID: VarInt = 0;
|
||||
pub const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
protocol_version: bytebuf.read_var_int().unwrap(),
|
||||
server_address: bytebuf.read_string_len(255).unwrap(),
|
||||
server_port: bytebuf.read_u16().unwrap(),
|
||||
next_state: ConnectionState::from_varint(bytebuf.read_var_int().unwrap()),
|
||||
protocol_version: bytebuf.get_var_int(),
|
||||
server_address: bytebuf.get_string_len(255).unwrap(),
|
||||
server_port: bytebuf.get_u16(),
|
||||
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 name: String, // 16
|
||||
@@ -6,12 +6,12 @@ pub struct SLoginStart {
|
||||
}
|
||||
|
||||
impl SLoginStart {
|
||||
pub const PACKET_ID: VarInt = 0;
|
||||
pub const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
name: bytebuf.read_string_len(16).unwrap(),
|
||||
uuid: bytebuf.read_uuid().unwrap(),
|
||||
name: bytebuf.get_string_len(16).unwrap(),
|
||||
uuid: bytebuf.get_uuid(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,18 +24,18 @@ pub struct SEncryptionResponse {
|
||||
}
|
||||
|
||||
impl SEncryptionResponse {
|
||||
pub const PACKET_ID: VarInt = 1;
|
||||
pub const PACKET_ID: VarInt = 0x01;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
let shared_secret_length = bytebuf.read_var_int().unwrap();
|
||||
let shared_secret = bytebuf.read_bytes(shared_secret_length as usize).unwrap();
|
||||
let verify_token_length = bytebuf.read_var_int().unwrap();
|
||||
let verify_token = bytebuf.read_bytes(shared_secret_length as usize).unwrap();
|
||||
let shared_secret_length = bytebuf.get_var_int();
|
||||
let shared_secret = bytebuf.copy_to_bytes(shared_secret_length as usize);
|
||||
let verify_token_length = bytebuf.get_var_int();
|
||||
let verify_token = bytebuf.copy_to_bytes(shared_secret_length as usize);
|
||||
Self {
|
||||
shared_secret_length,
|
||||
shared_secret,
|
||||
shared_secret: shared_secret.to_vec(),
|
||||
verify_token_length,
|
||||
verify_token,
|
||||
verify_token: verify_token.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,12 +47,12 @@ pub struct 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 {
|
||||
Self {
|
||||
message_id: bytebuf.read_var_int().unwrap(),
|
||||
successful: bytebuf.read_bool().unwrap(),
|
||||
message_id: bytebuf.get_var_int(),
|
||||
successful: bytebuf.get_bool(),
|
||||
data: None, // TODO
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ pub struct SLoginAcknowledged {
|
||||
}
|
||||
|
||||
impl SLoginAcknowledged {
|
||||
pub const PACKET_ID: VarInt = 3;
|
||||
pub const PACKET_ID: VarInt = 0x03;
|
||||
|
||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::protocol::{bytebuf::buffer::ByteBuffer, VarInt};
|
||||
use crate::protocol::{bytebuf::ByteBuffer, VarInt};
|
||||
|
||||
pub struct SStatusRequest {
|
||||
// empty
|
||||
}
|
||||
|
||||
impl SStatusRequest {
|
||||
pub const PACKET_ID: VarInt = 0;
|
||||
pub const PACKET_ID: VarInt = 0x00;
|
||||
|
||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
@@ -17,11 +17,11 @@ pub struct SPingRequest {
|
||||
}
|
||||
|
||||
impl SPingRequest {
|
||||
pub const PACKET_ID: VarInt = 1;
|
||||
pub const PACKET_ID: VarInt = 0x01;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
payload: bytebuf.read_i64().unwrap(),
|
||||
payload: bytebuf.get_i64(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ use crate::{
|
||||
},
|
||||
protocol::{
|
||||
client::{
|
||||
config::{CKnownPacks, CRegistryData, Entry, KnownPack},
|
||||
config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry},
|
||||
play::CLogin,
|
||||
},
|
||||
Players, Sample, StatusResponse, VarInt, Version,
|
||||
Players, Sample, StatusResponse, VarInt, VarInt32, Version,
|
||||
},
|
||||
world::World,
|
||||
};
|
||||
@@ -94,23 +94,6 @@ impl Server {
|
||||
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(
|
||||
player.entity_id(),
|
||||
@@ -145,6 +128,18 @@ impl Server {
|
||||
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(
|
||||
config: &(BasicConfiguration, AdvancedConfiguration),
|
||||
) -> StatusResponse {
|
||||
|
||||
Reference in New Issue
Block a user