Rewrite packet
This commit is contained in:
parent
6bd701b41a
commit
56b645df18
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -1,5 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
@ -40,6 +42,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.0.1"
|
||||
@ -52,6 +60,12 @@ version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
@ -95,6 +109,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"ctrlc",
|
||||
"fastnbt",
|
||||
"fern",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
@ -117,6 +132,17 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastnbt"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a5f728dc3e8c3154312f657d6cc9afd081b8c1becddc62ed67448e0a19adfd"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.0"
|
||||
|
@ -20,6 +20,8 @@ lazy_static = "1.4.0"
|
||||
ctrlc = "3.1.8"
|
||||
futures = "0.3.13"
|
||||
uuid = "0.8.2"
|
||||
fastnbt = "*"
|
||||
|
||||
# colorful = "0.2.1"
|
||||
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)
|
||||
# toml = "0.5.6"
|
||||
|
@ -1 +0,0 @@
|
||||
pub mod player;
|
@ -1,38 +0,0 @@
|
||||
use crate::world::location::Location;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Player {
|
||||
pub position: Location,
|
||||
pub uuid: Uuid,
|
||||
names: (String, Option<String>),
|
||||
}
|
||||
impl Player {
|
||||
pub fn new() -> Player {
|
||||
Player {
|
||||
position: Location::zero(),
|
||||
uuid: Uuid::nil(),
|
||||
names: ("Player".into(), None),
|
||||
}
|
||||
}
|
||||
pub fn username(&self) -> &String {
|
||||
&self.names.0
|
||||
}
|
||||
pub fn username_mut(&mut self) -> &mut String {
|
||||
&mut self.names.0
|
||||
}
|
||||
pub fn display_name(&self) -> &String {
|
||||
match &self.names.1 {
|
||||
Some(name) => name,
|
||||
None => &self.names.0,
|
||||
}
|
||||
}
|
||||
pub fn display_name_mut(&mut self) -> &mut Option<String> {
|
||||
&mut self.names.1
|
||||
}
|
||||
}
|
||||
impl Default for Player {
|
||||
fn default() -> Self {
|
||||
Player::new()
|
||||
}
|
||||
}
|
30
src/lib.rs
30
src/lib.rs
@ -1,17 +1,11 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
/// Data types for every entity in the game.
|
||||
pub mod entity;
|
||||
/// Data types needed for the Minecraft protocol.
|
||||
pub mod mctypes;
|
||||
/// The logic for the server.
|
||||
pub mod net;
|
||||
pub mod server;
|
||||
/// Data types for blocks, chunks, dimensions, and world files.
|
||||
pub mod world;
|
||||
|
||||
use log::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::prelude::*;
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -23,7 +17,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: Config = {
|
||||
pub static ref CONFIG: Config = {
|
||||
let config_from_file = || -> std::io::Result<Config> {
|
||||
use std::{fs::File, io::prelude::*};
|
||||
let mut data = String::new();
|
||||
@ -50,7 +44,7 @@ lazy_static! {
|
||||
}
|
||||
}
|
||||
};
|
||||
static ref FAVICON: std::io::Result<Vec<u8>> = {
|
||||
pub static ref FAVICON: std::io::Result<Vec<u8>> = {
|
||||
use std::{fs::File, io::prelude::*};
|
||||
let mut data = vec![];
|
||||
let mut file = File::open(CONFIG.favicon.clone())?;
|
||||
@ -97,3 +91,19 @@ pub fn init() -> Receiver<()> {
|
||||
pub async fn start_server() -> server::Server {
|
||||
server::Server::new(format!("0.0.0.0:{}", CONFIG.port))
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{mctypes::*, CONFIG, FAVICON};
|
||||
pub use log::*;
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
pub type JSON = serde_json::Value;
|
||||
pub type NBT = fastnbt::Value;
|
||||
pub use std::collections::VecDeque;
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ParseError {
|
||||
NotEnoughData,
|
||||
InvalidData,
|
||||
VarIntTooBig,
|
||||
}
|
||||
pub type ParseResult<T> = Result<(T, usize), ParseError>;
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
/// Read a single byte from the given `TcpStream`.
|
||||
pub async fn read_byte(t: &mut TcpStream) -> tokio::io::Result<u8> {
|
||||
let mut buffer = [0u8; 1];
|
||||
t.read_exact(&mut buffer).await?;
|
||||
Ok(buffer[0])
|
||||
}
|
||||
/// Read `l` bytes from the given `TcpStream`.
|
||||
pub async fn read_bytes(t: &mut TcpStream, l: usize) -> tokio::io::Result<Vec<u8>> {
|
||||
let mut buffer = vec![];
|
||||
for _ in 0..l {
|
||||
buffer.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(buffer)
|
||||
}
|
||||
/// Write a single byte to the given `TcpStream`.
|
||||
pub async fn write_byte(t: &mut TcpStream, value: u8) -> tokio::io::Result<()> {
|
||||
t.write(&[value]).await?;
|
||||
Ok(())
|
||||
}
|
||||
/// Write multiple bytes to the given `TcpStream`.
|
||||
pub async fn write_bytes(t: &mut TcpStream, bytes: &[u8]) -> tokio::io::Result<()> {
|
||||
for b in bytes {
|
||||
write_byte(t, *b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// Take `l` bytes from the given `Vec<u8>`.
|
||||
pub fn get_bytes(v: Vec<u8>, l: usize) -> Box<[u8]> {
|
||||
use std::collections::VecDeque;
|
||||
let mut v = VecDeque::from(v);
|
||||
while v.len() > l {
|
||||
v.pop_front();
|
||||
}
|
||||
while v.len() < l {
|
||||
v.push_front(0u8);
|
||||
}
|
||||
let mut a = Vec::new();
|
||||
for b in v {
|
||||
a.push(b);
|
||||
}
|
||||
a.into_boxed_slice()
|
||||
}
|
||||
/// Makes returning errors shorter.
|
||||
pub fn io_error(s: &str) -> tokio::io::Error {
|
||||
use tokio::io::{Error, ErrorKind};
|
||||
Error::new(ErrorKind::Other, s)
|
||||
}
|
@ -1,41 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Helper functions.
|
||||
pub mod functions;
|
||||
/// All the numbers, from `i8` and `u8` to `i64` and `u64`, plus `VarInt`s.
|
||||
pub mod numbers;
|
||||
/// The other types, (booleans and strings).
|
||||
pub mod other;
|
||||
|
||||
pub use functions::*;
|
||||
pub use numbers::*;
|
||||
pub use other::*;
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
clone::Clone,
|
||||
convert::{Into, TryFrom},
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
};
|
||||
|
||||
// /// Make sure all types can serialize and deserialize to/from `Vec<u8>`.
|
||||
// pub trait MCType: Into<Vec<u8>> + TryFrom<Vec<u8>> + Display {
|
||||
// pub async fn read(_stream: &mut TcpStream) -> tokio::io::Result<Self>;
|
||||
// }
|
||||
|
||||
pub enum MCTypeError {
|
||||
NetworkError(tokio::io::Error),
|
||||
NotEnoughData,
|
||||
ParseError,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait MCType: Into<Vec<u8>> + TryFrom<Vec<u8>> + Clone + Debug + Display {
|
||||
fn to_bytes(&self) -> Vec<u8>;
|
||||
fn from_bytes(bytes: Vec<u8>) -> Result<Self, MCTypeError>;
|
||||
async fn read(t: &mut TcpStream) -> Result<Self, MCTypeError>;
|
||||
async fn write(t: &mut TcpStream) -> tokio::io::Result<()>;
|
||||
}
|
||||
|
@ -1,736 +1,187 @@
|
||||
use super::*;
|
||||
use std::convert::{From, Into, TryFrom};
|
||||
use std::fmt::Display;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The equivalent of an `i8`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCByte {
|
||||
pub value: i8, // -128 to 127
|
||||
}
|
||||
impl MCByte {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCByte> {
|
||||
Ok(MCByte::from_bytes(vec![read_byte(t).await?]))
|
||||
pub fn parse_byte(data: &[u8]) -> ParseResult<i8> {
|
||||
if data.is_empty() {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = i8::from_be_bytes([data[0]]);
|
||||
Ok((value, 1))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCByte {
|
||||
MCByte {
|
||||
value: get_bytes(v, 1)[0] as i8,
|
||||
}
|
||||
pub fn serialize_byte(num: i8) -> [u8; 1] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_short(data: &[u8]) -> ParseResult<i16> {
|
||||
if data.len() < 2 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = i16::from_be_bytes([data[0], data[1]]);
|
||||
Ok((value, 2))
|
||||
}
|
||||
}
|
||||
pub fn serialize_short(num: i16) -> [u8; 2] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_int(data: &[u8]) -> ParseResult<i32> {
|
||||
if data.len() < 4 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = i32::from_be_bytes([data[0], data[1], data[2], data[3]]);
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
pub fn serialize_int(num: i32) -> [u8; 4] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_long(data: &[u8]) -> ParseResult<i64> {
|
||||
if data.len() < 8 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = i64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
Ok((value, 8))
|
||||
}
|
||||
}
|
||||
pub fn serialize_long(num: i64) -> [u8; 8] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
pub fn parse_varint(data: &[u8]) -> ParseResult<i32> {
|
||||
let mut offset = 0;
|
||||
let mut output = 0i32;
|
||||
let mut bytes_read = 0i32;
|
||||
|
||||
loop {
|
||||
if data.len() <= offset {
|
||||
return Err(ParseError::NotEnoughData);
|
||||
}
|
||||
|
||||
output |= (((data[offset] & 0x7f) as i32) << bytes_read * 7) as i32;
|
||||
bytes_read += 1;
|
||||
if data[offset] & 0x80 != 0x80 {
|
||||
break;
|
||||
}
|
||||
offset += 1;
|
||||
if bytes_read >= 5 {
|
||||
return Err(ParseError::VarIntTooBig);
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
|
||||
Ok((output, bytes_read as usize))
|
||||
}
|
||||
impl From<i8> for MCByte {
|
||||
fn from(v: i8) -> MCByte {
|
||||
MCByte { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<i8> for MCByte {
|
||||
fn into(self) -> i8 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<i8> for MCByte {
|
||||
fn eq(&self, other: &i8) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCByte {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCByte {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.is_empty() {
|
||||
Err("Not enough bytes")
|
||||
pub fn serialize_varint(value: i32) -> Vec<u8> {
|
||||
println!("serializing {}", value);
|
||||
let mut value = value as u32;
|
||||
let mut output = vec![];
|
||||
loop {
|
||||
let data = (value & 0x7f) as u8;
|
||||
value >>= 7;
|
||||
println!("\tvalue: {}", value);
|
||||
|
||||
if value == 0 {
|
||||
output.push(data);
|
||||
break;
|
||||
} else {
|
||||
let mut a = [0u8; 1];
|
||||
a.copy_from_slice(&get_bytes(bytes, 1));
|
||||
Ok(MCByte {
|
||||
value: i8::from_be_bytes(a),
|
||||
})
|
||||
output.push(data | 0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCByte {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// The equivalent of a `u8`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCUnsignedByte {
|
||||
pub value: u8, // 0 to 255
|
||||
}
|
||||
impl MCUnsignedByte {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCUnsignedByte> {
|
||||
Ok(MCUnsignedByte::from_bytes(vec![read_byte(t).await?]))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCUnsignedByte {
|
||||
MCUnsignedByte {
|
||||
value: get_bytes(v, 1)[0],
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
pub fn parse_unsigned_byte(data: &[u8]) -> ParseResult<u8> {
|
||||
if data.is_empty() {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = u8::from_be_bytes([data[0]]);
|
||||
Ok((value, 1))
|
||||
}
|
||||
}
|
||||
impl From<u8> for MCUnsignedByte {
|
||||
fn from(v: u8) -> MCUnsignedByte {
|
||||
MCUnsignedByte { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<u8> for MCUnsignedByte {
|
||||
fn into(self) -> u8 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<u8> for MCUnsignedByte {
|
||||
fn eq(&self, other: &u8) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCUnsignedByte {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCUnsignedByte {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.is_empty() {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 1];
|
||||
a.copy_from_slice(&get_bytes(bytes, 1));
|
||||
Ok(MCUnsignedByte {
|
||||
value: u8::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCUnsignedByte {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
pub fn serialize_unsigned_byte(num: u8) -> [u8; 1] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
/// The equivalent of an `i16`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCShort {
|
||||
pub value: i16, // -32768 to 32767
|
||||
}
|
||||
impl MCShort {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCShort> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.push(read_byte(t).await?); // MSD
|
||||
bytes.push(read_byte(t).await?); // LSD
|
||||
Ok(MCShort::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCShort {
|
||||
let mut a = [0u8; 2];
|
||||
a.copy_from_slice(&get_bytes(v, 2));
|
||||
MCShort {
|
||||
value: i16::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
pub fn parse_unsigned_short(data: &[u8]) -> ParseResult<u16> {
|
||||
if data.len() < 2 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = u16::from_be_bytes([data[0], data[1]]);
|
||||
Ok((value, 2))
|
||||
}
|
||||
}
|
||||
impl From<i16> for MCShort {
|
||||
fn from(v: i16) -> MCShort {
|
||||
MCShort { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<i16> for MCShort {
|
||||
fn into(self) -> i16 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<i16> for MCShort {
|
||||
fn eq(&self, other: &i16) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCShort {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCShort {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 2 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 2];
|
||||
a.copy_from_slice(&get_bytes(bytes, 2));
|
||||
Ok(MCShort {
|
||||
value: i16::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCShort {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
pub fn serialize_unsigned_short(num: u16) -> [u8; 2] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
/// The equivalent of a `u16`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCUnsignedShort {
|
||||
pub value: u16, // 0 to 65535
|
||||
}
|
||||
impl MCUnsignedShort {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCUnsignedShort> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.push(read_byte(t).await?); // MSD
|
||||
bytes.push(read_byte(t).await?); // LSD
|
||||
Ok(MCUnsignedShort::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCUnsignedShort {
|
||||
let mut a = [0u8; 2];
|
||||
a.copy_from_slice(&get_bytes(v, 2));
|
||||
MCUnsignedShort {
|
||||
value: u16::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
pub fn parse_unsigned_int(data: &[u8]) -> ParseResult<u32> {
|
||||
if data.len() < 4 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
|
||||
Ok((value, 4))
|
||||
}
|
||||
}
|
||||
impl From<u16> for MCUnsignedShort {
|
||||
fn from(v: u16) -> MCUnsignedShort {
|
||||
MCUnsignedShort { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<u16> for MCUnsignedShort {
|
||||
fn into(self) -> u16 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<u16> for MCUnsignedShort {
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCUnsignedShort {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCUnsignedShort {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 2 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 2];
|
||||
a.copy_from_slice(&get_bytes(bytes, 2));
|
||||
Ok(MCUnsignedShort {
|
||||
value: u16::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCUnsignedShort {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
pub fn serialize_unsigned_int(num: u32) -> [u8; 4] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
/// The equivalent of an `i32`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCInt {
|
||||
pub value: i32, // -2147483648 to 2147483647
|
||||
}
|
||||
impl MCInt {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCInt> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..4 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCInt::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCInt {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(v, 4));
|
||||
MCInt {
|
||||
value: i32::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
pub fn parse_unsigned_long(data: &[u8]) -> ParseResult<u64> {
|
||||
if data.len() < 8 {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
let value = u64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
Ok((value, 8))
|
||||
}
|
||||
}
|
||||
impl From<i32> for MCInt {
|
||||
fn from(v: i32) -> MCInt {
|
||||
MCInt { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<i32> for MCInt {
|
||||
fn into(self) -> i32 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<i32> for MCInt {
|
||||
fn eq(&self, other: &i32) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCInt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCInt {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 4 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(bytes, 4));
|
||||
Ok(MCInt {
|
||||
value: i32::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCInt {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
pub fn serialize_unsigned_long(num: u64) -> [u8; 8] {
|
||||
num.to_be_bytes()
|
||||
}
|
||||
|
||||
/// The equivalent of a `u32`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCUnsignedInt {
|
||||
pub value: u32, // 0 to 4294967295
|
||||
}
|
||||
impl MCUnsignedInt {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCUnsignedInt> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..4 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCUnsignedInt::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCUnsignedInt {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(v, 4));
|
||||
MCUnsignedInt {
|
||||
value: u32::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
impl From<u32> for MCUnsignedInt {
|
||||
fn from(v: u32) -> MCUnsignedInt {
|
||||
MCUnsignedInt { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<u32> for MCUnsignedInt {
|
||||
fn into(self) -> u32 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<u32> for MCUnsignedInt {
|
||||
fn eq(&self, other: &u32) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCUnsignedInt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCUnsignedInt {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 4 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(bytes, 4));
|
||||
Ok(MCUnsignedInt {
|
||||
value: u32::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCUnsignedInt {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// The equivalent of an `864`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCLong {
|
||||
pub value: i64, // -9223372036854775808 to 9223372036854775807
|
||||
}
|
||||
impl MCLong {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCLong> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..8 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCLong::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCLong {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(v, 8));
|
||||
MCLong {
|
||||
value: i64::from_be_bytes(a),
|
||||
#[test]
|
||||
fn parse_varint_works() {
|
||||
let tests = vec![
|
||||
(Ok((0, 1)), vec![0x00]),
|
||||
(Ok((1, 1)), vec![0x01]),
|
||||
(Ok((2, 1)), vec![0x02]),
|
||||
(Ok((127, 1)), vec![0x7f]),
|
||||
(Ok((128, 2)), vec![0x80, 0x01]),
|
||||
(Ok((255, 2)), vec![0xff, 0x01]),
|
||||
(Ok((25565, 3)), vec![0xdd, 0xc7, 0x01]),
|
||||
(Ok((2097151, 3)), vec![0xff, 0xff, 0x7f]),
|
||||
(Ok((2147483647, 5)), vec![0xff, 0xff, 0xff, 0xff, 0x07]),
|
||||
(Ok((-1, 5)), vec![0xff, 0xff, 0xff, 0xff, 0x0f]),
|
||||
(Ok((-2147483648, 5)), vec![0x80, 0x80, 0x80, 0x80, 0x08]),
|
||||
];
|
||||
for test in &tests {
|
||||
assert_eq!(test.0, parse_varint(&test.1));
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
impl From<i64> for MCLong {
|
||||
fn from(v: i64) -> MCLong {
|
||||
MCLong { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<i64> for MCLong {
|
||||
fn into(self) -> i64 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<i64> for MCLong {
|
||||
fn eq(&self, other: &i64) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCLong {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCLong {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 8 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(bytes, 8));
|
||||
Ok(MCLong {
|
||||
value: i64::from_be_bytes(a),
|
||||
})
|
||||
#[test]
|
||||
fn serialize_varint_works() {
|
||||
let tests = vec![
|
||||
(0, vec![0x00]),
|
||||
(1, vec![0x01]),
|
||||
(2, vec![0x02]),
|
||||
(127, vec![0x7f]),
|
||||
(128, vec![0x80, 0x01]),
|
||||
(255, vec![0xff, 0x01]),
|
||||
(25565, vec![0xdd, 0xc7, 0x01]),
|
||||
(2097151, vec![0xff, 0xff, 0x7f]),
|
||||
(2147483647, vec![0xff, 0xff, 0xff, 0xff, 0x07]),
|
||||
(-1, vec![0xff, 0xff, 0xff, 0xff, 0x0f]),
|
||||
(-2147483648, vec![0x80, 0x80, 0x80, 0x80, 0x08]),
|
||||
];
|
||||
for test in &tests {
|
||||
assert_eq!(serialize_varint(test.0), test.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCLong {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// The equivalent of a `u64`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCUnsignedLong {
|
||||
pub value: u64, // 0 to 18446744073709551615
|
||||
}
|
||||
impl MCUnsignedLong {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCUnsignedLong> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..8 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCUnsignedLong::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCUnsignedLong {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(v, 8));
|
||||
MCUnsignedLong {
|
||||
value: u64::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
impl From<u64> for MCUnsignedLong {
|
||||
fn from(v: u64) -> MCUnsignedLong {
|
||||
MCUnsignedLong { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<u64> for MCUnsignedLong {
|
||||
fn into(self) -> u64 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<u64> for MCUnsignedLong {
|
||||
fn eq(&self, other: &u64) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCUnsignedLong {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCUnsignedLong {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 8 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(bytes, 8));
|
||||
Ok(MCUnsignedLong {
|
||||
value: u64::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCUnsignedLong {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// The equivalent of a `f32`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCFloat {
|
||||
pub value: f32, // 32-bit floating point number
|
||||
}
|
||||
impl MCFloat {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCFloat> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..4 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCFloat::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCFloat {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(v, 4));
|
||||
MCFloat {
|
||||
value: f32::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
impl From<f32> for MCFloat {
|
||||
fn from(v: f32) -> MCFloat {
|
||||
MCFloat { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<f32> for MCFloat {
|
||||
fn into(self) -> f32 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<f32> for MCFloat {
|
||||
fn eq(&self, other: &f32) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCFloat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCFloat {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 4 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 4];
|
||||
a.copy_from_slice(&get_bytes(bytes, 4));
|
||||
Ok(MCFloat {
|
||||
value: f32::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCFloat {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// The equivalent of a `f64`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCDouble {
|
||||
pub value: f64, // 64-bit floating point number
|
||||
}
|
||||
impl MCDouble {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCDouble> {
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..8 {
|
||||
bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCDouble::from_bytes(bytes))
|
||||
}
|
||||
pub fn from_bytes(v: Vec<u8>) -> MCDouble {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(v, 8));
|
||||
MCDouble {
|
||||
value: f64::from_be_bytes(a),
|
||||
}
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
impl From<f64> for MCDouble {
|
||||
fn from(v: f64) -> MCDouble {
|
||||
MCDouble { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<f64> for MCDouble {
|
||||
fn into(self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<f64> for MCDouble {
|
||||
fn eq(&self, other: &f64) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCDouble {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCDouble {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 8 {
|
||||
Err("Not enough bytes")
|
||||
} else {
|
||||
let mut a = [0u8; 8];
|
||||
a.copy_from_slice(&get_bytes(bytes, 8));
|
||||
Ok(MCDouble {
|
||||
value: f64::from_be_bytes(a),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCDouble {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.value.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// A variable-length integer.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct MCVarInt {
|
||||
pub value: i32, // Variable length 32-bit integer
|
||||
}
|
||||
impl MCVarInt {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCVarInt> {
|
||||
let mut num_read = 0;
|
||||
let mut result = 0i128;
|
||||
let mut read = 0u8;
|
||||
let mut run_once = false;
|
||||
while (read & 0b10000000) != 0 || !run_once {
|
||||
run_once = true;
|
||||
read = read_byte(t).await?;
|
||||
let value = (read & 0b01111111) as i128;
|
||||
result |= value << (7 * num_read);
|
||||
num_read += 1;
|
||||
if num_read > 5 {
|
||||
return Err(io_error("MCVarInt is too big"));
|
||||
}
|
||||
}
|
||||
Ok(MCVarInt {
|
||||
value: result as i32,
|
||||
})
|
||||
}
|
||||
pub fn from_bytes(_v: Vec<u8>) -> MCVarInt {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
Into::<Vec<u8>>::into(*self)
|
||||
}
|
||||
}
|
||||
impl From<u8> for MCVarInt {
|
||||
fn from(v: u8) -> MCVarInt {
|
||||
MCVarInt { value: v as i32 }
|
||||
}
|
||||
}
|
||||
impl Into<u8> for MCVarInt {
|
||||
fn into(self) -> u8 {
|
||||
self.value as u8
|
||||
}
|
||||
}
|
||||
impl PartialEq<u8> for MCVarInt {
|
||||
fn eq(&self, other: &u8) -> bool {
|
||||
self.value == *other as i32
|
||||
}
|
||||
}
|
||||
impl From<i32> for MCVarInt {
|
||||
fn from(v: i32) -> MCVarInt {
|
||||
MCVarInt { value: v }
|
||||
}
|
||||
}
|
||||
impl Into<i32> for MCVarInt {
|
||||
fn into(self) -> i32 {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<i32> for MCVarInt {
|
||||
fn eq(&self, other: &i32) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl Display for MCVarInt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCVarInt {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
let mut num_read = 0;
|
||||
let mut result: i32 = 0;
|
||||
loop {
|
||||
let value = bytes[num_read] & 0b01111111;
|
||||
result |= (value << (7 * num_read)) as i32;
|
||||
if bytes[num_read] & 0b10000000 == 0x00 {
|
||||
break;
|
||||
}
|
||||
num_read += 1;
|
||||
if num_read == bytes.len() {
|
||||
return Err("Not enough bytes");
|
||||
}
|
||||
if num_read > 5 {
|
||||
return Err("VarInt is too big");
|
||||
}
|
||||
}
|
||||
Ok(MCVarInt { value: result })
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCVarInt {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut value = self.value;
|
||||
let mut run_once = false;
|
||||
let mut out: Vec<u8> = Vec::new();
|
||||
while value != 0 || !run_once {
|
||||
run_once = true;
|
||||
let mut temp: u8 = (value & 0b01111111) as u8;
|
||||
value >>= 7;
|
||||
if value != 0 {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
out.push(temp);
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
@ -1,293 +1,43 @@
|
||||
use super::*;
|
||||
use std::convert::{From, Into, TryFrom};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The equivalent of a `bool`.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum MCBoolean {
|
||||
True,
|
||||
False,
|
||||
}
|
||||
impl From<bool> for MCBoolean {
|
||||
fn from(v: bool) -> MCBoolean {
|
||||
if v {
|
||||
MCBoolean::True
|
||||
} else {
|
||||
MCBoolean::False
|
||||
}
|
||||
pub fn parse_bool(data: &[u8]) -> ParseResult<bool> {
|
||||
if data.is_empty() {
|
||||
Err(ParseError::NotEnoughData)
|
||||
} else {
|
||||
Ok((data[0] == 1, 1))
|
||||
}
|
||||
}
|
||||
impl Into<bool> for MCBoolean {
|
||||
fn into(self) -> bool {
|
||||
match self {
|
||||
MCBoolean::True => true,
|
||||
MCBoolean::False => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for MCBoolean {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
MCBoolean::True => "true",
|
||||
MCBoolean::False => "false",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCBoolean {
|
||||
type Error = &'static str;
|
||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.is_empty() {
|
||||
Err("Not enough bytes")
|
||||
} else if bytes[0] == 1u8 {
|
||||
Ok(MCBoolean::True)
|
||||
} else {
|
||||
Ok(MCBoolean::False)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCBoolean {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
MCBoolean::True => vec![0x01],
|
||||
MCBoolean::False => vec![0x00],
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MCBoolean {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<MCBoolean> {
|
||||
let b = read_byte(t).await?;
|
||||
Ok(MCBoolean::try_from(vec![b]).unwrap())
|
||||
pub fn serialize_bool(value: bool) -> [u8; 1] {
|
||||
if value {
|
||||
[0x01]
|
||||
} else {
|
||||
[0x00]
|
||||
}
|
||||
}
|
||||
|
||||
/// The equivalent of a `String`.
|
||||
#[derive(PartialEq)]
|
||||
pub struct MCString {
|
||||
pub value: String,
|
||||
}
|
||||
impl From<&str> for MCString {
|
||||
fn from(s: &str) -> MCString {
|
||||
MCString {
|
||||
value: s.to_owned(),
|
||||
}
|
||||
pub fn parse_string(data: &[u8]) -> ParseResult<String> {
|
||||
let mut offset = 0;
|
||||
let (length, offset_delta) = parse_varint(&data[offset..])?;
|
||||
offset += offset_delta;
|
||||
let length = length as usize;
|
||||
if data.len() < offset + length {
|
||||
return Err(ParseError::NotEnoughData);
|
||||
}
|
||||
let output = String::from_utf8_lossy(&data[offset..offset + length]).to_string();
|
||||
offset += length;
|
||||
Ok((output, offset))
|
||||
}
|
||||
impl From<String> for MCString {
|
||||
fn from(s: String) -> MCString {
|
||||
MCString { value: s }
|
||||
}
|
||||
}
|
||||
impl Into<String> for MCString {
|
||||
fn into(self) -> String {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<&str> for MCString {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.value == **other
|
||||
}
|
||||
}
|
||||
impl PartialEq<String> for MCString {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.value == *other
|
||||
}
|
||||
}
|
||||
impl PartialEq<&String> for MCString {
|
||||
fn eq(&self, other: &&String) -> bool {
|
||||
self.value == **other
|
||||
}
|
||||
}
|
||||
impl Clone for MCString {
|
||||
fn clone(&self) -> Self {
|
||||
MCString {
|
||||
value: self.value.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Debug for MCString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"MCString {{ \"{}\" ({} chars) }}",
|
||||
self.value,
|
||||
self.value.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Display for MCString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCString {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("Cannot read MCString from bytes")
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCString {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let str_len: Vec<u8> = MCVarInt::from(self.value.len() as i32).into();
|
||||
for b in str_len {
|
||||
out.push(b);
|
||||
}
|
||||
for b in self.value.into_bytes() {
|
||||
out.push(b);
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
impl MCString {
|
||||
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let str_len = MCVarInt::read(t).await?;
|
||||
let mut str_bytes = vec![];
|
||||
for _ in 0i32..str_len.into() {
|
||||
str_bytes.push(read_byte(t).await?);
|
||||
}
|
||||
Ok(MCString {
|
||||
value: String::from_utf8_lossy(&str_bytes).to_string(),
|
||||
})
|
||||
}
|
||||
pub fn serialize_string(value: &str) -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend_from_slice(&serialize_varint(value.len() as i32));
|
||||
output.extend_from_slice(value.as_bytes());
|
||||
output
|
||||
}
|
||||
|
||||
/// A normal `MCString`, but with extra embedded formatting data.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MCChat {
|
||||
pub text: MCString,
|
||||
pub fn parse_json(data: &[u8]) -> ParseResult<JSON> {
|
||||
unimplemented!()
|
||||
}
|
||||
impl From<&str> for MCChat {
|
||||
fn from(s: &str) -> MCChat {
|
||||
MCChat { text: s.into() }
|
||||
}
|
||||
}
|
||||
impl From<String> for MCChat {
|
||||
fn from(s: String) -> MCChat {
|
||||
MCChat { text: s.into() }
|
||||
}
|
||||
}
|
||||
impl Into<String> for MCChat {
|
||||
fn into(self) -> String {
|
||||
self.text.value
|
||||
}
|
||||
}
|
||||
impl PartialEq<&str> for MCChat {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.text.value == **other
|
||||
}
|
||||
}
|
||||
impl PartialEq<String> for MCChat {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.text.value == *other
|
||||
}
|
||||
}
|
||||
impl PartialEq<&String> for MCChat {
|
||||
fn eq(&self, other: &&String) -> bool {
|
||||
self.text.value == **other
|
||||
}
|
||||
}
|
||||
impl Clone for MCChat {
|
||||
fn clone(&self) -> Self {
|
||||
MCChat {
|
||||
text: self.text.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for MCChat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"\"{}\" ({} chars)",
|
||||
self.text.value,
|
||||
self.text.value.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCChat {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("Cannot read MCChat from bytes")
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCChat {
|
||||
fn into(self) -> Vec<u8> {
|
||||
// Just output
|
||||
// {"text": "<data>"}
|
||||
Into::<MCString>::into(
|
||||
json!({
|
||||
"text": self.text.value
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
impl MCChat {
|
||||
pub async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
Err(io_error("Cannot read MCChat from stream"))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Actually make the MCPosition work.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct MCPosition {
|
||||
x: MCLong,
|
||||
y: MCLong,
|
||||
z: MCLong,
|
||||
}
|
||||
impl Display for MCPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {}, {})", self.x, self.y, self.z,)
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for MCPosition {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("Cannot read MCPosition from bytes")
|
||||
}
|
||||
}
|
||||
impl Into<Vec<u8>> for MCPosition {
|
||||
fn into(self) -> Vec<u8> {
|
||||
// Just output
|
||||
// {"text": "<data>"}
|
||||
let mut out = vec![];
|
||||
let mut temp = vec![];
|
||||
temp.extend_from_slice(
|
||||
&((((Into::<i64>::into(self.x) & 0x3FFFFFF) << 38)
|
||||
| ((Into::<i64>::into(self.y) & 0xFFF) << 26)
|
||||
| (Into::<i64>::into(self.z) & 0x3FFFFFF)) as u64)
|
||||
.to_be_bytes(),
|
||||
);
|
||||
// temp.extend_from_slice(&"{\"text\": \"".to_owned().into_bytes());
|
||||
// temp.extend_from_slice(&self.text.value.into_bytes());
|
||||
// temp.extend_from_slice(&"\"}".to_owned().into_bytes());
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl MCPosition {
|
||||
pub fn new() -> MCPosition {
|
||||
MCPosition {
|
||||
x: 0.into(),
|
||||
y: 0.into(),
|
||||
z: 0.into(),
|
||||
}
|
||||
}
|
||||
pub async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
Err(io_error("Cannot read MCPosition from stream"))
|
||||
}
|
||||
}
|
||||
impl Default for MCPosition {
|
||||
fn default() -> Self {
|
||||
MCPosition {
|
||||
x: 0.into(),
|
||||
y: 0.into(),
|
||||
z: 0.into(),
|
||||
}
|
||||
}
|
||||
pub fn serialize_json(value: JSON) -> Vec<u8> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
24
src/net/mod.rs
Normal file
24
src/net/mod.rs
Normal file
@ -0,0 +1,24 @@
|
||||
pub mod packets;
|
||||
|
||||
use crate::prelude::*;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub enum NetworkClientState {
|
||||
Disconnected,
|
||||
Handshake,
|
||||
Status,
|
||||
Login,
|
||||
Play,
|
||||
}
|
||||
|
||||
pub struct NetworkClient {
|
||||
pub status: NetworkClientState,
|
||||
pub stream: TcpStream,
|
||||
pub buffer: VecDeque<u8>,
|
||||
}
|
||||
impl NetworkClient {
|
||||
pub async fn read_bytes(&mut self) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
104
src/net/packets.rs
Normal file
104
src/net/packets.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use super::NetworkClientState;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Packet {
|
||||
// Handshake
|
||||
SH00Handshake {
|
||||
protocol_version: i32,
|
||||
server_address: String,
|
||||
server_port: u16,
|
||||
next_state: NetworkClientState,
|
||||
},
|
||||
|
||||
// Status
|
||||
CS00Response {
|
||||
json_response: JSON,
|
||||
},
|
||||
CS01Pong {
|
||||
payload: i64,
|
||||
},
|
||||
SS00Request,
|
||||
SS01Ping {
|
||||
payload: i64,
|
||||
},
|
||||
// Login
|
||||
// Play
|
||||
}
|
||||
impl Packet {
|
||||
pub fn parse_body(
|
||||
data: &[u8],
|
||||
length: usize,
|
||||
id: usize,
|
||||
state: NetworkClientState,
|
||||
serverbound: bool,
|
||||
) -> ParseResult<Packet> {
|
||||
use NetworkClientState::*;
|
||||
use Packet::*;
|
||||
|
||||
let mut offset = 0;
|
||||
match state {
|
||||
Disconnected => Err(ParseError::InvalidData),
|
||||
Handshake => {
|
||||
if id == 0x00 && serverbound {
|
||||
let (protocol_version, offset_delta) = parse_varint(&data[offset..])?;
|
||||
offset += offset_delta;
|
||||
let (server_address, offset_delta) = parse_string(&data[offset..])?;
|
||||
offset += offset_delta;
|
||||
let (server_port, offset_delta) = parse_unsigned_short(&data[offset..])?;
|
||||
offset += offset_delta;
|
||||
let (next_state, offset_delta) = parse_varint(&data[offset..])?;
|
||||
offset += offset_delta;
|
||||
let next_state = match next_state {
|
||||
0 => NetworkClientState::Status,
|
||||
1 => NetworkClientState::Login,
|
||||
_ => return Err(ParseError::InvalidData),
|
||||
};
|
||||
Ok((
|
||||
Packet::SH00Handshake {
|
||||
protocol_version,
|
||||
server_address,
|
||||
server_port,
|
||||
next_state,
|
||||
},
|
||||
offset,
|
||||
))
|
||||
} else {
|
||||
Err(ParseError::InvalidData)
|
||||
}
|
||||
}
|
||||
Status => match id {
|
||||
0x00 => {
|
||||
if serverbound {
|
||||
unimplemented!("Parse SS00Request")
|
||||
} else {
|
||||
unimplemented!("Parse CS00Response")
|
||||
}
|
||||
}
|
||||
0x01 => {
|
||||
if serverbound {
|
||||
unimplemented!("Parse SS01Ping")
|
||||
} else {
|
||||
unimplemented!("Parse CS01Pong")
|
||||
}
|
||||
}
|
||||
_ => Err(ParseError::InvalidData),
|
||||
},
|
||||
Login => unimplemented!("Parse Login packet"),
|
||||
Play => unimplemented!("Parse Play packet"),
|
||||
}
|
||||
}
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
use Packet::*;
|
||||
let (id, mut body): (usize, Vec<u8>) = match self {
|
||||
CS00Response { json_response } => (0x00, serialize_json(json_response.clone())),
|
||||
CS01Pong { payload } => (0x01, serialize_long(payload.clone()).to_vec()),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let mut id_and_body = serialize_varint(id as i32);
|
||||
id_and_body.append(&mut body);
|
||||
let mut output = serialize_varint(id_and_body.len() as i32);
|
||||
output.append(&mut id_and_body);
|
||||
output
|
||||
}
|
||||
}
|
@ -1,272 +1,18 @@
|
||||
/// Internal messaging for the server.
|
||||
pub mod messages;
|
||||
/// Put the network client struct in its own file.
|
||||
pub mod net;
|
||||
use crate::prelude::*;
|
||||
use tokio::net::ToSocketAddrs;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::{mctypes::*, CONFIG, FAVICON};
|
||||
use log::*;
|
||||
use messages::*;
|
||||
use net::{
|
||||
packets::{self, Packet, PacketCommon},
|
||||
*,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::sync::mpsc::{self, Receiver, TryRecvError};
|
||||
use std::time::Duration;
|
||||
use tokio::net::{TcpListener, ToSocketAddrs};
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ServerError {}
|
||||
|
||||
/// The struct containing all the data and running all the updates.
|
||||
pub struct Server {
|
||||
network_clients: Vec<NetworkClient>,
|
||||
network_receiver: Receiver<NetworkClient>,
|
||||
pub players: Vec<Player>,
|
||||
}
|
||||
pub struct Server {}
|
||||
impl Server {
|
||||
pub fn new<A: 'static + ToSocketAddrs + Send>(addr: A) -> Server {
|
||||
let (network_client_tx, network_client_rx) = mpsc::channel();
|
||||
let (serverbound_message_tx, _serverbound_message_rx) = mpsc::channel();
|
||||
tokio::task::spawn(async move {
|
||||
let listener = TcpListener::bind(addr)
|
||||
.await
|
||||
.expect("Could not bind to TCP socket");
|
||||
let mut id = 0;
|
||||
loop {
|
||||
let (stream, _) = listener
|
||||
.accept()
|
||||
.await
|
||||
.expect("Network receiver disconnected");
|
||||
network_client_tx
|
||||
.send(NetworkClient::new(
|
||||
stream,
|
||||
id as u128,
|
||||
serverbound_message_tx.clone(),
|
||||
))
|
||||
.expect("Network receiver disconnected");
|
||||
id += 1;
|
||||
}
|
||||
});
|
||||
info!("Network server started!");
|
||||
Server {
|
||||
network_receiver: network_client_rx,
|
||||
network_clients: vec![],
|
||||
// message_receiver: serverbound_message_rx,
|
||||
players: vec![],
|
||||
}
|
||||
pub fn new<A: ToSocketAddrs>(bind_address: A) -> Server {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Shut down the server.
|
||||
///
|
||||
/// Disconnects all clients.
|
||||
pub async fn shutdown(&mut self) {
|
||||
info!(
|
||||
"Server shutting down. Uptime: {:?}",
|
||||
crate::START_TIME.elapsed()
|
||||
);
|
||||
self.broadcast_message(ClientboundMessage::Disconnect(
|
||||
"The server is shutting down".into(),
|
||||
))
|
||||
.await;
|
||||
pub async fn update(&mut self) -> Result<(), ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn num_players(&self) -> usize {
|
||||
let mut num = 0;
|
||||
for client in &self.network_clients {
|
||||
if client.state == NetworkClientState::Play {
|
||||
num += 1;
|
||||
}
|
||||
}
|
||||
num
|
||||
}
|
||||
|
||||
/// Send a `ClientboundMessage` to all connected network clients.
|
||||
pub async fn broadcast_message(&mut self, message: ClientboundMessage) {
|
||||
let mut v = Vec::new();
|
||||
for client in self.network_clients.iter_mut() {
|
||||
v.push(client.handle_message(message.clone()));
|
||||
}
|
||||
futures::future::join_all(v).await;
|
||||
}
|
||||
|
||||
/// Get a client from their id.
|
||||
pub fn client_from_id(&mut self, client_id: u128) -> Option<&mut NetworkClient> {
|
||||
// Find the client based on id.
|
||||
let mut client_index = -1isize;
|
||||
for (i, c) in self.network_clients.iter().enumerate() {
|
||||
if c.id == client_id {
|
||||
client_index = i as isize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if client_index == -1 {
|
||||
return None;
|
||||
}
|
||||
Some(&mut self.network_clients[client_index as usize])
|
||||
}
|
||||
|
||||
/// Update the game server.
|
||||
pub async fn update(&mut self) -> tokio::io::Result<()> {
|
||||
// Get new clients from the network listener thread.
|
||||
loop {
|
||||
match self.network_receiver.try_recv() {
|
||||
Ok(client) => {
|
||||
info!(
|
||||
"Got client at {}",
|
||||
client.stream.peer_addr().expect("could not get peer addr")
|
||||
);
|
||||
self.network_clients.push(client)
|
||||
}
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(TryRecvError::Disconnected) => panic!("network sender disconnected"),
|
||||
}
|
||||
}
|
||||
// Read new packets from each client.
|
||||
for client in self.network_clients.iter_mut() {
|
||||
// Update and ignore errors.
|
||||
if client.update().await.is_err() {
|
||||
client.force_disconnect();
|
||||
}
|
||||
}
|
||||
// Update the client and server according to each packet.
|
||||
let mut packets = vec![];
|
||||
for client in self.network_clients.iter_mut() {
|
||||
while let Some(packet) = client.read_packet() {
|
||||
packets.push((client.id, packet));
|
||||
}
|
||||
}
|
||||
for (client_id, packet) in packets {
|
||||
if self.handle_packet(client_id, packet).await.is_err() {
|
||||
self.client_from_id(client_id).unwrap().force_disconnect();
|
||||
}
|
||||
}
|
||||
// Disconnect clients when necessary.
|
||||
for client in self.network_clients.iter_mut() {
|
||||
if client.state == NetworkClientState::Disconnected {
|
||||
client.force_disconnect();
|
||||
} else if client.last_keep_alive.elapsed() > Duration::from_secs(20) {
|
||||
debug!("Disconnecting client for timing out");
|
||||
client.state = NetworkClientState::Disconnected;
|
||||
client.force_disconnect();
|
||||
}
|
||||
}
|
||||
// Remove disconnected clients.
|
||||
self.network_clients
|
||||
.retain(|nc| nc.state != NetworkClientState::Disconnected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle a packet.
|
||||
pub async fn handle_packet<P: PacketCommon>(
|
||||
&mut self,
|
||||
client_id: u128,
|
||||
packet: P,
|
||||
) -> tokio::io::Result<()> {
|
||||
let num_players = self.num_players();
|
||||
let mut client = self.client_from_id(client_id).unwrap();
|
||||
match packet.as_packet() {
|
||||
// Handshaking.
|
||||
Packet::Handshake(handshake) => {
|
||||
if handshake.next_state == 1 {
|
||||
client.state = NetworkClientState::Status;
|
||||
} else if handshake.next_state == 2 {
|
||||
client.state = NetworkClientState::Login;
|
||||
} else {
|
||||
client.state = NetworkClientState::Disconnected;
|
||||
}
|
||||
if handshake.protocol_version != 47 {
|
||||
let mut logindisconnect = packets::LoginDisconnect::new();
|
||||
logindisconnect.reason = MCChat {
|
||||
text: MCString::from("Incompatible client! Server is on 1.8.9"),
|
||||
};
|
||||
client.send_packet(logindisconnect).await?;
|
||||
client.state = NetworkClientState::Disconnected;
|
||||
}
|
||||
}
|
||||
// Status.
|
||||
Packet::StatusRequest(_statusrequest) => {
|
||||
let mut statusresponse = packets::StatusResponse::new();
|
||||
statusresponse.json_response = json!({
|
||||
"version": {
|
||||
"name": "1.8.9",
|
||||
"protocol": 47,
|
||||
},
|
||||
"players": {
|
||||
"max": CONFIG.max_players,
|
||||
"online": num_players,
|
||||
"sample": [
|
||||
{
|
||||
"name": "shvr",
|
||||
"id": "e3f58380-60bb-4714-91f2-151d525e64aa"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"text": CONFIG.motd
|
||||
},
|
||||
"favicon": format!("data:image/png;base64,{}", if FAVICON.is_ok() { radix64::STD.encode(FAVICON.as_ref().unwrap().as_slice()) } else { "".to_owned() })
|
||||
})
|
||||
.to_string()
|
||||
.into();
|
||||
client.send_packet(statusresponse).await?;
|
||||
}
|
||||
Packet::StatusPing(statusping) => {
|
||||
let mut statuspong = packets::StatusPong::new();
|
||||
statuspong.payload = statusping.payload;
|
||||
client.send_packet(statuspong).await?;
|
||||
}
|
||||
// Login.
|
||||
Packet::LoginStart(loginstart) => {
|
||||
client.player = Some(crate::entity::player::Player::new());
|
||||
let player = client.player.as_mut().unwrap();
|
||||
*player.username_mut() = loginstart.player_name.into();
|
||||
// Offline mode skips encryption and compression.
|
||||
// TODO: Encryption and compression
|
||||
let mut loginsuccess = packets::LoginSuccess::new();
|
||||
// We're in offline mode, so this is a temporary uuid.
|
||||
// TODO: Get uuid and username from Mojang servers.
|
||||
loginsuccess.uuid = player.uuid.clone().to_hyphenated().to_string().into();
|
||||
loginsuccess.username = player.username().clone().into();
|
||||
client.send_packet(loginsuccess).await?;
|
||||
client.state = NetworkClientState::Play;
|
||||
client.send_packet(packets::JoinGame::new()).await?;
|
||||
}
|
||||
Packet::ClientSettings(_clientsettings) => {
|
||||
// TODO: Handle the packet.
|
||||
client.send_packet(packets::HeldItemChange::new()).await?;
|
||||
client
|
||||
.send_packet(packets::ClientboundPlayerPositionAndLook::new())
|
||||
.await?;
|
||||
client.send_packet(packets::SpawnPosition::new()).await?;
|
||||
}
|
||||
// Play.
|
||||
Packet::KeepAlivePong(_keepalivepong) => {
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
Packet::ServerboundChatMessage(chatmessage) => {
|
||||
let player_name = client.player.as_ref().unwrap().username().clone();
|
||||
info!("<{}> {}", player_name, chatmessage.text);
|
||||
self.broadcast_message(ClientboundMessage::Chat(format!(
|
||||
"<{}> {}",
|
||||
player_name, chatmessage.text
|
||||
)))
|
||||
.await;
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
Packet::Player(_player) => {
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
Packet::PlayerPosition(_playerposition) => {
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
Packet::PlayerLook(_playerlook) => {
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
Packet::ServerboundPlayerPositionAndLook(_playerpositionandlook) => {
|
||||
// TODO: Handle the packet.
|
||||
}
|
||||
// Other.
|
||||
_ => error!("handling unknown packet type: {:?}", packet),
|
||||
}
|
||||
Ok(())
|
||||
pub async fn shutdown(&mut self) -> Result<(), ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -1,218 +0,0 @@
|
||||
/// Definitions for all the packets in the Minecraft protocol.
|
||||
pub mod packets;
|
||||
|
||||
use super::messages::*;
|
||||
use crate::mctypes::*;
|
||||
use log::*;
|
||||
use packets::*;
|
||||
// use serde_json::json;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::mpsc::Sender,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
/// The network client can only be in a few states,
|
||||
/// this enum keeps track of that.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum NetworkClientState {
|
||||
Handshake,
|
||||
Status,
|
||||
Login,
|
||||
Play,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
/// A wrapper to contain everything related
|
||||
/// to networking for the client.
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkClient {
|
||||
pub id: u128,
|
||||
pub connected: bool,
|
||||
pub stream: TcpStream,
|
||||
pub state: NetworkClientState,
|
||||
pub last_keep_alive: Instant,
|
||||
pub message_sender: Sender<ServerboundMessage>,
|
||||
packets: VecDeque<Packet>,
|
||||
pub player: Option<crate::entity::player::Player>,
|
||||
}
|
||||
impl NetworkClient {
|
||||
/// Create a new `NetworkClient`
|
||||
pub fn new(
|
||||
stream: TcpStream,
|
||||
id: u128,
|
||||
message_sender: Sender<ServerboundMessage>,
|
||||
) -> NetworkClient {
|
||||
NetworkClient {
|
||||
id,
|
||||
connected: true,
|
||||
stream,
|
||||
state: NetworkClientState::Handshake,
|
||||
last_keep_alive: Instant::now(),
|
||||
message_sender,
|
||||
packets: VecDeque::new(),
|
||||
player: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to read a new packet into the processing queue.
|
||||
pub async fn update(&mut self) -> tokio::io::Result<()> {
|
||||
// Don't try to read packets if disconnected.
|
||||
if self.state == NetworkClientState::Disconnected {
|
||||
return Ok(());
|
||||
}
|
||||
if self.stream.peek(&mut [0u8; 4096]).await? > 0 {
|
||||
// Read the packet header.
|
||||
let (_packet_length, packet_id) = read_packet_header(&mut self.stream).await?;
|
||||
// Get the packet based on packet_id.
|
||||
let packet = match packet_id.value {
|
||||
0x00 => match self.state {
|
||||
NetworkClientState::Handshake => {
|
||||
Some(self.get_wrapped_packet::<Handshake>().await)
|
||||
}
|
||||
NetworkClientState::Status => {
|
||||
Some(self.get_wrapped_packet::<StatusRequest>().await)
|
||||
}
|
||||
NetworkClientState::Login => {
|
||||
Some(self.get_wrapped_packet::<LoginStart>().await)
|
||||
}
|
||||
NetworkClientState::Play => {
|
||||
Some(self.get_wrapped_packet::<KeepAlivePong>().await)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
0x01 => {
|
||||
match self.state {
|
||||
NetworkClientState::Status => {
|
||||
Some(self.get_wrapped_packet::<StatusPing>().await)
|
||||
}
|
||||
NetworkClientState::Login => None, // TODO: 0x01 Encryption Response
|
||||
NetworkClientState::Play => {
|
||||
Some(self.get_wrapped_packet::<ServerboundChatMessage>().await)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// The rest of the packets are all always in the play state.
|
||||
0x02 => None, // TODO: 0x02 Use Entity
|
||||
0x03 => Some(self.get_wrapped_packet::<Player>().await),
|
||||
0x04 => Some(self.get_wrapped_packet::<PlayerPosition>().await),
|
||||
0x05 => Some(self.get_wrapped_packet::<PlayerLook>().await),
|
||||
0x06 => Some(
|
||||
self.get_wrapped_packet::<ServerboundPlayerPositionAndLook>()
|
||||
.await,
|
||||
),
|
||||
0x07 => None, // TODO: 0x07 Player Digging
|
||||
0x08 => None, // TODO: 0x08 Player Block Placement
|
||||
0x09 => None, // TODO: 0x09 Held Item Change
|
||||
0x0a => None, // TODO: 0x0a Animation
|
||||
0x0b => None, // TODO: 0x0b Entity Action
|
||||
0x0c => None, // TODO: 0x0c Steer Vehicle
|
||||
0x0d => None, // TODO: 0x0d Close Window
|
||||
0x0e => None, // TODO: 0x0e Click Window
|
||||
0x0f => None, // TODO: 0x0f Confirm Transaction
|
||||
0x10 => None, // TODO: 0x10 Creative Inventory Action
|
||||
0x11 => None, // TODO: 0x11 Enchant Item
|
||||
0x12 => None, // TODO: 0x12 Update Sign
|
||||
0x13 => None, // TODO: 0x13 Player Abilities
|
||||
0x14 => None, // TODO: 0x14 Tab-Complete
|
||||
0x15 => Some(self.get_wrapped_packet::<ClientSettings>().await),
|
||||
0x16 => None, // TODO: 0x16 Client Status
|
||||
0x17 => None, // TODO: 0x17 Plugin Message
|
||||
0x18 => None, // TODO: 0x18 Spectate
|
||||
0x19 => None, // TODO: 0x19 Resource Pack Status
|
||||
_ => None,
|
||||
};
|
||||
if let Some(Ok(packet)) = packet {
|
||||
// Add it to the internal queue to be processed.
|
||||
self.packets.push_back(packet);
|
||||
}
|
||||
}
|
||||
if self.last_keep_alive.elapsed() > Duration::from_millis(1000) {
|
||||
debug!(
|
||||
"Sending keep alive, last one was {:?} ago",
|
||||
self.last_keep_alive.elapsed()
|
||||
);
|
||||
self.keep_alive().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pop a packet from the queue.
|
||||
pub fn read_packet(&mut self) -> Option<Packet> {
|
||||
self.packets.pop_front()
|
||||
}
|
||||
|
||||
/// Send a generic packet to the client.
|
||||
pub async fn send_packet<P: PacketCommon>(&mut self, packet: P) -> tokio::io::Result<()> {
|
||||
debug!("Sent {:?} {:#04X?} {:?}", self.state, P::id(), packet);
|
||||
packet.write(&mut self.stream).await
|
||||
}
|
||||
|
||||
/// Read a generic packet from the network.
|
||||
async fn get_packet<T: PacketCommon>(&mut self) -> tokio::io::Result<T> {
|
||||
let packet = T::read(&mut self.stream).await?;
|
||||
debug!("Got {:?} {:#04X?} {:?}", self.state, T::id(), packet);
|
||||
Ok(packet)
|
||||
}
|
||||
|
||||
/// Read a generic packet from the network and wrap it in `Packet`.
|
||||
async fn get_wrapped_packet<T: PacketCommon>(&mut self) -> tokio::io::Result<Packet> {
|
||||
let packet = T::read(&mut self.stream).await?;
|
||||
debug!("Got {:?} {:#04X?} {:?}", self.state, T::id(), packet);
|
||||
Ok(packet.as_packet())
|
||||
}
|
||||
|
||||
/// Send the client a message in chat.
|
||||
pub async fn send_chat_message<C: Into<MCChat>>(
|
||||
&mut self,
|
||||
message: C,
|
||||
) -> tokio::io::Result<()> {
|
||||
let mut chatmessage = ClientboundChatMessage::new();
|
||||
chatmessage.text = message.into();
|
||||
self.send_packet(chatmessage).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disconnect the client.
|
||||
///
|
||||
/// Sends `0x40 Disconnect` then waits 10 seconds before forcing the connection closed.
|
||||
pub async fn disconnect<S: Into<MCString>>(&mut self, reason: S) -> tokio::io::Result<()> {
|
||||
let mut disconnect = Disconnect::new();
|
||||
disconnect.reason.text = reason.into();
|
||||
self.send_packet(disconnect).await?;
|
||||
self.force_disconnect();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Force disconnect the client by marking it for cleanup as disconnected.
|
||||
pub fn force_disconnect(&mut self) {
|
||||
self.connected = false;
|
||||
self.state = NetworkClientState::Disconnected;
|
||||
}
|
||||
|
||||
/// Send a keep alive packet to the client.
|
||||
pub async fn keep_alive(&mut self) -> tokio::io::Result<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
// self.send_chat_message("keep alive").await?;
|
||||
}
|
||||
// Keep alive ping to client.
|
||||
self.send_packet(KeepAlivePing::new()).await?;
|
||||
// Keep alive pong to server.
|
||||
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
|
||||
let _ = self.get_packet::<KeepAlivePong>().await?;
|
||||
self.last_keep_alive = Instant::now();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receives messages from the server.
|
||||
pub async fn handle_message(&mut self, message: ClientboundMessage) -> tokio::io::Result<()> {
|
||||
use ClientboundMessage::*;
|
||||
match message {
|
||||
Chat(s) => self.send_chat_message(s).await?,
|
||||
Disconnect(reason) => self.disconnect(reason).await?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,604 +0,0 @@
|
||||
use super::PacketCommon;
|
||||
use crate::mctypes::*;
|
||||
use crate::CONFIG;
|
||||
use std::convert::{Into, TryFrom};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusResponse {
|
||||
pub json_response: MCString,
|
||||
}
|
||||
impl Into<Vec<u8>> for StatusResponse {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Status Response.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.json_response));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for StatusResponse {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for StatusResponse {
|
||||
fn new() -> Self {
|
||||
StatusResponse {
|
||||
json_response: MCString::from(""),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut statusresponse = StatusResponse::new();
|
||||
statusresponse.json_response = MCString::read(t).await?;
|
||||
Ok(statusresponse)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusPong {
|
||||
pub payload: MCLong,
|
||||
}
|
||||
impl Into<Vec<u8>> for StatusPong {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x01 Status Pong.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.payload));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for StatusPong {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for StatusPong {
|
||||
fn new() -> Self {
|
||||
StatusPong { payload: 0.into() }
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x01
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut statuspong = StatusPong::new();
|
||||
statuspong.payload = MCLong::read(t).await?;
|
||||
Ok(statuspong)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoginSuccess {
|
||||
pub uuid: MCString,
|
||||
pub username: MCString,
|
||||
}
|
||||
impl Into<Vec<u8>> for LoginSuccess {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x02 Login Success.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.uuid));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.username));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for LoginSuccess {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for LoginSuccess {
|
||||
fn new() -> Self {
|
||||
LoginSuccess {
|
||||
uuid: MCString::from(""),
|
||||
username: MCString::from(""),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x02
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut loginsuccess = LoginSuccess::new();
|
||||
loginsuccess.uuid = MCString::read(t).await?;
|
||||
loginsuccess.username = MCString::read(t).await?;
|
||||
Ok(loginsuccess)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoginDisconnect {
|
||||
pub reason: MCChat,
|
||||
}
|
||||
impl Into<Vec<u8>> for LoginDisconnect {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Login Disconnect.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.reason));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for LoginDisconnect {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for LoginDisconnect {
|
||||
fn new() -> Self {
|
||||
LoginDisconnect {
|
||||
reason: MCChat {
|
||||
text: MCString::from(""),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut logindisconnect = LoginDisconnect::new();
|
||||
logindisconnect.reason = MCChat {
|
||||
text: MCString::read(t).await?,
|
||||
};
|
||||
Ok(logindisconnect)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinGame {
|
||||
pub entity_id: MCInt, // The player's Entity ID (EID)
|
||||
pub gamemode: MCUnsignedByte, // 0: Survival, 1: Creative, 2: Adventure, 3: Spectator. Bit 3 (0x8) is the hardcore flag.
|
||||
pub dimension: MCByte, // -1: Nether, 0: Overworld, 1: End
|
||||
pub difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard
|
||||
pub max_players: MCUnsignedByte, // Used by the client to draw the player list
|
||||
pub level_type: MCString, // default, flat, largeBiomes, amplified, default_1_1
|
||||
pub reduced_debug_info: MCBoolean, // If true, a Notchian client shows reduced information on the debug screen.
|
||||
}
|
||||
impl Into<Vec<u8>> for JoinGame {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x01 Join Game.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.entity_id));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.gamemode));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.dimension));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.difficulty));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.max_players));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.level_type));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.reduced_debug_info));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for JoinGame {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for JoinGame {
|
||||
fn new() -> Self {
|
||||
JoinGame {
|
||||
entity_id: 0.into(),
|
||||
gamemode: 1.into(), // Default to creative mode.
|
||||
dimension: 0.into(), // Default to overworld.
|
||||
difficulty: 2.into(),
|
||||
max_players: (CONFIG.max_players as u8).into(),
|
||||
level_type: "default".into(), // Use the default world type.
|
||||
reduced_debug_info: false.into(), // The debug info should be useful.
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x01
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut joingame = JoinGame::new();
|
||||
joingame.entity_id = MCInt::read(t).await?;
|
||||
joingame.gamemode = MCUnsignedByte::read(t).await?;
|
||||
joingame.dimension = MCByte::read(t).await?;
|
||||
joingame.difficulty = MCUnsignedByte::read(t).await?;
|
||||
joingame.max_players = MCUnsignedByte::read(t).await?;
|
||||
joingame.level_type = MCString::read(t).await?;
|
||||
joingame.reduced_debug_info = MCBoolean::read(t).await?;
|
||||
Ok(joingame)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HeldItemChange {
|
||||
pub selected_slot: MCByte,
|
||||
}
|
||||
impl Into<Vec<u8>> for HeldItemChange {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x09 Held Item Change.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.selected_slot));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for HeldItemChange {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for HeldItemChange {
|
||||
fn new() -> Self {
|
||||
HeldItemChange {
|
||||
selected_slot: 0.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x09
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut helditemchange = HeldItemChange::new();
|
||||
helditemchange.selected_slot = MCByte::read(t).await?;
|
||||
Ok(helditemchange)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EntityStatus {
|
||||
pub entity_id: MCInt,
|
||||
pub entity_status: MCByte, // See table below.
|
||||
// 1: Sent when resetting a mob spawn minecart's timer / Rabbit jump animation
|
||||
// 2: Living Entity hurt
|
||||
// 3: Living Entity dead
|
||||
// 4: Iron Golem throwing up arms
|
||||
// 6: Wolf/Ocelot/Horse taming — Spawn “heart” particles
|
||||
// 7: Wolf/Ocelot/Horse tamed — Spawn “smoke” particles
|
||||
// 8: Wolf shaking water — Trigger the shaking animation
|
||||
// 9: (of self) Eating accepted by server
|
||||
// 10: Sheep eating grass
|
||||
// 10: Play TNT ignite sound
|
||||
// 11: Iron Golem handing over a rose
|
||||
// 12: Villager mating — Spawn “heart” particles
|
||||
// 13: Spawn particles indicating that a villager is angry and seeking revenge
|
||||
// 14: Spawn happy particles near a villager
|
||||
// 15: Witch animation — Spawn “magic” particles
|
||||
// 16: Play zombie converting into a villager sound
|
||||
// 17: Firework exploding
|
||||
// 18: Animal in love (ready to mate) — Spawn “heart” particles
|
||||
// 19: Reset squid rotation
|
||||
// 20: Spawn explosion particle — works for some living entities
|
||||
// 21: Play guardian sound — works for only for guardians
|
||||
// 22: Enables reduced debug for players
|
||||
// 23: Disables reduced debug for players
|
||||
}
|
||||
impl Into<Vec<u8>> for EntityStatus {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x1a Entity Status.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.entity_id));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.entity_status));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for EntityStatus {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for EntityStatus {
|
||||
fn new() -> Self {
|
||||
EntityStatus {
|
||||
entity_id: 0.into(),
|
||||
entity_status: 0.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x1a
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut entitystatus = EntityStatus::new();
|
||||
entitystatus.entity_id = MCInt::read(t).await?;
|
||||
entitystatus.entity_status = MCByte::read(t).await?;
|
||||
Ok(entitystatus)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientboundPlayerPositionAndLook {
|
||||
pub x: MCDouble,
|
||||
pub y: MCDouble,
|
||||
pub z: MCDouble,
|
||||
pub yaw: MCFloat,
|
||||
pub pitch: MCFloat,
|
||||
pub flags: MCByte,
|
||||
}
|
||||
impl Into<Vec<u8>> for ClientboundPlayerPositionAndLook {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x08 Player Position and Look.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.x));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.y));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.z));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.yaw));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.pitch));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.flags));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ClientboundPlayerPositionAndLook {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for ClientboundPlayerPositionAndLook {
|
||||
fn new() -> Self {
|
||||
ClientboundPlayerPositionAndLook {
|
||||
x: 0.0.into(),
|
||||
y: 0.0.into(),
|
||||
z: 0.0.into(),
|
||||
yaw: 0.0.into(),
|
||||
pitch: 0.0.into(),
|
||||
flags: 0x00.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x08
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut playerpositionandlook = ClientboundPlayerPositionAndLook::new();
|
||||
playerpositionandlook.x = MCDouble::read(t).await?;
|
||||
playerpositionandlook.y = MCDouble::read(t).await?;
|
||||
playerpositionandlook.z = MCDouble::read(t).await?;
|
||||
playerpositionandlook.yaw = MCFloat::read(t).await?;
|
||||
playerpositionandlook.pitch = MCFloat::read(t).await?;
|
||||
playerpositionandlook.flags = MCByte::read(t).await?;
|
||||
Ok(playerpositionandlook)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Actually send the position.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpawnPosition {
|
||||
pub position: MCPosition,
|
||||
}
|
||||
impl Into<Vec<u8>> for SpawnPosition {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x05 Spawn Position.
|
||||
// temp.extend_from_slice(&Into::<Vec<u8>>::into(self.position));
|
||||
temp.extend_from_slice(&0u64.to_be_bytes());
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for SpawnPosition {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for SpawnPosition {
|
||||
fn new() -> Self {
|
||||
SpawnPosition {
|
||||
position: MCPosition::new(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x05
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut spawnposition = SpawnPosition::new();
|
||||
spawnposition.position = MCPosition::read(t).await?;
|
||||
Ok(spawnposition)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeepAlivePing {
|
||||
pub payload: MCVarInt,
|
||||
}
|
||||
impl Into<Vec<u8>> for KeepAlivePing {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Keep Alive.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.payload));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for KeepAlivePing {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for KeepAlivePing {
|
||||
fn new() -> Self {
|
||||
KeepAlivePing { payload: 0.into() }
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut keepalive = KeepAlivePing::new();
|
||||
keepalive.payload = MCVarInt::read(t).await?;
|
||||
Ok(keepalive)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Disconnect {
|
||||
pub reason: MCChat,
|
||||
}
|
||||
impl Into<Vec<u8>> for Disconnect {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x40 Disconnect.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.reason));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for Disconnect {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for Disconnect {
|
||||
fn new() -> Self {
|
||||
Disconnect {
|
||||
reason: MCChat {
|
||||
text: "Disconnected".into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x40
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut keepalive = Disconnect::new();
|
||||
keepalive.reason = MCChat::read(t).await?;
|
||||
Ok(keepalive)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientboundChatMessage {
|
||||
pub text: MCChat,
|
||||
pub position: MCByte, // 0: chat (chat box), 1: system message (chat box), 2: above hotbar
|
||||
}
|
||||
impl Into<Vec<u8>> for ClientboundChatMessage {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x02 Clientbound Chat Message.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.text));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.position));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ClientboundChatMessage {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for ClientboundChatMessage {
|
||||
fn new() -> Self {
|
||||
ClientboundChatMessage {
|
||||
text: MCChat { text: "".into() },
|
||||
position: 0.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x02
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut clientboundchatmessage = ClientboundChatMessage::new();
|
||||
clientboundchatmessage.text = MCChat::read(t).await?;
|
||||
clientboundchatmessage.position = MCByte::read(t).await?;
|
||||
Ok(clientboundchatmessage)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/// The packets that get sent to the client by the server.
|
||||
pub mod clientbound;
|
||||
/// The packets that get sent to the server by the client.
|
||||
pub mod serverbound;
|
||||
|
||||
use crate::mctypes::MCVarInt;
|
||||
pub use clientbound::*;
|
||||
use core::convert::TryFrom;
|
||||
pub use serverbound::*;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
/// A helper function to read the packet header.
|
||||
pub async fn read_packet_header(t: &mut TcpStream) -> tokio::io::Result<(MCVarInt, MCVarInt)> {
|
||||
let length = MCVarInt::read(t).await?;
|
||||
let id = MCVarInt::read(t).await?;
|
||||
Ok((length, id))
|
||||
}
|
||||
|
||||
/// A way to generically encode a packet.
|
||||
macro_rules! register_packets {
|
||||
($($name:ident),*) => {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Packet {
|
||||
$($name($name),)*
|
||||
Null,
|
||||
}
|
||||
impl Packet {
|
||||
pub fn new() -> Packet {
|
||||
Packet::Null
|
||||
}
|
||||
pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
match self {
|
||||
$(
|
||||
Packet::$name(p) => p.write(t).await,
|
||||
)*
|
||||
Packet::Null => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for Packet {
|
||||
fn default() -> Self {
|
||||
Packet::Null
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $name {
|
||||
pub fn as_packet(&self) -> Packet {
|
||||
Packet::$name(self.clone())
|
||||
}
|
||||
}
|
||||
impl Into<Packet> for $name {
|
||||
fn into(self) -> Packet {
|
||||
Packet::$name(self.clone())
|
||||
}
|
||||
}
|
||||
impl TryFrom<Packet> for $name {
|
||||
type Error = &'static str;
|
||||
fn try_from(p: Packet) -> Result<Self, Self::Error> {
|
||||
match p {
|
||||
Packet::$name(i) => Ok(i),
|
||||
_ => Err("wrong kind"),
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// Register all the packets.
|
||||
register_packets!(
|
||||
// Clientbound.
|
||||
StatusResponse,
|
||||
StatusPong,
|
||||
LoginSuccess,
|
||||
LoginDisconnect,
|
||||
JoinGame,
|
||||
HeldItemChange,
|
||||
EntityStatus,
|
||||
ClientboundPlayerPositionAndLook,
|
||||
SpawnPosition,
|
||||
KeepAlivePing,
|
||||
Disconnect,
|
||||
ClientboundChatMessage,
|
||||
// Serverbound.
|
||||
Handshake,
|
||||
StatusRequest,
|
||||
StatusPing,
|
||||
LoginStart,
|
||||
ClientSettings,
|
||||
KeepAlivePong,
|
||||
ServerboundChatMessage,
|
||||
Player,
|
||||
PlayerPosition,
|
||||
PlayerLook,
|
||||
ServerboundPlayerPositionAndLook
|
||||
);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait PacketCommon: Into<Packet> + core::fmt::Debug + Clone
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn new() -> Self;
|
||||
fn id() -> u8;
|
||||
fn as_packet(&self) -> Packet {
|
||||
self.clone().into()
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self>;
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()>;
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for Packet {
|
||||
fn new() -> Self {
|
||||
Packet::new()
|
||||
}
|
||||
fn id() -> u8 {
|
||||
255 // The generic `Packet` doesn't really have an id, but I can't leave it blank.
|
||||
}
|
||||
async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
panic!("cannot PacketCommon::read a generic Packet")
|
||||
}
|
||||
async fn write(&self, _t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
panic!("cannot PacketCommon::write a generic Packet")
|
||||
}
|
||||
}
|
@ -1,542 +0,0 @@
|
||||
use super::PacketCommon;
|
||||
use crate::mctypes::*;
|
||||
use std::convert::{Into, TryFrom};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
/// Needed for every interaction with the server.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Handshake {
|
||||
pub protocol_version: MCVarInt,
|
||||
pub server_address: MCString,
|
||||
pub server_port: MCUnsignedShort,
|
||||
pub next_state: MCVarInt,
|
||||
}
|
||||
impl Into<Vec<u8>> for Handshake {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Handshake.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.protocol_version));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.server_address));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.server_port));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.next_state));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for Handshake {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for Handshake {
|
||||
fn new() -> Self {
|
||||
Handshake {
|
||||
protocol_version: 0.into(),
|
||||
server_address: "".into(),
|
||||
server_port: 0.into(),
|
||||
next_state: 0.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut handshake = Handshake::new();
|
||||
handshake.protocol_version = MCVarInt::read(t).await?;
|
||||
handshake.server_address = MCString::read(t).await?;
|
||||
handshake.server_port = MCUnsignedShort::read(t).await?;
|
||||
handshake.next_state = MCVarInt::read(t).await?;
|
||||
Ok(handshake)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusRequest {}
|
||||
impl Into<Vec<u8>> for StatusRequest {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Status Request.
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for StatusRequest {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for StatusRequest {
|
||||
fn new() -> Self {
|
||||
StatusRequest {}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let statusrequest = StatusRequest::new();
|
||||
Ok(statusrequest)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusPing {
|
||||
pub payload: MCLong,
|
||||
}
|
||||
impl Into<Vec<u8>> for StatusPing {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x01 Status Pong.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.payload));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for StatusPing {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for StatusPing {
|
||||
fn new() -> Self {
|
||||
StatusPing { payload: 0.into() }
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x01
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut statusping = StatusPing::new();
|
||||
statusping.payload = MCLong::read(t).await?;
|
||||
Ok(statusping)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoginStart {
|
||||
pub player_name: MCString,
|
||||
}
|
||||
impl Into<Vec<u8>> for LoginStart {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Login Start.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.player_name));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for LoginStart {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for LoginStart {
|
||||
fn new() -> Self {
|
||||
LoginStart {
|
||||
player_name: "".into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut loginstart = LoginStart::new();
|
||||
loginstart.player_name = MCString::read(t).await?;
|
||||
Ok(loginstart)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientSettings {
|
||||
pub locale: MCString,
|
||||
pub view_distance: MCByte,
|
||||
pub chat_mode: MCVarInt, // 0: enabled, 1: commands only, 2: hidden.
|
||||
pub chat_colors: MCBoolean,
|
||||
pub displayed_skin_parts: MCUnsignedByte, // Bit mask
|
||||
// Displayed skin parts flags:
|
||||
// Bit 0 (0x01): Cape enabled
|
||||
// Bit 1 (0x02): Jacket enabled
|
||||
// Bit 2 (0x04): Left Sleeve enabled
|
||||
// Bit 3 (0x08): Right Sleeve enabled
|
||||
// Bit 4 (0x10): Left Pants Leg enabled
|
||||
// Bit 5 (0x20): Right Pants Leg enabled
|
||||
// Bit 6 (0x40): Hat enabled
|
||||
}
|
||||
impl Into<Vec<u8>> for ClientSettings {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x15 Client Settings.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.locale));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.view_distance));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.chat_mode));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.chat_colors));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.displayed_skin_parts));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ClientSettings {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for ClientSettings {
|
||||
fn new() -> Self {
|
||||
ClientSettings {
|
||||
locale: "en_US".into(),
|
||||
view_distance: 8.into(), // 8 chunks.
|
||||
chat_mode: 0.into(), // All chat enabled.
|
||||
chat_colors: true.into(),
|
||||
displayed_skin_parts: 0xff.into(), // Enable all parts.
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x15
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut clientsettings = ClientSettings::new();
|
||||
clientsettings.locale = MCString::read(t).await?;
|
||||
clientsettings.view_distance = MCByte::read(t).await?;
|
||||
clientsettings.chat_mode = MCVarInt::read(t).await?;
|
||||
clientsettings.chat_colors = MCBoolean::read(t).await?;
|
||||
clientsettings.displayed_skin_parts = MCUnsignedByte::read(t).await?;
|
||||
Ok(clientsettings)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeepAlivePong {
|
||||
pub payload: MCVarInt,
|
||||
}
|
||||
impl Into<Vec<u8>> for KeepAlivePong {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x00 Keep Alive.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.payload));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for KeepAlivePong {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for KeepAlivePong {
|
||||
fn new() -> Self {
|
||||
KeepAlivePong { payload: 0.into() }
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x00
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut keepalive = KeepAlivePong::new();
|
||||
keepalive.payload = MCVarInt::read(t).await?;
|
||||
Ok(keepalive)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerboundChatMessage {
|
||||
pub text: MCString,
|
||||
}
|
||||
impl Into<Vec<u8>> for ServerboundChatMessage {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x01 Serverbound Chat Message.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.text));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ServerboundChatMessage {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for ServerboundChatMessage {
|
||||
fn new() -> Self {
|
||||
ServerboundChatMessage { text: "".into() }
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x01
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut serverboundchatmessage = ServerboundChatMessage::new();
|
||||
serverboundchatmessage.text = MCString::read(t).await?;
|
||||
Ok(serverboundchatmessage)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Player {
|
||||
pub on_ground: MCBoolean,
|
||||
}
|
||||
impl Into<Vec<u8>> for Player {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x03 Player.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.on_ground));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for Player {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for Player {
|
||||
fn new() -> Self {
|
||||
Player {
|
||||
on_ground: false.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x03
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut player = Player::new();
|
||||
player.on_ground = MCBoolean::read(t).await?;
|
||||
Ok(player)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlayerPosition {
|
||||
pub x: MCDouble,
|
||||
pub y: MCDouble,
|
||||
pub z: MCDouble,
|
||||
pub on_ground: MCBoolean,
|
||||
}
|
||||
impl Into<Vec<u8>> for PlayerPosition {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x04 Player Position.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.x));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.y));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.z));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.on_ground));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for PlayerPosition {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for PlayerPosition {
|
||||
fn new() -> Self {
|
||||
PlayerPosition {
|
||||
x: 0.0.into(),
|
||||
y: 0.0.into(),
|
||||
z: 0.0.into(),
|
||||
on_ground: false.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x04
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut playerposition = PlayerPosition::new();
|
||||
playerposition.x = MCDouble::read(t).await?;
|
||||
playerposition.y = MCDouble::read(t).await?;
|
||||
playerposition.z = MCDouble::read(t).await?;
|
||||
playerposition.on_ground = MCBoolean::read(t).await?;
|
||||
Ok(playerposition)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlayerLook {
|
||||
pub yaw: MCFloat,
|
||||
pub pitch: MCFloat,
|
||||
pub on_ground: MCBoolean,
|
||||
}
|
||||
impl Into<Vec<u8>> for PlayerLook {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x05 Player Look.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.yaw));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.pitch));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.on_ground));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for PlayerLook {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for PlayerLook {
|
||||
fn new() -> Self {
|
||||
PlayerLook {
|
||||
yaw: 0.0.into(),
|
||||
pitch: 0.0.into(),
|
||||
on_ground: false.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x05
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut playerlook = PlayerLook::new();
|
||||
playerlook.yaw = MCFloat::read(t).await?;
|
||||
playerlook.pitch = MCFloat::read(t).await?;
|
||||
playerlook.on_ground = MCBoolean::read(t).await?;
|
||||
Ok(playerlook)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerboundPlayerPositionAndLook {
|
||||
pub x: MCDouble,
|
||||
pub y: MCDouble,
|
||||
pub z: MCDouble,
|
||||
pub yaw: MCFloat,
|
||||
pub pitch: MCFloat,
|
||||
pub on_ground: MCBoolean,
|
||||
}
|
||||
impl Into<Vec<u8>> for ServerboundPlayerPositionAndLook {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut out = vec![];
|
||||
let mut temp: Vec<u8> = MCVarInt::from(Self::id()).into(); // 0x06 Serverbound Player Position And Look.
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.x));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.y));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.z));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.yaw));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.pitch));
|
||||
temp.extend_from_slice(&Into::<Vec<u8>>::into(self.on_ground));
|
||||
out.extend_from_slice(&Into::<Vec<u8>>::into(MCVarInt::from(temp.len() as i32)));
|
||||
out.extend_from_slice(&temp);
|
||||
out
|
||||
}
|
||||
}
|
||||
impl TryFrom<Vec<u8>> for ServerboundPlayerPositionAndLook {
|
||||
type Error = &'static str;
|
||||
fn try_from(_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Err("unimplemented")
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PacketCommon for ServerboundPlayerPositionAndLook {
|
||||
fn new() -> Self {
|
||||
ServerboundPlayerPositionAndLook {
|
||||
x: 0.0.into(),
|
||||
y: 0.0.into(),
|
||||
z: 0.0.into(),
|
||||
yaw: 0.0.into(),
|
||||
pitch: 0.0.into(),
|
||||
on_ground: false.into(),
|
||||
}
|
||||
}
|
||||
fn id() -> u8 {
|
||||
0x06
|
||||
}
|
||||
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
|
||||
let mut playerpositionandlook = ServerboundPlayerPositionAndLook::new();
|
||||
playerpositionandlook.x = MCDouble::read(t).await?;
|
||||
playerpositionandlook.y = MCDouble::read(t).await?;
|
||||
playerpositionandlook.z = MCDouble::read(t).await?;
|
||||
playerpositionandlook.yaw = MCFloat::read(t).await?;
|
||||
playerpositionandlook.pitch = MCFloat::read(t).await?;
|
||||
playerpositionandlook.on_ground = MCBoolean::read(t).await?;
|
||||
Ok(playerpositionandlook)
|
||||
}
|
||||
async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> {
|
||||
for b in Into::<Vec<u8>>::into(self.clone()) {
|
||||
write_byte(t, b).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/// Used to store a point in a world.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Location {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub pitch: f32,
|
||||
pub yaw: f32,
|
||||
// TODO: Store a reference to the world this location is in.
|
||||
}
|
||||
impl Location {
|
||||
/// Create a new `Location`.
|
||||
pub fn new(x: f64, y: f64, z: f64, pitch: f32, yaw: f32) -> Location {
|
||||
Location {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
pitch,
|
||||
yaw,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no rotation.
|
||||
pub fn position(x: f64, y: f64, z: f64) -> Location {
|
||||
Location {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no position.
|
||||
pub fn rotation(pitch: f32, yaw: f32) -> Location {
|
||||
Location {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
pitch,
|
||||
yaw,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Location` with no rotation or position.
|
||||
pub fn zero() -> Location {
|
||||
Location {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
pub mod location;
|
Loading…
x
Reference in New Issue
Block a user