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
|
/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 super::{Endian, CONTINUE_BIT, SEGMENT_BITS};
|
||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||||
@@ -618,6 +618,29 @@ impl ByteBuffer {
|
|||||||
read_number!(self, read_f64, 8)
|
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> {
|
pub fn read_nbt(&mut self) -> Result<NBT> {
|
||||||
match NBT::deserialize_buf(self) {
|
match NBT::deserialize_buf(self) {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
|
|||||||
@@ -1,26 +1,4 @@
|
|||||||
use crate::protocol::{nbt::NBT, ClientPacket, VarInt};
|
use crate::protocol::{registry, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CCookieRequest {
|
pub struct CCookieRequest {
|
||||||
// TODO
|
// TODO
|
||||||
@@ -69,3 +47,69 @@ impl ClientPacket for CFinishConfig {
|
|||||||
|
|
||||||
fn write(&self, _bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
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::{
|
use crate::{
|
||||||
entity::player::GameMode,
|
entity::player::GameMode,
|
||||||
protocol::{ClientPacket, VarInt},
|
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 crate::protocol::bytebuf::buffer::ByteBuffer;
|
||||||
|
|
||||||
use super::{Tag, NBT};
|
use super::{nbt::ParseError, Tag, NBT};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ParseError {
|
|
||||||
InvalidType(u8),
|
|
||||||
InvalidString(FromUtf8Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidType(ty) => write!(f, "invalid tag type: {}", ty),
|
Self::InvalidType(ty) => write!(f, "invalid tag type: {ty}"),
|
||||||
Self::InvalidString(e) => write!(f, "invalid string: {}", e),
|
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 Error for ParseError {}
|
||||||
|
|
||||||
impl NBT {
|
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.
|
/// 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))
|
Self::deserialize_buf(&mut ByteBuffer::from_vec(buf))
|
||||||
}
|
}
|
||||||
/// Deserializes the given buffer as nbt data. This will continue reading
|
/// 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
|
/// 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).
|
/// no guarantees as too how far ahead the buffer will have been advanced).
|
||||||
pub fn deserialize_buf(buf: &mut ByteBuffer) -> Result<Self, ParseError> {
|
pub fn deserialize_buf(buf: &mut ByteBuffer) -> Result<Self, ParseError> {
|
||||||
let ty = buf.read_u8().unwrap();
|
let ty = buf.read_u8()?;
|
||||||
let len = buf.read_u16().unwrap();
|
if ty == 0 {
|
||||||
let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap();
|
Ok(NBT::empty())
|
||||||
Ok(NBT::new(&name, Tag::deserialize(ty, buf)?))
|
} 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> {
|
fn deserialize(ty: u8, buf: &mut ByteBuffer) -> Result<Self, ParseError> {
|
||||||
match ty {
|
match ty {
|
||||||
0 => Ok(Self::End),
|
0 => Ok(Self::End),
|
||||||
1 => Ok(Self::Byte(buf.read_i8().unwrap())),
|
1 => Ok(Self::Byte(buf.read_i8()?)),
|
||||||
2 => Ok(Self::Short(buf.read_i16().unwrap())),
|
2 => Ok(Self::Short(buf.read_i16()?)),
|
||||||
3 => Ok(Self::Int(buf.read_i32().unwrap())),
|
3 => Ok(Self::Int(buf.read_i32()?)),
|
||||||
4 => Ok(Self::Long(buf.read_i64().unwrap())),
|
4 => Ok(Self::Long(buf.read_i64()?)),
|
||||||
5 => Ok(Self::Float(buf.read_f32().unwrap())),
|
5 => Ok(Self::Float(buf.read_f32()?)),
|
||||||
6 => Ok(Self::Double(buf.read_f64().unwrap())),
|
6 => Ok(Self::Double(buf.read_f64()?)),
|
||||||
7 => {
|
7 => {
|
||||||
let len = buf.read_i32().unwrap();
|
let len = buf.read_i32()?;
|
||||||
Ok(Self::ByteArr(buf.read_bytes(len as usize).unwrap()))
|
Ok(Self::ByteArr(buf.read_bytes(len as usize)?))
|
||||||
}
|
}
|
||||||
8 => {
|
8 => {
|
||||||
let len = buf.read_u16().unwrap();
|
let len = buf.read_u16()?;
|
||||||
match String::from_utf8(buf.read_bytes(len as usize).unwrap()) {
|
match String::from_utf8(buf.read_bytes(len as usize)?) {
|
||||||
Ok(v) => Ok(Self::String(v)),
|
Ok(v) => Ok(Self::String(v)),
|
||||||
Err(e) => Err(ParseError::InvalidString(e)),
|
Err(e) => Err(ParseError::InvalidString(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
9 => {
|
9 => {
|
||||||
let inner_ty = buf.read_u8().unwrap();
|
let inner_ty = buf.read_u8()?;
|
||||||
let len = buf.read_i32().unwrap();
|
let len = buf.read_i32()?;
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
let mut inner = Vec::with_capacity(len as usize);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
inner.push(Tag::deserialize(inner_ty, buf)?);
|
inner.push(Tag::deserialize(inner_ty, buf)?);
|
||||||
@@ -72,30 +99,30 @@ impl Tag {
|
|||||||
10 => {
|
10 => {
|
||||||
let mut inner = HashMap::new();
|
let mut inner = HashMap::new();
|
||||||
loop {
|
loop {
|
||||||
let ty = buf.read_u8().unwrap();
|
let ty = buf.read_u8()?;
|
||||||
if ty == Self::End.ty() {
|
if ty == Self::End.ty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let len = buf.read_u16().unwrap();
|
let len = buf.read_u16()?;
|
||||||
let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap();
|
let name = String::from_utf8(buf.read_bytes(len as usize)?).unwrap();
|
||||||
let tag = Tag::deserialize(ty, buf)?;
|
let tag = Tag::deserialize(ty, buf)?;
|
||||||
inner.insert(name, tag);
|
inner.insert(name, tag);
|
||||||
}
|
}
|
||||||
Ok(Self::Compound(inner))
|
Ok(inner.into())
|
||||||
}
|
}
|
||||||
11 => {
|
11 => {
|
||||||
let len = buf.read_i32().unwrap();
|
let len = buf.read_i32()?;
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
let mut inner = Vec::with_capacity(len as usize);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
inner.push(buf.read_i32().unwrap());
|
inner.push(buf.read_i32()?);
|
||||||
}
|
}
|
||||||
Ok(Self::IntArray(inner))
|
Ok(Self::IntArray(inner))
|
||||||
}
|
}
|
||||||
12 => {
|
12 => {
|
||||||
let len = buf.read_i32().unwrap();
|
let len = buf.read_i32()?;
|
||||||
let mut inner = Vec::with_capacity(len as usize);
|
let mut inner = Vec::with_capacity(len as usize);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
inner.push(buf.read_i64().unwrap());
|
inner.push(buf.read_i64()?);
|
||||||
}
|
}
|
||||||
Ok(Self::LongArray(inner))
|
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 std::collections::HashMap;
|
||||||
|
|
||||||
|
use error::Error;
|
||||||
|
use error::Result;
|
||||||
|
use nbt::Tag;
|
||||||
|
use nbt::NBT;
|
||||||
|
use serde::{ser, Serialize};
|
||||||
|
|
||||||
mod deserialize;
|
mod deserialize;
|
||||||
|
mod error;
|
||||||
|
pub mod nbt;
|
||||||
mod serialize;
|
mod serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
pub fn to_nbt<T>(name: &str, value: &T) -> anyhow::Result<NBT>
|
||||||
pub struct 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,
|
tag: Tag,
|
||||||
name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a single tag. It does not contain a name, but has the actual data
|
pub struct SeqSerializer<'a> {
|
||||||
/// for any of the nbt tags.
|
ser: &'a mut Serializer,
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
items: Vec<Tag>,
|
||||||
pub enum Tag {
|
}
|
||||||
End,
|
pub struct MapSerializer<'a> {
|
||||||
Byte(i8),
|
ser: &'a mut Serializer,
|
||||||
Short(i16),
|
key: Option<String>,
|
||||||
Int(i32),
|
items: HashMap<String, Tag>,
|
||||||
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>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NBT {
|
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||||
/// Creates a new nbt tag. The tag value can be anything.
|
// The output type produced by this `Serializer` during successful
|
||||||
///
|
// serialization. Most serializers that produce text or binary output should
|
||||||
/// # Panics
|
// set `Ok = ()` and serialize into an `io::Write` or buffer contained
|
||||||
/// This will panic if the tag is a list, and the values within that list
|
// within the `Serializer` instance, as happens here. Serializers that build
|
||||||
/// contain multiple types. This is a limitation with the nbt data format:
|
// in-memory data structures may be simplified by using `Ok` to propagate
|
||||||
/// lists can only contain one type of data.
|
// the data structure around.
|
||||||
pub fn new(name: &str, tag: Tag) -> Self {
|
type Ok = ();
|
||||||
if let Tag::List(inner) = &tag {
|
type Error = Error;
|
||||||
if let Some(v) = inner.get(0) {
|
|
||||||
let ty = v.ty();
|
// Associated types for keeping track of additional state while serializing
|
||||||
for v in inner {
|
// compound data structures like sequences and maps. In this case no
|
||||||
if v.ty() != ty {
|
// additional state is required beyond what is already stored in the
|
||||||
panic!("the given list contains multiple types: {:?}", inner);
|
// 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 {
|
self.items.push(tag);
|
||||||
tag,
|
Ok(())
|
||||||
name: name.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty nbt tag.
|
fn end(self) -> Result<()> {
|
||||||
pub fn empty(name: &str) -> Self {
|
self.ser.tag = Tag::List(self.items);
|
||||||
NBT {
|
Ok(())
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
// Same thing but for tuples.
|
||||||
/// A simpler way to construct compound tags inline.
|
impl<'a> ser::SerializeTuple for SeqSerializer<'a> {
|
||||||
pub fn compound(value: &[(&str, Tag)]) -> Self {
|
type Ok = ();
|
||||||
let mut inner = HashMap::new();
|
type Error = Error;
|
||||||
for (name, tag) in value {
|
|
||||||
inner.insert(name.to_string(), tag.clone());
|
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||||
}
|
where
|
||||||
Self::Compound(inner)
|
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};
|
use super::{Tag, NBT};
|
||||||
|
|
||||||
impl NBT {
|
impl NBT {
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize_buf(&self, out: &mut ByteBuffer) {
|
||||||
let mut out = ByteBuffer::new();
|
|
||||||
out.write_u8(self.tag.ty());
|
out.write_u8(self.tag.ty());
|
||||||
|
if matches!(self.tag, Tag::End) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
out.write_u16(self.name.len() as u16);
|
out.write_u16(self.name.len() as u16);
|
||||||
out.write_bytes(self.name.as_bytes());
|
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()
|
out.into_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,8 +39,8 @@ impl Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(&self) -> Vec<u8> {
|
/// Serializes the data of the tag. Does not add type byte.
|
||||||
let mut out = ByteBuffer::new();
|
fn serialize(&self, out: &mut ByteBuffer) {
|
||||||
match self {
|
match self {
|
||||||
Self::End => (),
|
Self::End => (),
|
||||||
Self::Byte(v) => out.write_i8(*v),
|
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_u8(v.get(0).unwrap_or(&Self::End).ty());
|
||||||
out.write_i32(v.len() as i32);
|
out.write_i32(v.len() as i32);
|
||||||
for tag in v {
|
for tag in v {
|
||||||
out.write_bytes(&tag.serialize());
|
tag.serialize(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Compound(v) => {
|
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
|
// 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.
|
// separated form, so we have a manual implementation of serialize() here.
|
||||||
out.write_u8(tag.ty());
|
out.write_u8(tag.ty());
|
||||||
@@ -69,7 +75,7 @@ impl Tag {
|
|||||||
}
|
}
|
||||||
out.write_u16(name.len() as u16);
|
out.write_u16(name.len() as u16);
|
||||||
out.write_bytes(name.as_bytes());
|
out.write_bytes(name.as_bytes());
|
||||||
out.write_bytes(&tag.serialize());
|
tag.serialize(out);
|
||||||
}
|
}
|
||||||
out.write_u8(Self::End.ty());
|
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},
|
player::{GameMode, Player},
|
||||||
Entity, EntityId,
|
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,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,6 +94,23 @@ impl Server {
|
|||||||
entity_id: self.new_entity_id(),
|
entity_id: self.new_entity_id(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// known data packs
|
||||||
|
client.send_packet(CKnownPacks::new(
|
||||||
|
1,
|
||||||
|
vec![KnownPack {
|
||||||
|
namespace: "minecraft".to_string(),
|
||||||
|
id: "core".to_string(),
|
||||||
|
version: "1.21".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
|
client.send_packet(CRegistryData::new(
|
||||||
|
"0".into(),
|
||||||
|
1,
|
||||||
|
vec![Entry {
|
||||||
|
entry_id: "minecraft:dimension_type".into(),
|
||||||
|
has_data: true,
|
||||||
|
}],
|
||||||
|
));
|
||||||
|
|
||||||
client.send_packet(CLogin::new(
|
client.send_packet(CLogin::new(
|
||||||
player.entity_id(),
|
player.entity_id(),
|
||||||
|
|||||||
Reference in New Issue
Block a user