Start implementing Play state up to chunk data
This commit is contained in:
parent
4849a7903d
commit
b12f612c62
90
Cargo.lock
generated
90
Cargo.lock
generated
@ -2,6 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
@ -109,11 +121,11 @@ dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"ctrlc",
|
||||
"fastnbt",
|
||||
"fern",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"quartz_nbt",
|
||||
"radix64",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -123,6 +135,15 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.1"
|
||||
@ -133,18 +154,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastnbt"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adbc536576ecb712740a53d5bf4c64f38ee0ceb221f3f90980919e7506e38cb7"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.1"
|
||||
@ -155,6 +164,18 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.21"
|
||||
@ -305,6 +326,15 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.2"
|
||||
@ -429,6 +459,31 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quartz_nbt"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348031720b71761481d77969dcb3c89ab06f04132ee1503aca1bd9313eef5e67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
"flate2",
|
||||
"quartz_nbt_macros",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quartz_nbt_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "289baa0c8a4d1f840d2de528a7f8c29e0e9af48b3018172b3edad4f716e8daed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
@ -478,15 +533,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
|
@ -11,7 +11,7 @@ build = "build.rs"
|
||||
async-trait = "0.1.48"
|
||||
chrono = "0.4.13"
|
||||
ctrlc = "3.1.8"
|
||||
fastnbt = "*"
|
||||
# fastnbt = "*"
|
||||
fern = {version = "0.6", features = ["colored"]}
|
||||
futures = "0.3.13"
|
||||
lazy_static = "1.4.0"
|
||||
@ -23,6 +23,7 @@ tokio = {version = "1", features = ["full"]}
|
||||
toml = "0.5"
|
||||
uuid = "0.8.2"
|
||||
substring = "1.4.5"
|
||||
quartz_nbt = {version = "0.2.6", features = ["serde"]}
|
||||
|
||||
# colorful = "0.2.1"
|
||||
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)
|
||||
|
@ -1,5 +1,5 @@
|
||||
favicon = "server-icon.png"
|
||||
max_players = 20
|
||||
motd = "Hello world!"
|
||||
max_players = 4294967295
|
||||
motd = "Hell world!"
|
||||
port = 25565
|
||||
log_level = "debug"
|
@ -63,7 +63,7 @@ pub mod prelude {
|
||||
pub use serde_json::json;
|
||||
pub use uuid::Uuid;
|
||||
pub type JSON = serde_json::Value;
|
||||
pub type NBT = fastnbt::Value;
|
||||
pub type NBT = quartz_nbt::NbtCompound;
|
||||
pub use std::collections::VecDeque;
|
||||
pub use substring::Substring;
|
||||
pub use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
@ -5,7 +5,11 @@ use std::time::Duration;
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
let ctrlc_rx = composition::init();
|
||||
info!("Starting {}", composition::CONFIG.server_version);
|
||||
info!(
|
||||
"Starting {} on port {}",
|
||||
composition::CONFIG.server_version,
|
||||
composition::CONFIG.port
|
||||
);
|
||||
let mut server = composition::start_server().await;
|
||||
info!("Done! Start took {:?}", composition::START_TIME.elapsed());
|
||||
|
||||
|
@ -8,8 +8,8 @@ pub fn parse_byte(data: &[u8]) -> ParseResult<i8> {
|
||||
Ok((value, 1))
|
||||
}
|
||||
}
|
||||
pub fn serialize_byte(num: i8) -> [u8; 1] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_byte(value: i8) -> [u8; 1] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_short(data: &[u8]) -> ParseResult<i16> {
|
||||
@ -20,8 +20,8 @@ pub fn parse_short(data: &[u8]) -> ParseResult<i16> {
|
||||
Ok((value, 2))
|
||||
}
|
||||
}
|
||||
pub fn serialize_short(num: i16) -> [u8; 2] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_short(value: i16) -> [u8; 2] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_int(data: &[u8]) -> ParseResult<i32> {
|
||||
@ -32,8 +32,8 @@ pub fn parse_int(data: &[u8]) -> ParseResult<i32> {
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
pub fn serialize_int(num: i32) -> [u8; 4] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_int(value: i32) -> [u8; 4] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_long(data: &[u8]) -> ParseResult<i64> {
|
||||
@ -46,8 +46,8 @@ pub fn parse_long(data: &[u8]) -> ParseResult<i64> {
|
||||
Ok((value, 8))
|
||||
}
|
||||
}
|
||||
pub fn serialize_long(num: i64) -> [u8; 8] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_long(value: i64) -> [u8; 8] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_varint(data: &[u8]) -> ParseResult<i32> {
|
||||
@ -90,6 +90,32 @@ pub fn serialize_varint(value: i32) -> Vec<u8> {
|
||||
output
|
||||
}
|
||||
|
||||
pub fn parse_float(data: &[u8]) -> ParseResult<f32> {
|
||||
if data.len() < 4 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = f32::from_be_bytes([data[0], data[1], data[2], data[3]]);
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
pub fn serialize_float(value: f32) -> [u8; 4] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_double(data: &[u8]) -> ParseResult<f64> {
|
||||
if data.len() < 8 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = f64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
pub fn serialize_double(value: f64) -> [u8; 8] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_unsigned_byte(data: &[u8]) -> ParseResult<u8> {
|
||||
if data.is_empty() {
|
||||
Err(ParseError::NotEnoughData)
|
||||
@ -98,8 +124,8 @@ pub fn parse_unsigned_byte(data: &[u8]) -> ParseResult<u8> {
|
||||
Ok((value, 1))
|
||||
}
|
||||
}
|
||||
pub fn serialize_unsigned_byte(num: u8) -> [u8; 1] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_unsigned_byte(value: u8) -> [u8; 1] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_unsigned_short(data: &[u8]) -> ParseResult<u16> {
|
||||
@ -110,8 +136,8 @@ pub fn parse_unsigned_short(data: &[u8]) -> ParseResult<u16> {
|
||||
Ok((value, 2))
|
||||
}
|
||||
}
|
||||
pub fn serialize_unsigned_short(num: u16) -> [u8; 2] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_unsigned_short(value: u16) -> [u8; 2] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_unsigned_int(data: &[u8]) -> ParseResult<u32> {
|
||||
@ -122,8 +148,8 @@ pub fn parse_unsigned_int(data: &[u8]) -> ParseResult<u32> {
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
pub fn serialize_unsigned_int(num: u32) -> [u8; 4] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_unsigned_int(value: u32) -> [u8; 4] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_unsigned_long(data: &[u8]) -> ParseResult<u64> {
|
||||
@ -136,8 +162,8 @@ pub fn parse_unsigned_long(data: &[u8]) -> ParseResult<u64> {
|
||||
Ok((value, 8))
|
||||
}
|
||||
}
|
||||
pub fn serialize_unsigned_long(num: u64) -> [u8; 8] {
|
||||
num.to_be_bytes()
|
||||
pub fn serialize_unsigned_long(value: u64) -> [u8; 8] {
|
||||
value.to_be_bytes()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -46,3 +46,47 @@ pub fn parse_json(data: &[u8]) -> ParseResult<JSON> {
|
||||
pub fn serialize_json(value: JSON) -> Vec<u8> {
|
||||
serialize_string(&serde_json::to_string(&value).expect("Could not serialize JSON"))
|
||||
}
|
||||
|
||||
pub fn parse_nbt(data: &[u8]) -> ParseResult<NBT> {
|
||||
use quartz_nbt::io::{read_nbt, Flavor};
|
||||
use std::io::Cursor;
|
||||
let mut data = Cursor::new(data);
|
||||
// let (value_string, offset) = parse_string(data)?;
|
||||
if let Ok(value) = read_nbt(&mut data, Flavor::Uncompressed) {
|
||||
Ok((value.0, data.position() as usize))
|
||||
} else {
|
||||
Err(ParseError::InvalidData)
|
||||
}
|
||||
}
|
||||
pub fn serialize_nbt(value: NBT) -> Vec<u8> {
|
||||
use quartz_nbt::io::{write_nbt, Flavor};
|
||||
// serialize_string(&fastnbt::to_string(&value).expect("Could not serialize JSON"))
|
||||
let mut out = vec![];
|
||||
write_nbt(&mut out, None, &value, Flavor::Uncompressed).expect("Could not serialize NBT");
|
||||
out
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Position {
|
||||
pub x: i32,
|
||||
pub y: i16,
|
||||
pub z: i32,
|
||||
}
|
||||
impl Position {
|
||||
pub fn new(x: i32, y: i16, z: i32) -> Position {
|
||||
Position { x, y, z }
|
||||
}
|
||||
pub fn parse(data: &[u8]) -> ParseResult<Position> {
|
||||
let (value, offset) = parse_unsigned_long(data)?;
|
||||
let x = (value >> 38) as i32;
|
||||
let y = (value & 0xFFF) as i16;
|
||||
let z = ((value >> 12) & 0x3FFFFFF) as i32;
|
||||
Ok((Position::new(x, y, z), offset))
|
||||
}
|
||||
pub fn serialize(&self) -> [u8; 8] {
|
||||
(((self.x as u64 & 0x3FFFFFF) << 38)
|
||||
| ((self.z as u64 & 0x3FFFFFF) << 12)
|
||||
| (self.y as u64 & 0xFFF))
|
||||
.to_be_bytes()
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ impl NetworkClient {
|
||||
}
|
||||
}
|
||||
pub async fn read_data(&mut self) -> Result<(), tokio::io::Error> {
|
||||
trace!("NetworkClient.read_data()");
|
||||
trace!("NetworkClient.read_data() id {}", self.id);
|
||||
// Try to read 4kb at a time until there is no more data.
|
||||
loop {
|
||||
let mut buf = [0; 4096];
|
||||
@ -45,6 +45,7 @@ impl NetworkClient {
|
||||
Ok(0) => break,
|
||||
// Data was read.
|
||||
Ok(n) => {
|
||||
trace!("Setting last_data_time for client {}", self.id);
|
||||
self.last_data_time = Instant::now();
|
||||
self.buffer.extend(&buf[0..n]);
|
||||
debug!("Read {} bytes from client {}", n, self.id);
|
||||
@ -58,7 +59,7 @@ impl NetworkClient {
|
||||
Ok(())
|
||||
}
|
||||
pub fn read_packet(&mut self) -> Result<(), ParseError> {
|
||||
trace!("NetworkClient.read_packet()");
|
||||
trace!("NetworkClient.read_packet() id {}", self.id);
|
||||
self.buffer.make_contiguous();
|
||||
if let (data, &[]) = self.buffer.as_slices() {
|
||||
let mut offset = 0;
|
||||
@ -85,15 +86,6 @@ impl NetworkClient {
|
||||
self.stream.write(&bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
pub async fn update(&mut self) {
|
||||
// if self.state == NetworkClientState::Disconnected {
|
||||
// return Err(tokio::io::Error::from(tokio::io::ErrorKind::BrokenPipe));
|
||||
// } else if self.last_data_time.elapsed() > Duration::from_secs(10) {
|
||||
// return self.disconnect(tokio::io::ErrorKind::TimedOut);
|
||||
// }
|
||||
let _ = self.read_data().await;
|
||||
let _ = self.read_packet();
|
||||
}
|
||||
pub async fn disconnect(&mut self, reason: Option<JSON>) {
|
||||
if let Some(reason) = reason {
|
||||
if self.state == NetworkClientState::Login {
|
||||
|
@ -60,7 +60,42 @@ pub enum Packet {
|
||||
successful: bool,
|
||||
data: Option<Vec<u8>>,
|
||||
},
|
||||
|
||||
// Play
|
||||
CP14WindowItems {
|
||||
window_id: u8,
|
||||
state_id: i32,
|
||||
slots: Vec<NBT>,
|
||||
carried_item: NBT,
|
||||
},
|
||||
CP26JoinGame,
|
||||
CP48HeldItemChange,
|
||||
CP66DeclareRecipes,
|
||||
CP67Tags,
|
||||
CP1BEntityStatus,
|
||||
CP12DeclareCommands,
|
||||
CP39UnlockRecipes,
|
||||
CP22ChunkDataAndUpdateLight,
|
||||
CP38PlayerPositionAndLook {
|
||||
x: (f64, bool),
|
||||
y: (f64, bool),
|
||||
z: (f64, bool),
|
||||
yaw: (f32, bool),
|
||||
pitch: (f32, bool),
|
||||
teleport_id: i32,
|
||||
dismount_vehicle: bool,
|
||||
},
|
||||
CP36PlayerInfo,
|
||||
CP49UpdateViewPosition,
|
||||
CP25UpdateLight,
|
||||
CP4BSpawnPosition {
|
||||
location: Position,
|
||||
angle: f32,
|
||||
},
|
||||
CP00TeleportConfirm,
|
||||
|
||||
SP05ClientSettings,
|
||||
SP04ClientStatus,
|
||||
}
|
||||
impl Packet {
|
||||
pub fn parse_body(
|
||||
@ -193,15 +228,69 @@ impl Packet {
|
||||
),
|
||||
CS01Pong { payload } => (0x01, serialize_long(*payload).to_vec()),
|
||||
CL00Disconnect { reason } => (0x00, serialize_json(reason.clone())),
|
||||
// CL01EncryptionRequest
|
||||
CL02LoginSuccess { uuid, username } => (0x02, {
|
||||
let mut out = vec![];
|
||||
out.extend(uuid.to_be_bytes());
|
||||
out.extend(serialize_string(username));
|
||||
out
|
||||
}),
|
||||
// CL03SetCompression
|
||||
// CL04LoginPluginRequest
|
||||
CP14WindowItems {
|
||||
window_id,
|
||||
state_id,
|
||||
slots,
|
||||
carried_item,
|
||||
} => (0x14, {
|
||||
let mut out = vec![*window_id];
|
||||
out.extend(serialize_varint(*state_id));
|
||||
out.extend(serialize_varint(slots.len() as i32));
|
||||
for slot in slots {
|
||||
out.extend(serialize_nbt(slot.clone()));
|
||||
}
|
||||
out.extend(serialize_nbt(carried_item.clone()));
|
||||
out
|
||||
}),
|
||||
CP38PlayerPositionAndLook {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
yaw,
|
||||
pitch,
|
||||
teleport_id,
|
||||
dismount_vehicle,
|
||||
} => (0x38, {
|
||||
let mut out = vec![];
|
||||
out.extend(serialize_double(x.0));
|
||||
out.extend(serialize_double(y.0));
|
||||
out.extend(serialize_double(z.0));
|
||||
out.extend(serialize_float(yaw.0));
|
||||
out.extend(serialize_float(pitch.0));
|
||||
let mut flags = 0x00;
|
||||
if x.1 {
|
||||
flags |= 0x01;
|
||||
}
|
||||
if y.1 {
|
||||
flags |= 0x02;
|
||||
}
|
||||
if z.1 {
|
||||
flags |= 0x04;
|
||||
}
|
||||
if yaw.1 {
|
||||
flags |= 0x10;
|
||||
}
|
||||
if pitch.1 {
|
||||
flags |= 0x08;
|
||||
}
|
||||
out.push(flags);
|
||||
out.extend(serialize_varint(*teleport_id));
|
||||
out.extend(serialize_bool(*dismount_vehicle));
|
||||
out
|
||||
}),
|
||||
CP4BSpawnPosition { location, angle } => (0x4b, {
|
||||
let mut out = vec![];
|
||||
out.extend(location.serialize());
|
||||
out.extend(serialize_float(*angle));
|
||||
out
|
||||
}),
|
||||
_ => unimplemented!("Serializing unknown packet"),
|
||||
};
|
||||
let mut id_and_body = serialize_varint(id as i32);
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::{net::*, prelude::*};
|
||||
use crate::{
|
||||
net::{mctypes::Position, *},
|
||||
prelude::*,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use tokio::{
|
||||
net::{TcpListener, ToSocketAddrs},
|
||||
sync::mpsc::{self, error::TryRecvError, UnboundedReceiver},
|
||||
@ -52,6 +56,9 @@ impl Server {
|
||||
if self.clients[i].state == NetworkClientState::Disconnected {
|
||||
debug!("Removed client {}", self.clients[i].id);
|
||||
self.clients.remove(i);
|
||||
} else if self.clients[i].last_data_time.elapsed() > Duration::from_secs(10) {
|
||||
debug!("Client {} timed out", self.clients[i].id);
|
||||
self.clients[i].disconnect(None).await;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
@ -149,6 +156,32 @@ impl Server {
|
||||
})
|
||||
.await;
|
||||
client.state = NetworkClientState::Play;
|
||||
// Log them in.
|
||||
let _ = client
|
||||
.send_packet(CP4BSpawnPosition {
|
||||
location: Position::new(0, 0, 0),
|
||||
angle: 0.0,
|
||||
})
|
||||
.await;
|
||||
let _ = client
|
||||
.send_packet(CP14WindowItems {
|
||||
window_id: 0,
|
||||
state_id: 0,
|
||||
slots: vec![quartz_nbt::compound! {}; 44],
|
||||
carried_item: quartz_nbt::compound! {},
|
||||
})
|
||||
.await;
|
||||
let _ = client
|
||||
.send_packet(CP38PlayerPositionAndLook {
|
||||
x: (0.0, false),
|
||||
y: (0.0, false),
|
||||
z: (0.0, false),
|
||||
yaw: (0.0, false),
|
||||
pitch: (0.0, false),
|
||||
teleport_id: 0,
|
||||
dismount_vehicle: false,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
_ => unimplemented!("Handling unknown packet"),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user