mirror of
https://github.com/GIKExe/rust_mc_serv.git
synced 2025-06-23 18:02:57 +03:00
nbt 95%
This commit is contained in:
parent
07b7fd4cdf
commit
52ed60c758
@ -1,5 +1,7 @@
|
||||
|
||||
use tokio::{net::TcpStream, time::Sleep};
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use crate::data::{clientbound, serverbound, AsyncReader, AsyncWriter, DataError, Packet, Reader, TextComponentBuilder, Writer};
|
||||
|
||||
@ -153,6 +155,6 @@ async fn the_configuration(conn: &mut Connection) -> Result<(), PacketError> {
|
||||
let packet = Packet::empty(clientbound::configuration::FINISH);
|
||||
conn.write_packet(packet).await?;
|
||||
loop {
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ mod writer;
|
||||
mod packet;
|
||||
mod packet_id;
|
||||
mod component;
|
||||
mod nbt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DataError {
|
||||
@ -25,7 +24,7 @@ pub enum DataError {
|
||||
DeSerializationError,
|
||||
ZlibError,
|
||||
NBTError,
|
||||
NBTCompoundError,
|
||||
// NBTCompoundError,
|
||||
}
|
||||
|
||||
pub type Buffer = Cursor<Vec<u8>>;
|
||||
@ -49,5 +48,4 @@ pub use async_writer::*;
|
||||
pub use writer::*;
|
||||
pub use packet::*;
|
||||
pub use packet_id::{clientbound, serverbound};
|
||||
pub use component::*;
|
||||
pub use nbt::*;
|
||||
pub use component::*;
|
154
src/data/nbt.rs
154
src/data/nbt.rs
@ -1,154 +0,0 @@
|
||||
use std::{collections::HashMap, io::Cursor};
|
||||
|
||||
use super::{DataError, Reader};
|
||||
|
||||
|
||||
|
||||
|
||||
const TAG_END: u8 = 0;
|
||||
const TAG_BYTE: u8 = 1;
|
||||
const TAG_SHORT: u8 = 2;
|
||||
const TAG_INT: u8 = 3;
|
||||
const TAG_LONG: u8 = 4;
|
||||
const TAG_FLOAT: u8 = 5;
|
||||
const TAG_DOUBLE: u8 = 6;
|
||||
const TAG_BYTE_ARRAY: u8 = 7;
|
||||
const TAG_STRING: u8 = 8;
|
||||
const TAG_LIST: u8 = 9;
|
||||
const TAG_COMPOUND: u8 = 10;
|
||||
const TAG_INT_ARRAY: u8 = 11;
|
||||
const TAG_LONG_ARRAY: u8 = 12;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TAG {
|
||||
END,
|
||||
COMPOUND(HashMap<String, TAG>),
|
||||
STRING(String),
|
||||
BYTE(i8),
|
||||
SHORT(i16),
|
||||
INT(i32),
|
||||
LONG(i64),
|
||||
FLOAT(f32),
|
||||
DOUBLE(f64),
|
||||
LIST(Vec<NBT>),
|
||||
BYTE_ARRAY(Vec<i8>),
|
||||
INT_ARRAY(Vec<i32>),
|
||||
LONG_ARRAY(Vec<i64>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NBT {
|
||||
pub key: Option<String>,
|
||||
pub value: TAG,
|
||||
}
|
||||
|
||||
impl NBT {
|
||||
fn new(key: Option<String>, value: TAG) -> Self {
|
||||
Self { key, value }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NBT_Reader {
|
||||
fn _read_nbt(&mut self, root: bool, inet: bool, byte: Option<u8>) -> Result<NBT, DataError>;
|
||||
fn read_nbt(&mut self, inet: bool) -> Result<NBT, DataError>;
|
||||
}
|
||||
|
||||
|
||||
impl NBT_Reader for Cursor<Vec<u8>> {
|
||||
fn read_nbt(&mut self, inet: bool) -> Result<NBT, DataError> {
|
||||
self._read_nbt(true, inet, None)
|
||||
}
|
||||
|
||||
fn _read_nbt(&mut self, root: bool, inet: bool, b: Option<u8>) -> Result<NBT, DataError> {
|
||||
let byte = match b { Some(v) => v, None => self.read_byte()? };
|
||||
let mut key: Option<String>;
|
||||
|
||||
if byte == TAG_END {
|
||||
return Ok(NBT::new(None, TAG::END));
|
||||
}
|
||||
|
||||
if root & (byte != TAG_COMPOUND) {
|
||||
return Err(DataError::NBTCompoundError)
|
||||
}
|
||||
|
||||
if root & inet {
|
||||
key = None;
|
||||
} else {
|
||||
let size = self.read_short()? as usize;
|
||||
let buf = self.read_bytes(size)?;
|
||||
key = Some(String::from_utf8_lossy(&buf).to_string());
|
||||
}
|
||||
|
||||
if b.is_some() {
|
||||
key = None;
|
||||
}
|
||||
|
||||
match byte {
|
||||
TAG_BYTE => { Ok(NBT::new(key, TAG::BYTE(self.read_signed_byte()?))) },
|
||||
TAG_SHORT => { Ok(NBT::new(key, TAG::SHORT(self.read_signed_short()?))) },
|
||||
TAG_INT => { Ok(NBT::new(key, TAG::INT(self.read_signed_int()?))) },
|
||||
TAG_LONG => { Ok(NBT::new(key, TAG::LONG(self.read_signed_long()?))) },
|
||||
TAG_FLOAT => { Ok(NBT::new(key, TAG::FLOAT(self.read_float()?))) },
|
||||
TAG_DOUBLE => { Ok(NBT::new(key, TAG::DOUBLE(self.read_double()?))) },
|
||||
|
||||
TAG_STRING => {
|
||||
let size = self.read_short()? as usize;
|
||||
let buf = self.read_bytes(size)?;
|
||||
Ok(NBT::new(key, TAG::STRING(String::from_utf8_lossy(&buf).to_string())))
|
||||
},
|
||||
|
||||
TAG_BYTE_ARRAY => {
|
||||
let size = self.read_signed_int()? as usize;
|
||||
let buf = self.read_bytes(size)?.into_iter().map(|x| x as i8).collect();
|
||||
Ok(NBT::new(key, TAG::BYTE_ARRAY(buf)))
|
||||
},
|
||||
|
||||
TAG_INT_ARRAY => {
|
||||
let count = self.read_signed_int()? as usize;
|
||||
let mut buf: Vec<i32> = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
buf.push(self.read_signed_int()?);
|
||||
}
|
||||
Ok(NBT::new(key, TAG::INT_ARRAY(buf)))
|
||||
},
|
||||
|
||||
TAG_LONG_ARRAY => {
|
||||
let count = self.read_signed_int()? as usize;
|
||||
let mut buf: Vec<i64> = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
buf.push(self.read_signed_long()?);
|
||||
}
|
||||
Ok(NBT::new(key, TAG::LONG_ARRAY(buf)))
|
||||
},
|
||||
|
||||
TAG_LIST => {
|
||||
// byte = self.read_byte()?;
|
||||
let mut buf: Vec<NBT> = Vec::new();
|
||||
let count = self.read_signed_int()? as usize;
|
||||
println!("List count: {count}");
|
||||
for _ in 0..count {
|
||||
buf.push(self._read_nbt(false, inet, None)?);
|
||||
}
|
||||
Ok(NBT::new(key, TAG::LIST(buf)))
|
||||
},
|
||||
|
||||
TAG_COMPOUND => {
|
||||
// println!("COMPOUND");
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
let nbt = self._read_nbt(false, inet, None)?;
|
||||
if matches!(nbt.value, TAG::END) { break; };
|
||||
let Some(key) = nbt.key else { continue; };
|
||||
map.insert(key, nbt.value);
|
||||
}
|
||||
Ok(NBT::new(key, TAG::COMPOUND(map)))
|
||||
}
|
||||
|
||||
_ => {
|
||||
// println!("o: {other}");
|
||||
Err(DataError::NBTError)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -73,12 +73,16 @@ pub trait Reader {
|
||||
Ok(self.read_short()? as i16)
|
||||
}
|
||||
|
||||
fn read_signed_int(&mut self) -> Result<i32, DataError> {
|
||||
Ok(i32::from_be_bytes(
|
||||
fn read_int(&mut self) -> Result<u32, DataError> {
|
||||
Ok(u32::from_be_bytes(
|
||||
self.read_bytes(4)?.try_into().unwrap()
|
||||
))
|
||||
}
|
||||
|
||||
fn read_signed_int(&mut self) -> Result<i32, DataError> {
|
||||
Ok(self.read_int()? as i32)
|
||||
}
|
||||
|
||||
fn read_long(&mut self) -> Result<u64, DataError> {
|
||||
Ok(u64::from_be_bytes(
|
||||
self.read_bytes(8)?.try_into().unwrap()
|
||||
|
@ -74,10 +74,12 @@ pub trait Writer {
|
||||
self.write_short(value as u16)
|
||||
}
|
||||
|
||||
fn write_string(&mut self, value: &str) -> Result<(), DataError> {
|
||||
self.write_varint(value.len() as i32)?;
|
||||
self.write_bytes(value.as_bytes())?;
|
||||
Ok(())
|
||||
fn write_int(&mut self, value: u32) -> Result<(), DataError> {
|
||||
self.write_bytes(&value.to_be_bytes())
|
||||
}
|
||||
|
||||
fn write_signed_int(&mut self, value: i32) -> Result<(), DataError> {
|
||||
self.write_int(value as u32)
|
||||
}
|
||||
|
||||
fn write_long(&mut self, value: u64) -> Result<(), DataError> {
|
||||
@ -88,6 +90,20 @@ pub trait Writer {
|
||||
self.write_long(value as u64)
|
||||
}
|
||||
|
||||
fn write_float(&mut self, value: f32) -> Result<(), DataError> {
|
||||
self.write_bytes(&value.to_be_bytes())
|
||||
}
|
||||
|
||||
fn write_double(&mut self, value: f64) -> Result<(), DataError> {
|
||||
self.write_bytes(&value.to_be_bytes())
|
||||
}
|
||||
|
||||
fn write_string(&mut self, value: &str) -> Result<(), DataError> {
|
||||
self.write_varint(value.len() as i32)?;
|
||||
self.write_bytes(value.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_uuid(&mut self, value: u128) -> Result<(), DataError> {
|
||||
self.write_bytes(&value.to_be_bytes())
|
||||
}
|
||||
|
4
src/lib.rs
Normal file
4
src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod inet;
|
||||
pub mod data;
|
||||
pub mod cycle;
|
||||
pub mod nbt;
|
43
src/main.rs
43
src/main.rs
@ -1,41 +1,16 @@
|
||||
use rust_mc_serv::{cycle, inet::Server};
|
||||
|
||||
|
||||
use std::fs;
|
||||
|
||||
use data::{Buffer, NBT_Reader, Reader};
|
||||
use inet::Server;
|
||||
|
||||
mod data;
|
||||
mod inet;
|
||||
mod cycle;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// let Ok(server) = Server::new("127.0.0.1:25565").await else {
|
||||
// println!("Не удалось забиндить сервер");
|
||||
// return;
|
||||
// };
|
||||
let Ok(server) = Server::new("127.0.0.1:25565").await else {
|
||||
println!("Не удалось забиндить сервер");
|
||||
return;
|
||||
};
|
||||
|
||||
// loop {
|
||||
// let stream = server.accept().await;
|
||||
// tokio::spawn(cycle::main(stream));
|
||||
// }
|
||||
|
||||
let mut buf = Buffer::new(fs::read("servers.dat").unwrap());
|
||||
|
||||
// let mut buf = Buffer::new(vec![
|
||||
// 0x0a,
|
||||
// // 0x00, 0x0b,
|
||||
// // 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
|
||||
// 0x08,
|
||||
// 0x00, 0x04,
|
||||
// 0x6e, 0x61, 0x6d, 0x65,
|
||||
// 0x00, 0x05,
|
||||
// 0x42, 0x61, 0x6e, 0x61, 0x6e,
|
||||
// 0x00
|
||||
// ]);
|
||||
|
||||
let nbt = buf.read_nbt(false).unwrap();
|
||||
println!("{nbt:?}");
|
||||
loop {
|
||||
let stream = server.accept().await;
|
||||
tokio::spawn(cycle::main(stream));
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/nbt/mod.rs
Normal file
25
src/nbt/mod.rs
Normal file
@ -0,0 +1,25 @@
|
||||
pub mod reader;
|
||||
pub mod writer;
|
||||
mod tags;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use reader::*;
|
||||
pub use writer::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Tag {
|
||||
End,
|
||||
Byte(i8),
|
||||
Short(i16),
|
||||
Int(i32),
|
||||
Long(i64),
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
ByteArray(Vec<i8>),
|
||||
String(String),
|
||||
List(Vec<Tag>),
|
||||
Compound(HashMap<String, Tag>),
|
||||
IntArray(Vec<i32>),
|
||||
LongArray(Vec<i64>),
|
||||
}
|
98
src/nbt/reader.rs
Normal file
98
src/nbt/reader.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use std::{collections::HashMap, io::Seek};
|
||||
|
||||
use crate::data::{Buffer, DataError, Reader};
|
||||
|
||||
use super::{tags::*, Tag};
|
||||
|
||||
trait NbtReaderInternal {
|
||||
fn read_value(&mut self, t: u8) -> Result<Tag, DataError>;
|
||||
fn read_compound(&mut self) -> Result<Tag, DataError>;
|
||||
fn read_list(&mut self) -> Result<Tag, DataError>;
|
||||
}
|
||||
|
||||
impl NbtReaderInternal for Buffer {
|
||||
fn read_value(&mut self, t: u8) -> Result<Tag, DataError> {
|
||||
match t {
|
||||
TAG_END => { self.read_byte()?; Ok(Tag::End) }
|
||||
TAG_BYTE => { Ok(Tag::Byte(self.read_signed_byte()?)) }
|
||||
TAG_SHORT => { Ok(Tag::Short(self.read_signed_short()?)) }
|
||||
TAG_INT => { Ok(Tag::Int(self.read_signed_int()?)) }
|
||||
TAG_LONG => { Ok(Tag::Long(self.read_signed_long()?)) }
|
||||
TAG_FLOAT => { Ok(Tag::Float(self.read_float()?)) }
|
||||
TAG_DOUBLE => { Ok(Tag::Double(self.read_double()?)) }
|
||||
TAG_BYTE_ARRAY => {
|
||||
let size = self.read_int()? as usize;
|
||||
Ok(Tag::ByteArray(self.read_bytes(size)?.into_iter().map(|x| x as i8).collect()))
|
||||
}
|
||||
TAG_STRING => {
|
||||
let size = self.read_short()? as usize;
|
||||
let data = self.read_bytes(size)?;
|
||||
Ok(Tag::String(String::from_utf8_lossy(&data).to_string()))
|
||||
}
|
||||
TAG_LIST => { self.read_list() }
|
||||
TAG_COMPOUND => { self.read_compound() }
|
||||
TAG_INT_ARRAY => {
|
||||
let size = self.read_int()? as usize;
|
||||
let mut data = Vec::new();
|
||||
for _ in 0..size {
|
||||
data.push(self.read_signed_int()?);
|
||||
}
|
||||
Ok(Tag::IntArray(data))
|
||||
}
|
||||
TAG_LONG_ARRAY => {
|
||||
let size = self.read_int()? as usize;
|
||||
let mut data = Vec::new();
|
||||
for _ in 0..size {
|
||||
data.push(self.read_signed_long()?);
|
||||
}
|
||||
Ok(Tag::LongArray(data))
|
||||
}
|
||||
other => {
|
||||
println!("Неверный тэг: {other}");
|
||||
Err(DataError::NBTError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_compound(&mut self) -> Result<Tag, DataError> {
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
let type_of_value = self.read_byte()?;
|
||||
if type_of_value == TAG_END { break; }
|
||||
let size = self.read_short()? as usize;
|
||||
let key = String::from_utf8_lossy(&self.read_bytes(size)?).to_string();
|
||||
map.insert(key, self.read_value(type_of_value)?);
|
||||
}
|
||||
Ok(Tag::Compound(map))
|
||||
}
|
||||
|
||||
fn read_list(&mut self) -> Result<Tag, DataError> {
|
||||
let type_of_list = self.read_byte()?;
|
||||
let size_of_list = self.read_signed_int()?;
|
||||
let mut list = Vec::new();
|
||||
if size_of_list <= 0 { return Ok(Tag::List(list)); };
|
||||
for _ in 0..size_of_list {
|
||||
list.push(self.read_value(type_of_list)?);
|
||||
}
|
||||
Ok(Tag::List(list))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NbtReader {
|
||||
fn read_nbt(&mut self) -> Result<Tag, DataError>;
|
||||
}
|
||||
|
||||
impl NbtReader for Buffer {
|
||||
fn read_nbt(&mut self) -> Result<Tag, DataError> {
|
||||
|
||||
if self.read_byte()? != TAG_COMPOUND {
|
||||
return Err(DataError::NBTError);
|
||||
}
|
||||
|
||||
if self.read_short()? != 0 {
|
||||
let _ = self.seek_relative(-2);
|
||||
}
|
||||
|
||||
self.read_compound()
|
||||
}
|
||||
}
|
16
src/nbt/tags.rs
Normal file
16
src/nbt/tags.rs
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
|
||||
pub const TAG_END: u8 = 0;
|
||||
pub const TAG_BYTE: u8 = 1;
|
||||
pub const TAG_SHORT: u8 = 2;
|
||||
pub const TAG_INT: u8 = 3;
|
||||
pub const TAG_LONG: u8 = 4;
|
||||
pub const TAG_FLOAT: u8 = 5;
|
||||
pub const TAG_DOUBLE: u8 = 6;
|
||||
pub const TAG_BYTE_ARRAY: u8 = 7;
|
||||
pub const TAG_STRING: u8 = 8;
|
||||
pub const TAG_LIST: u8 = 9;
|
||||
pub const TAG_COMPOUND: u8 = 10;
|
||||
pub const TAG_INT_ARRAY: u8 = 11;
|
||||
pub const TAG_LONG_ARRAY: u8 = 12;
|
109
src/nbt/writer.rs
Normal file
109
src/nbt/writer.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::data::{Buffer, DataError, Writer};
|
||||
|
||||
use super::{tags::*, Tag};
|
||||
|
||||
|
||||
|
||||
fn get_id(tag: &Tag) -> u8 {
|
||||
match tag {
|
||||
Tag::End => TAG_END,
|
||||
Tag::Byte(_) => TAG_BYTE,
|
||||
Tag::Short(_) => TAG_SHORT,
|
||||
Tag::Int(_) => TAG_INT,
|
||||
Tag::Long(_) => TAG_LONG,
|
||||
Tag::Float(_) => TAG_FLOAT,
|
||||
Tag::Double(_) => TAG_DOUBLE,
|
||||
Tag::ByteArray(_) => TAG_BYTE_ARRAY,
|
||||
Tag::String(_) => TAG_STRING,
|
||||
Tag::List(_) => TAG_LIST,
|
||||
Tag::Compound(_) => TAG_COMPOUND,
|
||||
Tag::IntArray(_) => TAG_INT_ARRAY,
|
||||
Tag::LongArray(_) => TAG_LONG_ARRAY,
|
||||
}
|
||||
}
|
||||
|
||||
trait NbtWriterInternal {
|
||||
fn write_value(&mut self, tag: Tag) -> Result<(), DataError>;
|
||||
fn write_compound(&mut self, map: HashMap<String, Tag>) -> Result<(), DataError>;
|
||||
fn write_list(&mut self, list: Vec<Tag>) -> Result<(), DataError>;
|
||||
}
|
||||
|
||||
impl NbtWriterInternal for Buffer {
|
||||
fn write_value(&mut self, tag: Tag) -> Result<(), DataError> {
|
||||
match tag {
|
||||
Tag::End => self.write_byte(0)?,
|
||||
Tag::Byte(value) => self.write_signed_byte(value)?,
|
||||
Tag::Short(value) => self.write_signed_short(value)?,
|
||||
Tag::Int(value) => self.write_signed_int(value)?,
|
||||
Tag::Long(value) => self.write_signed_long(value)?,
|
||||
Tag::Float(value) => self.write_float(value)?,
|
||||
Tag::Double(value) => self.write_double(value)?,
|
||||
Tag::ByteArray(value) => {
|
||||
self.write_int(value.len() as u32)?;
|
||||
let data: Vec<u8> = value.into_iter().map(|x| x as u8).collect();
|
||||
self.write_bytes(&data)?;
|
||||
}
|
||||
Tag::String(value) => {
|
||||
self.write_short(value.len() as u16)?;
|
||||
self.write_bytes(value.as_bytes())?;
|
||||
}
|
||||
Tag::List(list) => self.write_list(list)?,
|
||||
Tag::Compound(map) => self.write_compound(map)?,
|
||||
Tag::IntArray(list) => {
|
||||
self.write_int(list.len() as u32)?;
|
||||
for value in list {
|
||||
self.write_signed_int(value)?;
|
||||
}
|
||||
}
|
||||
Tag::LongArray(list) => {
|
||||
self.write_int(list.len() as u32)?;
|
||||
for value in list {
|
||||
self.write_signed_long(value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_compound(&mut self, map: HashMap<String, Tag>) -> Result<(), DataError> {
|
||||
for (key, tag) in map {
|
||||
self.write_byte(get_id(&tag))?;
|
||||
self.write_short(key.len() as u16)?;
|
||||
self.write_bytes(key.as_bytes())?;
|
||||
self.write_value(tag)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_list(&mut self, list: Vec<Tag>) -> Result<(), DataError> {
|
||||
let type_of_list = match list.as_slice() {
|
||||
[] => 0, [first, rest @ ..] => {
|
||||
let first_id = get_id(first);
|
||||
if rest.iter().all(|tag| get_id(tag) == first_id) { first_id }
|
||||
else { return Err(DataError::NBTError) }
|
||||
}
|
||||
};
|
||||
self.write_byte(type_of_list)?;
|
||||
self.write_int(list.len() as u32)?;
|
||||
for tag in list {
|
||||
self.write_value(tag)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NbtWriter {
|
||||
fn write_nbt(&mut self, tag: Tag, to_file: bool) -> Result<(), DataError>;
|
||||
}
|
||||
|
||||
impl NbtWriter for Buffer {
|
||||
fn write_nbt(&mut self, tag: Tag, to_file: bool) -> Result<(), DataError> {
|
||||
let Tag::Compound(map) = tag else { return Err(DataError::NBTError); };
|
||||
self.write_byte(TAG_COMPOUND)?;
|
||||
if to_file { self.write_short(0)? };
|
||||
self.write_compound(map)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user