mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
add nbt
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
/target
|
||||
configuration.toml
|
||||
features.toml
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
server_address = "127.0.0.1"
|
||||
server_port = 25565
|
||||
seed = ""
|
||||
max_plyers = 32
|
||||
view_distances = 10
|
||||
simulation_distance = 10
|
||||
resource_pack = ""
|
||||
resource_pack_sha1 = ""
|
||||
default_difficulty = "Normal"
|
||||
allow_nether = true
|
||||
hardcore = false
|
||||
online_mode = true
|
||||
spawn_protection = 16
|
||||
motd = "A Blazing fast Pumpkin Server!"
|
||||
@@ -1,2 +0,0 @@
|
||||
liquid_physics = true
|
||||
encryption = true
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::protocol::{nbt::NBT, VarInt, VarLong};
|
||||
use crate::protocol::{nbt::nbt::NBT, VarInt, VarLong};
|
||||
|
||||
use super::{Endian, CONTINUE_BIT, SEGMENT_BITS};
|
||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||
@@ -618,6 +618,29 @@ impl ByteBuffer {
|
||||
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),
|
||||
|
||||
@@ -1,26 +1,4 @@
|
||||
use crate::protocol::{nbt::NBT, ClientPacket, VarInt};
|
||||
|
||||
pub struct CRegistryData {
|
||||
registry_id: String,
|
||||
entry_count: VarInt,
|
||||
entries: NBT,
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
entry_id: String,
|
||||
has_data: bool,
|
||||
data: NBT,
|
||||
}
|
||||
|
||||
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_array(self.entries);
|
||||
}
|
||||
}
|
||||
use crate::protocol::{registry, ClientPacket, VarInt};
|
||||
|
||||
pub struct CCookieRequest {
|
||||
// TODO
|
||||
@@ -69,3 +47,69 @@ impl ClientPacket for CFinishConfig {
|
||||
|
||||
fn write(&self, _bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
||||
}
|
||||
|
||||
pub struct CKnownPacks {
|
||||
count: VarInt,
|
||||
known_packs: Vec<KnownPack>,
|
||||
}
|
||||
|
||||
impl CKnownPacks {
|
||||
pub fn new(count: VarInt, known_packs: Vec<KnownPack>) -> Self {
|
||||
Self { count, known_packs }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KnownPack {
|
||||
pub namespace: String,
|
||||
pub id: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl ClientPacket for CKnownPacks {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CRegistryData {
|
||||
registry_id: String,
|
||||
entry_count: VarInt,
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl CRegistryData {
|
||||
pub fn new(registry_id: String, entry_count: VarInt, entries: Vec<Entry>) -> Self {
|
||||
Self {
|
||||
registry_id,
|
||||
entry_count,
|
||||
entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Entry {
|
||||
pub entry_id: String,
|
||||
pub has_data: bool,
|
||||
// data provided by registry::write_codec
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
use crate::{
|
||||
entity::player::GameMode,
|
||||
protocol::{ClientPacket, VarInt},
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
use std::{collections::HashMap, error::Error, fmt, string::FromUtf8Error};
|
||||
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::{Tag, NBT};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
InvalidType(u8),
|
||||
InvalidString(FromUtf8Error),
|
||||
}
|
||||
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::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(buf: Vec<u8>) -> Result<Self, ParseError> {
|
||||
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
|
||||
@@ -32,10 +55,14 @@ impl NBT {
|
||||
/// 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().unwrap();
|
||||
let len = buf.read_u16().unwrap();
|
||||
let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap();
|
||||
Ok(NBT::new(&name, Tag::deserialize(ty, buf)?))
|
||||
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)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,26 +70,26 @@ 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().unwrap())),
|
||||
2 => Ok(Self::Short(buf.read_i16().unwrap())),
|
||||
3 => Ok(Self::Int(buf.read_i32().unwrap())),
|
||||
4 => Ok(Self::Long(buf.read_i64().unwrap())),
|
||||
5 => Ok(Self::Float(buf.read_f32().unwrap())),
|
||||
6 => Ok(Self::Double(buf.read_f64().unwrap())),
|
||||
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().unwrap();
|
||||
Ok(Self::ByteArr(buf.read_bytes(len as usize).unwrap()))
|
||||
let len = buf.read_i32()?;
|
||||
Ok(Self::ByteArr(buf.read_bytes(len as usize)?))
|
||||
}
|
||||
8 => {
|
||||
let len = buf.read_u16().unwrap();
|
||||
match String::from_utf8(buf.read_bytes(len as usize).unwrap()) {
|
||||
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().unwrap();
|
||||
let len = buf.read_i32().unwrap();
|
||||
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)?);
|
||||
@@ -72,30 +99,30 @@ impl Tag {
|
||||
10 => {
|
||||
let mut inner = HashMap::new();
|
||||
loop {
|
||||
let ty = buf.read_u8().unwrap();
|
||||
let ty = buf.read_u8()?;
|
||||
if ty == Self::End.ty() {
|
||||
break;
|
||||
}
|
||||
let len = buf.read_u16().unwrap();
|
||||
let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap();
|
||||
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(Self::Compound(inner))
|
||||
Ok(inner.into())
|
||||
}
|
||||
11 => {
|
||||
let len = buf.read_i32().unwrap();
|
||||
let len = buf.read_i32()?;
|
||||
let mut inner = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
inner.push(buf.read_i32().unwrap());
|
||||
inner.push(buf.read_i32()?);
|
||||
}
|
||||
Ok(Self::IntArray(inner))
|
||||
}
|
||||
12 => {
|
||||
let len = buf.read_i32().unwrap();
|
||||
let len = buf.read_i32()?;
|
||||
let mut inner = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
inner.push(buf.read_i64().unwrap());
|
||||
inner.push(buf.read_i64()?);
|
||||
}
|
||||
Ok(Self::LongArray(inner))
|
||||
}
|
||||
|
||||
56
pumpkin/src/protocol/nbt/error.rs
Normal file
56
pumpkin/src/protocol/nbt/error.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
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,115 +1,445 @@
|
||||
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;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NBT {
|
||||
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,
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// 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(HashMap<String, Tag>), // Types can be any kind, and are named. Order is not defined.
|
||||
IntArray(Vec<i32>),
|
||||
LongArray(Vec<i64>),
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
NBT {
|
||||
tag,
|
||||
name: name.into(),
|
||||
}
|
||||
self.items.push(tag);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates an empty nbt tag.
|
||||
pub fn empty(name: &str) -> Self {
|
||||
NBT {
|
||||
tag: Tag::End,
|
||||
name: name.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: {:?}, new: {:?}",
|
||||
inner, 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) -> &HashMap<String, Tag> {
|
||||
if let Tag::Compound(inner) = &self.tag {
|
||||
&inner
|
||||
} else {
|
||||
panic!("called compound on non-compound type: {:?}", self);
|
||||
}
|
||||
fn end(self) -> Result<()> {
|
||||
self.ser.tag = Tag::List(self.items);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
/// A simpler way to construct compound tags inline.
|
||||
pub fn compound(value: &[(&str, Tag)]) -> Self {
|
||||
let mut inner = HashMap::new();
|
||||
for (name, tag) in value {
|
||||
inner.insert(name.to_string(), tag.clone());
|
||||
}
|
||||
Self::Compound(inner)
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
283
pumpkin/src/protocol/nbt/nbt.rs
Normal file
283
pumpkin/src/protocol/nbt/nbt.rs
Normal file
@@ -0,0 +1,283 @@
|
||||
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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,18 @@ use crate::protocol::bytebuf::buffer::ByteBuffer;
|
||||
use super::{Tag, NBT};
|
||||
|
||||
impl NBT {
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut out = ByteBuffer::new();
|
||||
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());
|
||||
out.write_bytes(&self.tag.serialize());
|
||||
self.tag.serialize(out);
|
||||
}
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut out = ByteBuffer::new();
|
||||
self.serialize_buf(&mut out);
|
||||
out.into_vec()
|
||||
}
|
||||
}
|
||||
@@ -33,8 +39,8 @@ impl Tag {
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut out = ByteBuffer::new();
|
||||
/// 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),
|
||||
@@ -55,11 +61,11 @@ impl Tag {
|
||||
out.write_u8(v.get(0).unwrap_or(&Self::End).ty());
|
||||
out.write_i32(v.len() as i32);
|
||||
for tag in v {
|
||||
out.write_bytes(&tag.serialize());
|
||||
tag.serialize(out);
|
||||
}
|
||||
}
|
||||
Self::Compound(v) => {
|
||||
for (name, tag) in 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());
|
||||
@@ -69,7 +75,7 @@ impl Tag {
|
||||
}
|
||||
out.write_u16(name.len() as u16);
|
||||
out.write_bytes(name.as_bytes());
|
||||
out.write_bytes(&tag.serialize());
|
||||
tag.serialize(out);
|
||||
}
|
||||
out.write_u8(Self::End.ty());
|
||||
}
|
||||
@@ -86,6 +92,5 @@ impl Tag {
|
||||
}
|
||||
}
|
||||
}
|
||||
out.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
73
pumpkin/src/protocol/registry/biomes.rs
Normal file
73
pumpkin/src/protocol/registry/biomes.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use super::CodecItem;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Biome {
|
||||
category: String,
|
||||
depth: f32,
|
||||
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,
|
||||
#[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+
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct MoodSound {
|
||||
block_search_extent: i32,
|
||||
offset: f64,
|
||||
sound: String,
|
||||
tick_delay: i32,
|
||||
}
|
||||
|
||||
pub(super) fn all() -> Vec<CodecItem<Biome>> {
|
||||
let biome = Biome {
|
||||
precipitation: "rain".into(),
|
||||
depth: 1.0,
|
||||
temperature: 1.0,
|
||||
scale: 1.0,
|
||||
downfall: 1.0,
|
||||
category: "none".into(),
|
||||
has_precipitation: true,
|
||||
effects: BiomeEffects {
|
||||
sky_color: 0x78a7ff,
|
||||
fog_color: 0xc0d8ff,
|
||||
water_fog_color: 0x050533,
|
||||
water_color: 0x3f76e4,
|
||||
foliage_color: None,
|
||||
grass_color: 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,
|
||||
},
|
||||
};
|
||||
|
||||
vec![CodecItem {
|
||||
name: "minecraft:plains".into(),
|
||||
id: 0,
|
||||
element: biome,
|
||||
}]
|
||||
}
|
||||
30
pumpkin/src/protocol/registry/chat_type.rs
Normal file
30
pumpkin/src/protocol/registry/chat_type.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use super::CodecItem;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ChatType {
|
||||
chat: ChatParams,
|
||||
narration: ChatParams,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct ChatParams {
|
||||
parameters: Vec<String>,
|
||||
translation_key: String,
|
||||
}
|
||||
|
||||
pub(super) fn all() -> Vec<CodecItem<ChatType>> {
|
||||
vec![CodecItem {
|
||||
name: "minecraft:chat".into(),
|
||||
id: 0,
|
||||
element: ChatType {
|
||||
chat: ChatParams {
|
||||
parameters: vec!["sender".into(), "content".into()],
|
||||
translation_key: "chat.type.text".into(),
|
||||
},
|
||||
narration: ChatParams {
|
||||
parameters: vec!["sender".into(), "content".into()],
|
||||
translation_key: "chat.type.text.narrate".into(),
|
||||
},
|
||||
},
|
||||
}]
|
||||
}
|
||||
58
pumpkin/src/protocol/registry/damage_type.rs
Normal file
58
pumpkin/src/protocol/registry/damage_type.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use super::CodecItem;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DamageType {
|
||||
exhaustion: f32,
|
||||
message_id: String,
|
||||
scaling: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
effects: Option<String>,
|
||||
}
|
||||
|
||||
const NAMES: &[&str] = &[
|
||||
"in_fire",
|
||||
"lightning_bolt",
|
||||
"on_fire",
|
||||
"lava",
|
||||
"hot_floor",
|
||||
"in_wall",
|
||||
"cramming",
|
||||
"drown",
|
||||
"starve",
|
||||
"cactus",
|
||||
"fall",
|
||||
"fly_into_wall",
|
||||
"out_of_world",
|
||||
"generic",
|
||||
"magic",
|
||||
"wither",
|
||||
"dragon_breath",
|
||||
"dry_out",
|
||||
"sweet_berry_bush",
|
||||
"freeze",
|
||||
"stalagmite",
|
||||
// 1.20+
|
||||
"outside_border",
|
||||
"generic_kill",
|
||||
];
|
||||
|
||||
pub(super) fn all() -> Vec<CodecItem<DamageType>> {
|
||||
let mut items: Vec<_> = NAMES
|
||||
.iter()
|
||||
.map(|name| CodecItem {
|
||||
name: (*name).into(),
|
||||
id: 0,
|
||||
element: DamageType {
|
||||
exhaustion: 0.1,
|
||||
message_id: "inFire".into(),
|
||||
scaling: "when_caused_by_living_non_player".into(),
|
||||
effects: None,
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
items[1].element.effects = Some("burning".into());
|
||||
|
||||
items
|
||||
}
|
||||
49
pumpkin/src/protocol/registry/dimensions.rs
Normal file
49
pumpkin/src/protocol/registry/dimensions.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
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,
|
||||
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,
|
||||
ultrawarm: bool,
|
||||
|
||||
// 1.19+
|
||||
monster_spawn_light_level: i32,
|
||||
monster_spawn_block_light_limit: i32,
|
||||
}
|
||||
|
||||
pub fn overworld(world_min_y: i32, world_height: u32) -> Dimension {
|
||||
Dimension {
|
||||
piglin_safe: false,
|
||||
natural: true,
|
||||
ambient_light: 0.0,
|
||||
fixed_time: 6000,
|
||||
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,
|
||||
coordinate_scale: 1.0,
|
||||
ultrawarm: false,
|
||||
has_ceiling: false,
|
||||
min_y: world_min_y,
|
||||
height: (world_height as i32 + 15) / 16 * 16,
|
||||
|
||||
monster_spawn_light_level: 7,
|
||||
monster_spawn_block_light_limit: 7,
|
||||
}
|
||||
}
|
||||
@@ -1 +1,75 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{bytebuf::buffer::ByteBuffer, nbt};
|
||||
|
||||
mod biomes;
|
||||
mod chat_type;
|
||||
mod damage_type;
|
||||
mod dimensions;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct LoginInfo {
|
||||
#[serde(rename = "minecraft:dimension_type")]
|
||||
dimensions: Codec<dimensions::Dimension>,
|
||||
#[serde(rename = "minecraft:worldgen/biome")]
|
||||
biomes: Codec<biomes::Biome>,
|
||||
#[serde(rename = "minecraft:chat_type")]
|
||||
chat: Codec<chat_type::ChatType>,
|
||||
#[serde(rename = "minecraft:damage_type")]
|
||||
damage: Codec<damage_type::DamageType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct Codec<T> {
|
||||
#[serde(rename = "type")]
|
||||
ty: String,
|
||||
value: Vec<CodecItem<T>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct CodecItem<T> {
|
||||
name: String,
|
||||
id: i32,
|
||||
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,
|
||||
{
|
||||
let dimension = dimensions::overworld(world_min_y, world_height);
|
||||
out.write_bytes(&nbt::to_nbt("", &dimension).unwrap().serialize());
|
||||
}
|
||||
|
||||
pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
|
||||
let dimension = dimensions::overworld(world_min_y, world_height);
|
||||
|
||||
let info = LoginInfo {
|
||||
dimensions: Codec {
|
||||
ty: "minecraft:dimension_type".into(),
|
||||
value: vec![CodecItem {
|
||||
name: "minecraft:overworld".into(),
|
||||
id: 0,
|
||||
element: dimension,
|
||||
}],
|
||||
},
|
||||
biomes: Codec {
|
||||
ty: "minecraft:worldgen/biome".into(),
|
||||
value: biomes::all(),
|
||||
},
|
||||
chat: Codec {
|
||||
ty: "minecraft:chat_type".into(),
|
||||
value: chat_type::all(),
|
||||
},
|
||||
damage: Codec {
|
||||
ty: "minecraft:damage_type".into(),
|
||||
value: damage_type::all(),
|
||||
},
|
||||
};
|
||||
|
||||
// Dimension codec
|
||||
out.write_bytes(&nbt::to_nbt("", &info).unwrap().serialize());
|
||||
// Current dimension type (key in dimension codec)
|
||||
out.write_string("minecraft:overworld");
|
||||
// Current world
|
||||
out.write_string("minecraft:overworld");
|
||||
}
|
||||
|
||||
@@ -15,7 +15,13 @@ use crate::{
|
||||
player::{GameMode, Player},
|
||||
Entity, EntityId,
|
||||
},
|
||||
protocol::{client::play::CLogin, Players, Sample, StatusResponse, VarInt, Version},
|
||||
protocol::{
|
||||
client::{
|
||||
config::{CKnownPacks, CRegistryData, Entry, KnownPack},
|
||||
play::CLogin,
|
||||
},
|
||||
Players, Sample, StatusResponse, VarInt, Version,
|
||||
},
|
||||
world::World,
|
||||
};
|
||||
|
||||
@@ -88,6 +94,23 @@ 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(),
|
||||
|
||||
Reference in New Issue
Block a user