mirror of
https://github.com/GIKExe/rust_mc_serv.git
synced 2025-06-24 10:22:57 +03:00
Compare commits
10 Commits
a664c43887
...
a746a0d9b4
Author | SHA1 | Date | |
---|---|---|---|
a746a0d9b4 | |||
8b5084460e | |||
4036be8cc1 | |||
3ff2af9e28 | |||
221a8dd849 | |||
fc93e519b5 | |||
f4873b1f21 | |||
68fa183c0e | |||
b83efa34cb | |||
a7636c4028 |
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -109,6 +109,15 @@ version = "2.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.17.0"
|
version = "3.17.0"
|
||||||
@ -214,6 +223,16 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
@ -273,6 +292,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@ -330,6 +359,16 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -495,6 +534,16 @@ version = "0.4.27"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-5"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@ -967,6 +1016,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -984,6 +1039,15 @@ name = "uuid"
|
|||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
||||||
|
dependencies = [
|
||||||
|
"md-5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
cargo-features = ["edition2024"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rust_mc_serv"
|
name = "rust_mc_serv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -15,7 +17,7 @@ palette = "0.7.6"
|
|||||||
craftflow-nbt = "2.1.0"
|
craftflow-nbt = "2.1.0"
|
||||||
colog = "1.3.0"
|
colog = "1.3.0"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
uuid = "1.16.0"
|
uuid = { version = "1.16.0", features = ["v3"] }
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
ignore-result = "0.2.0"
|
ignore-result = "0.2.0"
|
||||||
|
@ -46,6 +46,8 @@ rust_mc_serv = { git = "https://github.com/GIKExe/rust_mc_serv.git" }
|
|||||||
let config = Arc::new(Config::default());
|
let config = Arc::new(Config::default());
|
||||||
let mut server = ServerContext::new(config);
|
let mut server = ServerContext::new(config);
|
||||||
|
|
||||||
|
server.add_packet_handler(Box::new(PlayHandler)); // Добавляем дефолтную обработку режима Play
|
||||||
|
|
||||||
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
||||||
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Использование:
|
# Использование:
|
||||||
#
|
#
|
||||||
# ./parse_ids.py < Packets.html > src/server/protocol/id.rs
|
# ./parse_ids.py < Packets.html > src/server/protocol/packet_id.rs
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
with import <nixpkgs> { };
|
with import <nixpkgs> { };
|
||||||
|
|
||||||
mkShell {
|
mkShell {
|
||||||
@ -11,6 +10,4 @@ mkShell {
|
|||||||
python3Packages.beautifulsoup4
|
python3Packages.beautifulsoup4
|
||||||
python3Packages.requests
|
python3Packages.requests
|
||||||
];
|
];
|
||||||
|
|
||||||
NIX_ENFORCE_PURITY = true;
|
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
use std::{net::SocketAddr, sync::Arc};
|
use std::{
|
||||||
|
net::SocketAddr,
|
||||||
|
sync::{Arc, atomic::AtomicI32},
|
||||||
|
};
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -15,6 +18,7 @@ use super::{
|
|||||||
pub struct ServerContext {
|
pub struct ServerContext {
|
||||||
pub config: Arc<Config>,
|
pub config: Arc<Config>,
|
||||||
pub clients: DashMap<SocketAddr, Arc<ClientContext>>,
|
pub clients: DashMap<SocketAddr, Arc<ClientContext>>,
|
||||||
|
pub world: WorldContext,
|
||||||
listeners: Vec<Box<dyn Listener>>,
|
listeners: Vec<Box<dyn Listener>>,
|
||||||
handlers: Vec<Box<dyn PacketHandler>>,
|
handlers: Vec<Box<dyn PacketHandler>>,
|
||||||
}
|
}
|
||||||
@ -26,6 +30,7 @@ impl ServerContext {
|
|||||||
listeners: Vec::new(),
|
listeners: Vec::new(),
|
||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
clients: DashMap::new(),
|
clients: DashMap::new(),
|
||||||
|
world: WorldContext::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,3 +97,15 @@ impl ServerContext {
|
|||||||
self.listeners.iter().sorted_by_key(sort_by).collect_vec()
|
self.listeners.iter().sorted_by_key(sort_by).collect_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WorldContext {
|
||||||
|
pub entity_id_counter: AtomicI32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldContext {
|
||||||
|
pub fn new() -> WorldContext {
|
||||||
|
WorldContext {
|
||||||
|
entity_id_counter: AtomicI32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -58,7 +58,9 @@ impl TextComponent {
|
|||||||
.collect::<Vec<TextComponent>>();
|
.collect::<Vec<TextComponent>>();
|
||||||
|
|
||||||
let mut parent = children[0].clone();
|
let mut parent = children[0].clone();
|
||||||
|
if children.len() > 1 {
|
||||||
parent.extra = Some(children[1..].to_vec());
|
parent.extra = Some(children[1..].to_vec());
|
||||||
|
}
|
||||||
parent
|
parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ pub mod config;
|
|||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
pub mod play;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use rust_mc_serv::{
|
|||||||
context::ServerContext,
|
context::ServerContext,
|
||||||
data::text_component::TextComponent,
|
data::text_component::TextComponent,
|
||||||
event::{Listener, PacketHandler},
|
event::{Listener, PacketHandler},
|
||||||
|
play::PlayHandler,
|
||||||
player::context::ClientContext,
|
player::context::ClientContext,
|
||||||
protocol::ConnectionState,
|
protocol::ConnectionState,
|
||||||
start_server,
|
start_server,
|
||||||
@ -153,6 +154,8 @@ fn main() {
|
|||||||
// Передается во все подключения
|
// Передается во все подключения
|
||||||
let mut server = ServerContext::new(config);
|
let mut server = ServerContext::new(config);
|
||||||
|
|
||||||
|
server.add_packet_handler(Box::new(PlayHandler)); // Добавляем дефолтную обработку режима Play
|
||||||
|
|
||||||
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
||||||
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
||||||
|
|
||||||
|
51
src/play/config.rs
Normal file
51
src/play/config.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use std::io::Cursor;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rust_mc_proto::{DataWriter, Packet, read_packet};
|
||||||
|
|
||||||
|
use crate::protocol::packet_id::*;
|
||||||
|
use crate::{ServerError, player::context::ClientContext};
|
||||||
|
|
||||||
|
pub fn send_update_tags(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
|
// TODO: rewrite this hardcode bullshit
|
||||||
|
|
||||||
|
client.write_packet(&Packet::from_bytes(
|
||||||
|
clientbound::configuration::UPDATE_TAGS,
|
||||||
|
include_bytes!("update-tags.bin"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_registry_data(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
|
// TODO: rewrite this hardcode bullshit
|
||||||
|
|
||||||
|
let mut registry_data = Cursor::new(include_bytes!("registry-data.bin"));
|
||||||
|
|
||||||
|
while let Ok(mut packet) = read_packet(&mut registry_data, None) {
|
||||||
|
packet.set_id(clientbound::configuration::REGISTRY_DATA);
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавки в Configuration стейт чтобы все работало
|
||||||
|
pub fn handle_configuration_state(
|
||||||
|
client: Arc<ClientContext>, // Контекст клиента
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS);
|
||||||
|
packet.write_varint(1)?;
|
||||||
|
packet.write_string("minecraft:vanilla")?;
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS);
|
||||||
|
packet.write_varint(1)?;
|
||||||
|
packet.write_string("minecraft")?;
|
||||||
|
packet.write_string("core")?;
|
||||||
|
packet.write_string("1.21.5")?;
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
|
||||||
|
client.read_packet(&[serverbound::configuration::KNOWN_PACKS])?;
|
||||||
|
|
||||||
|
send_registry_data(client.clone())?;
|
||||||
|
send_update_tags(client.clone())
|
||||||
|
}
|
109
src/play/helper.rs
Normal file
109
src/play/helper.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ServerError,
|
||||||
|
data::{ReadWriteNBT, text_component::TextComponent},
|
||||||
|
player::context::ClientContext,
|
||||||
|
protocol::packet_id::{clientbound, serverbound},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn send_game_event(
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
event: u8,
|
||||||
|
value: f32,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::GAME_EVENT);
|
||||||
|
|
||||||
|
packet.write_byte(event)?;
|
||||||
|
packet.write_float(value)?;
|
||||||
|
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sync_player_pos(
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
z: f64,
|
||||||
|
vel_x: f64,
|
||||||
|
vel_y: f64,
|
||||||
|
vel_z: f64,
|
||||||
|
yaw: f32,
|
||||||
|
pitch: f32,
|
||||||
|
flags: i32,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let timestamp = (SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis()
|
||||||
|
& 0xFFFFFFFF) as i32;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SYNCHRONIZE_PLAYER_POSITION);
|
||||||
|
|
||||||
|
packet.write_varint(timestamp)?;
|
||||||
|
packet.write_double(x)?;
|
||||||
|
packet.write_double(y)?;
|
||||||
|
packet.write_double(z)?;
|
||||||
|
packet.write_double(vel_x)?;
|
||||||
|
packet.write_double(vel_y)?;
|
||||||
|
packet.write_double(vel_z)?;
|
||||||
|
packet.write_float(yaw)?;
|
||||||
|
packet.write_float(pitch)?;
|
||||||
|
packet.write_int(flags)?;
|
||||||
|
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_center_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SET_CENTER_CHUNK);
|
||||||
|
|
||||||
|
packet.write_varint(x)?;
|
||||||
|
packet.write_varint(z)?;
|
||||||
|
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_keep_alive(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
|
let timestamp = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs() as i64;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE);
|
||||||
|
packet.write_long(timestamp)?;
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = client.read_packet(&[serverbound::play::KEEP_ALIVE])?;
|
||||||
|
let timestamp2 = packet.read_long()?;
|
||||||
|
if timestamp2 != timestamp {
|
||||||
|
// Послать клиента нахуй
|
||||||
|
Err(ServerError::WrongPacket)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_system_message(
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
message: TextComponent,
|
||||||
|
is_action_bar: bool,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE);
|
||||||
|
packet.write_nbt(&message)?;
|
||||||
|
packet.write_boolean(is_action_bar)?;
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unload_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::UNLOAD_CHUNK);
|
||||||
|
packet.write_int(z)?;
|
||||||
|
packet.write_int(x)?;
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
453
src/play/mod.rs
Normal file
453
src/play/mod.rs
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::{sync::Arc, thread, time::Duration};
|
||||||
|
|
||||||
|
use config::handle_configuration_state;
|
||||||
|
use helper::{
|
||||||
|
send_game_event, send_keep_alive, send_system_message, set_center_chunk, sync_player_pos,
|
||||||
|
unload_chunk,
|
||||||
|
};
|
||||||
|
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::player::context::EntityInfo;
|
||||||
|
use crate::{
|
||||||
|
ServerError, data::text_component::TextComponent, event::PacketHandler,
|
||||||
|
player::context::ClientContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::protocol::{ConnectionState, packet_id::*};
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod helper;
|
||||||
|
pub mod planner;
|
||||||
|
|
||||||
|
pub struct PlayHandler;
|
||||||
|
|
||||||
|
impl PacketHandler for PlayHandler {
|
||||||
|
fn on_outcoming_packet(
|
||||||
|
&self,
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
packet: &mut Packet,
|
||||||
|
cancel: &mut bool,
|
||||||
|
state: ConnectionState,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
if !*cancel // проверяем что пакет не отмененный, облегчаем себе задачу, ведь проверять айди наверняка сложней
|
||||||
|
&& state == ConnectionState::Configuration // проверяем стейт, т.к айди могут быть одинаковыми между стейтами
|
||||||
|
&& packet.id() == clientbound::configuration::FINISH
|
||||||
|
{
|
||||||
|
handle_configuration_state(client)?; // делаем наши грязные дела
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_state(
|
||||||
|
&self,
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
state: ConnectionState,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
if state == ConnectionState::Play {
|
||||||
|
// перешли в режим плей, отлично! делаем дела
|
||||||
|
|
||||||
|
handle_play_state(client)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
|
// Отправка пакета Login
|
||||||
|
let mut packet = Packet::empty(clientbound::play::LOGIN);
|
||||||
|
|
||||||
|
packet.write_int(client.entity_info().entity_id)?; // Entity ID
|
||||||
|
packet.write_boolean(false)?; // Is hardcore
|
||||||
|
packet.write_varint(4)?; // Dimension Names
|
||||||
|
packet.write_string("minecraft:overworld")?;
|
||||||
|
packet.write_string("minecraft:nether")?;
|
||||||
|
packet.write_string("minecraft:the_end")?;
|
||||||
|
packet.write_string("minecraft:overworld_caves")?;
|
||||||
|
packet.write_varint(0)?; // Max Players
|
||||||
|
packet.write_varint(8)?; // View Distance
|
||||||
|
packet.write_varint(5)?; // Simulation Distance
|
||||||
|
packet.write_boolean(false)?; // Reduced Debug Info
|
||||||
|
packet.write_boolean(true)?; // Enable respawn screen
|
||||||
|
packet.write_boolean(false)?; // Do limited crafting
|
||||||
|
|
||||||
|
packet.write_varint(0)?; // Dimension Type
|
||||||
|
packet.write_string("minecraft:overworld")?; // Dimension Name
|
||||||
|
packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed
|
||||||
|
packet.write_byte(0)?; // Game mode
|
||||||
|
packet.write_signed_byte(-1)?; // Previous Game mode
|
||||||
|
packet.write_boolean(false)?; // Is Debug
|
||||||
|
packet.write_boolean(true)?; // Is Flat
|
||||||
|
packet.write_boolean(false)?; // Has death location
|
||||||
|
packet.write_varint(20)?; // Portal cooldown
|
||||||
|
packet.write_varint(60)?; // Sea level
|
||||||
|
|
||||||
|
packet.write_boolean(false)?; // Enforces Secure Chat
|
||||||
|
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_example_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::CHUNK_DATA_AND_UPDATE_LIGHT);
|
||||||
|
|
||||||
|
packet.write_int(x)?;
|
||||||
|
packet.write_int(z)?;
|
||||||
|
|
||||||
|
// heightmap
|
||||||
|
|
||||||
|
packet.write_varint(1)?; // heightmaps count
|
||||||
|
packet.write_varint(0)?; // MOTION_BLOCKING - 0
|
||||||
|
// bits per entry is ceil(log2(385)) = 9 where 385 is the world height
|
||||||
|
// so, the length of the following array is (9 * 16 * 16) / 8 = 37
|
||||||
|
// ... idk how it came to that
|
||||||
|
packet.write_varint(37)?; // Length of the following long array
|
||||||
|
for _ in 0..37 {
|
||||||
|
packet.write_long(0)?; // THIS WORKS ONLY BECAUSE OUR HEIGHT IS 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// sending chunk data
|
||||||
|
|
||||||
|
let mut chunk_data = Vec::new();
|
||||||
|
|
||||||
|
// we want to fill the area from -64 to 0, so it will be 4 chunk sections
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks
|
||||||
|
|
||||||
|
// blocks paletted container
|
||||||
|
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
||||||
|
chunk_data.write_varint(10)?; // block state id in the registry (1 for stone, 10 for dirt)
|
||||||
|
|
||||||
|
// biomes palleted container
|
||||||
|
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
||||||
|
chunk_data.write_varint(1)?; // biome id in the registry
|
||||||
|
}
|
||||||
|
|
||||||
|
// air chunk sections
|
||||||
|
|
||||||
|
for _ in 0..20 {
|
||||||
|
chunk_data.write_short(0)?; // non-air blocks count, 0
|
||||||
|
|
||||||
|
// blocks paletted container
|
||||||
|
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
||||||
|
chunk_data.write_varint(0)?; // block state id in the registry (0 for air)
|
||||||
|
|
||||||
|
// biomes palleted container
|
||||||
|
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
||||||
|
chunk_data.write_varint(27)?; // biome id in the registry
|
||||||
|
}
|
||||||
|
|
||||||
|
packet.write_usize_varint(chunk_data.len())?;
|
||||||
|
packet.write_bytes(&chunk_data)?;
|
||||||
|
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
|
||||||
|
// light data
|
||||||
|
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
packet.write_byte(0)?;
|
||||||
|
|
||||||
|
client.write_packet(&packet)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_example_chunks_in_distance(
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
chunks: &mut Vec<(i32, i32)>,
|
||||||
|
distance: i32,
|
||||||
|
center: (i32, i32),
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut new_chunks = Vec::new();
|
||||||
|
|
||||||
|
for x in -distance + center.0..=distance + center.0 {
|
||||||
|
for z in -distance + center.1..=distance + center.1 {
|
||||||
|
if !chunks.contains(&(x, z)) {
|
||||||
|
send_example_chunk(client.clone(), x as i32, z as i32)?;
|
||||||
|
}
|
||||||
|
new_chunks.push((x, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x, z) in chunks.iter() {
|
||||||
|
if !new_chunks.contains(&(*x, *z)) {
|
||||||
|
unload_chunk(client.clone(), *x, *z)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*chunks = new_chunks;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_player(
|
||||||
|
receiver: Arc<ClientContext>,
|
||||||
|
player: Arc<ClientContext>,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::PLAYER_INFO_UPDATE);
|
||||||
|
|
||||||
|
packet.write_byte(0x01)?; // only Add Player
|
||||||
|
packet.write_varint(1)?; // players list
|
||||||
|
packet.write_uuid(&player.entity_info().uuid)?; // player uuid
|
||||||
|
packet.write_string(&player.player_info().unwrap().name)?; // player name
|
||||||
|
packet.write_varint(0)?; // no properties
|
||||||
|
|
||||||
|
receiver.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SPAWN_ENTITY);
|
||||||
|
|
||||||
|
let (x, y, z) = player.entity_info().position();
|
||||||
|
let (yaw, pitch) = player.entity_info().rotation();
|
||||||
|
let (vel_x, vel_y, vel_z) = player.entity_info().velocity();
|
||||||
|
|
||||||
|
packet.write_varint(player.entity_info().entity_id)?; // Entity ID
|
||||||
|
packet.write_uuid(&player.entity_info().uuid)?; // Entity UUID
|
||||||
|
packet.write_varint(148)?; // Entity type TODO: move to const
|
||||||
|
packet.write_double(x)?;
|
||||||
|
packet.write_double(y)?;
|
||||||
|
packet.write_double(z)?;
|
||||||
|
packet.write_signed_byte((pitch / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?; // head yaw TODO: make player head yaw field
|
||||||
|
packet.write_varint(0)?;
|
||||||
|
packet.write_short(vel_x as i16)?;
|
||||||
|
packet.write_short(vel_y as i16)?;
|
||||||
|
packet.write_short(vel_z as i16)?;
|
||||||
|
|
||||||
|
receiver.write_packet(&packet)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_offline_uuid(name: &str) -> Uuid {
|
||||||
|
let mut namespaces_bytes: [u8; 16] = [0; 16];
|
||||||
|
for (i, byte) in format!("OfflinePlayer:{}", &name[..2])
|
||||||
|
.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
namespaces_bytes[i] = *byte;
|
||||||
|
}
|
||||||
|
let namespace = Uuid::from_bytes(namespaces_bytes);
|
||||||
|
Uuid::new_v3(&namespace, (&name[2..]).as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отдельная функция для работы с самой игрой
|
||||||
|
pub fn handle_play_state(
|
||||||
|
client: Arc<ClientContext>, // Контекст клиента
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
client.set_entity_info(EntityInfo::new(
|
||||||
|
client
|
||||||
|
.server
|
||||||
|
.world
|
||||||
|
.entity_id_counter
|
||||||
|
.fetch_add(1, Ordering::SeqCst),
|
||||||
|
get_offline_uuid(&client.player_info().unwrap().name), // TODO: authenticated uuid
|
||||||
|
));
|
||||||
|
|
||||||
|
client.entity_info().set_position((8.0, 0.0, 8.0)); // set 8 0 8 as position
|
||||||
|
|
||||||
|
thread::spawn({
|
||||||
|
let client = client.clone();
|
||||||
|
|
||||||
|
move || {
|
||||||
|
let _ = client.run_read_loop();
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
send_login(client.clone())?;
|
||||||
|
sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?;
|
||||||
|
send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks
|
||||||
|
set_center_chunk(client.clone(), 0, 0)?;
|
||||||
|
|
||||||
|
let mut chunks = Vec::new();
|
||||||
|
|
||||||
|
let view_distance = client.client_info().unwrap().view_distance as i32 / 2;
|
||||||
|
|
||||||
|
send_example_chunks_in_distance(client.clone(), &mut chunks, view_distance, (0, 0))?;
|
||||||
|
|
||||||
|
// sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?;
|
||||||
|
|
||||||
|
send_system_message(
|
||||||
|
client.clone(),
|
||||||
|
TextComponent::rainbow(format!("Your Name: {}", client.player_info().unwrap().name)),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
send_system_message(
|
||||||
|
client.clone(),
|
||||||
|
TextComponent::rainbow(format!(
|
||||||
|
"Your Entity ID: {}",
|
||||||
|
client.entity_info().entity_id
|
||||||
|
)),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
send_system_message(
|
||||||
|
client.clone(),
|
||||||
|
TextComponent::rainbow(format!("Your UUID: {}", client.entity_info().uuid)),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for player in client.server.players() {
|
||||||
|
if client.addr == player.addr {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
send_player(client.clone(), player.clone())?;
|
||||||
|
send_player(player.clone(), client.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::spawn({
|
||||||
|
let client = client.clone();
|
||||||
|
|
||||||
|
move || -> Result<(), ServerError> {
|
||||||
|
while client.is_alive() {
|
||||||
|
let mut packet = client.read_packet(&[
|
||||||
|
serverbound::play::SET_PLAYER_POSITION,
|
||||||
|
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION,
|
||||||
|
serverbound::play::SET_PLAYER_ROTATION,
|
||||||
|
])?;
|
||||||
|
|
||||||
|
match packet.id() {
|
||||||
|
serverbound::play::SET_PLAYER_POSITION => {
|
||||||
|
let x = packet.read_double()?;
|
||||||
|
let y = packet.read_double()?;
|
||||||
|
let z = packet.read_double()?;
|
||||||
|
let flags = packet.read_byte()?; // flags
|
||||||
|
|
||||||
|
let prev = client.entity_info().position();
|
||||||
|
|
||||||
|
for player in client.server.players() {
|
||||||
|
if client.addr == player.addr {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::UPDATE_ENTITY_POSITION);
|
||||||
|
packet.write_varint(client.entity_info().entity_id)?;
|
||||||
|
packet.write_short((x * 4096.0 - prev.0 * 4096.0) as i16)?; // formula: currentX * 4096 - prevX * 4096
|
||||||
|
packet.write_short((y * 4096.0 - prev.1 * 4096.0) as i16)?;
|
||||||
|
packet.write_short((z * 4096.0 - prev.2 * 4096.0) as i16)?;
|
||||||
|
packet.write_boolean(flags & 0x01 != 0)?;
|
||||||
|
player.write_packet(&packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.entity_info().set_position((x, y, z));
|
||||||
|
}
|
||||||
|
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => {
|
||||||
|
let x = packet.read_double()?;
|
||||||
|
let y = packet.read_double()?;
|
||||||
|
let z = packet.read_double()?;
|
||||||
|
let yaw = packet.read_float()?;
|
||||||
|
let pitch = packet.read_float()?;
|
||||||
|
let flags = packet.read_byte()?; // flags
|
||||||
|
|
||||||
|
let prev = client.entity_info().position();
|
||||||
|
|
||||||
|
for player in client.server.players() {
|
||||||
|
if client.addr == player.addr {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut packet =
|
||||||
|
Packet::empty(clientbound::play::UPDATE_ENTITY_POSITION_AND_ROTATION);
|
||||||
|
packet.write_varint(client.entity_info().entity_id)?;
|
||||||
|
packet.write_short((x * 4096.0 - prev.0 * 4096.0) as i16)?; // formula: currentX * 4096 - prevX * 4096
|
||||||
|
packet.write_short((y * 4096.0 - prev.1 * 4096.0) as i16)?;
|
||||||
|
packet.write_short((z * 4096.0 - prev.2 * 4096.0) as i16)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_signed_byte((pitch / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_boolean(flags & 0x01 != 0)?;
|
||||||
|
player.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SET_HEAD_ROTATION);
|
||||||
|
packet.write_varint(client.entity_info().entity_id)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?;
|
||||||
|
player.write_packet(&packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.entity_info().set_position((x, y, z));
|
||||||
|
client.entity_info().set_rotation((yaw, pitch));
|
||||||
|
}
|
||||||
|
serverbound::play::SET_PLAYER_ROTATION => {
|
||||||
|
let yaw = packet.read_float()?;
|
||||||
|
let pitch = packet.read_float()?;
|
||||||
|
let flags = packet.read_byte()?; // flags
|
||||||
|
|
||||||
|
for player in client.server.players() {
|
||||||
|
if client.addr == player.addr {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::UPDATE_ENTITY_ROTATION);
|
||||||
|
packet.write_varint(client.entity_info().entity_id)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_signed_byte((pitch / 360.0 * 256.0) as i8)?;
|
||||||
|
packet.write_boolean(flags & 0x01 != 0)?;
|
||||||
|
player.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::SET_HEAD_ROTATION);
|
||||||
|
packet.write_varint(client.entity_info().entity_id)?;
|
||||||
|
packet.write_signed_byte((yaw / 360.0 * 256.0) as i8)?;
|
||||||
|
player.write_packet(&packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.entity_info().set_rotation((yaw, pitch));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut ticks_alive = 0u64;
|
||||||
|
|
||||||
|
while client.is_alive() {
|
||||||
|
if ticks_alive % 200 == 0 {
|
||||||
|
// 10 secs timer
|
||||||
|
send_keep_alive(client.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ticks_alive % 20 == 0 {
|
||||||
|
// 1 sec timer
|
||||||
|
let (x, _, z) = client.entity_info().position();
|
||||||
|
|
||||||
|
let (chunk_x, chunk_z) = ((x / 16.0) as i64, (z / 16.0) as i64);
|
||||||
|
let (chunk_x, chunk_z) = (chunk_x as i32, chunk_z as i32);
|
||||||
|
|
||||||
|
set_center_chunk(client.clone(), chunk_x, chunk_z)?;
|
||||||
|
send_example_chunks_in_distance(
|
||||||
|
client.clone(),
|
||||||
|
&mut chunks,
|
||||||
|
view_distance,
|
||||||
|
(chunk_x, chunk_z),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// text animation
|
||||||
|
{
|
||||||
|
let animation_text = format!("Ticks alive: {} жёпа", ticks_alive);
|
||||||
|
let animation_index = ((ticks_alive + 40) % 300) as usize;
|
||||||
|
let animation_end = animation_text.len() + 20;
|
||||||
|
|
||||||
|
if animation_index < animation_end {
|
||||||
|
let now_length = (animation_index + 1).min(animation_text.chars().count());
|
||||||
|
let now_text = animation_text.chars().take(now_length).collect();
|
||||||
|
|
||||||
|
send_system_message(client.clone(), TextComponent::rainbow(now_text), true)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(50)); // 1 tick
|
||||||
|
ticks_alive += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
1
src/play/planner.rs
Normal file
1
src/play/planner.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
// TODO: thread-safe planner like BukkitScheduler
|
@ -29,9 +29,7 @@ pub struct ClientContext {
|
|||||||
packet_buffer: Mutex<VecDeque<Packet>>,
|
packet_buffer: Mutex<VecDeque<Packet>>,
|
||||||
read_loop: AtomicBool,
|
read_loop: AtomicBool,
|
||||||
is_alive: AtomicBool,
|
is_alive: AtomicBool,
|
||||||
position: RwLock<(f64, f64, f64)>,
|
entity_info: RwLock<Option<Arc<EntityInfo>>>,
|
||||||
velocity: RwLock<(f64, f64, f64)>,
|
|
||||||
rotation: RwLock<(f32, f32)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Реализуем сравнение через адрес
|
// Реализуем сравнение через адрес
|
||||||
@ -63,9 +61,7 @@ impl ClientContext {
|
|||||||
packet_buffer: Mutex::new(VecDeque::new()),
|
packet_buffer: Mutex::new(VecDeque::new()),
|
||||||
read_loop: AtomicBool::new(false),
|
read_loop: AtomicBool::new(false),
|
||||||
is_alive: AtomicBool::new(true),
|
is_alive: AtomicBool::new(true),
|
||||||
position: RwLock::new((0.0, 0.0, 0.0)),
|
entity_info: RwLock::new(None),
|
||||||
velocity: RwLock::new((0.0, 0.0, 0.0)),
|
|
||||||
rotation: RwLock::new((0.0, 0.0)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +77,10 @@ impl ClientContext {
|
|||||||
*self.player_info.write().unwrap() = Some(player_info);
|
*self.player_info.write().unwrap() = Some(player_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_entity_info(self: &Arc<Self>, entity_info: EntityInfo) {
|
||||||
|
*self.entity_info.write().unwrap() = Some(Arc::new(entity_info));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_state(self: &Arc<Self>, state: ConnectionState) -> Result<(), ServerError> {
|
pub fn set_state(self: &Arc<Self>, state: ConnectionState) -> Result<(), ServerError> {
|
||||||
*self.state.write().unwrap() = state.clone();
|
*self.state.write().unwrap() = state.clone();
|
||||||
|
|
||||||
@ -107,34 +107,14 @@ impl ClientContext {
|
|||||||
self.player_info.read().unwrap().clone()
|
self.player_info.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entity_info(self: &Arc<Self>) -> Arc<EntityInfo> {
|
||||||
|
self.entity_info.read().unwrap().clone().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn state(self: &Arc<Self>) -> ConnectionState {
|
pub fn state(self: &Arc<Self>) -> ConnectionState {
|
||||||
self.state.read().unwrap().clone()
|
self.state.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(self: &Arc<Self>, position: (f64, f64, f64)) {
|
|
||||||
*self.position.write().unwrap() = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_velocity(self: &Arc<Self>, velocity: (f64, f64, f64)) {
|
|
||||||
*self.velocity.write().unwrap() = velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_rotation(self: &Arc<Self>, rotation: (f32, f32)) {
|
|
||||||
*self.rotation.write().unwrap() = rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(self: &Arc<Self>) -> (f64, f64, f64) {
|
|
||||||
self.position.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn velocity(self: &Arc<Self>) -> (f64, f64, f64) {
|
|
||||||
self.velocity.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rotation(self: &Arc<Self>) -> (f32, f32) {
|
|
||||||
self.rotation.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_packet(self: &Arc<Self>, packet: &Packet) -> Result<(), ServerError> {
|
pub fn write_packet(self: &Arc<Self>, packet: &Packet) -> Result<(), ServerError> {
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
let mut packet = packet.clone();
|
let mut packet = packet.clone();
|
||||||
@ -304,3 +284,47 @@ pub struct PlayerInfo {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EntityInfo {
|
||||||
|
pub entity_id: i32,
|
||||||
|
pub uuid: Uuid,
|
||||||
|
position: RwLock<(f64, f64, f64)>,
|
||||||
|
velocity: RwLock<(f64, f64, f64)>,
|
||||||
|
rotation: RwLock<(f32, f32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityInfo {
|
||||||
|
pub fn new(entity_id: i32, uuid: Uuid) -> EntityInfo {
|
||||||
|
EntityInfo {
|
||||||
|
entity_id,
|
||||||
|
uuid,
|
||||||
|
position: RwLock::new((0.0, 0.0, 0.0)),
|
||||||
|
velocity: RwLock::new((0.0, 0.0, 0.0)),
|
||||||
|
rotation: RwLock::new((0.0, 0.0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(self: &Arc<Self>, position: (f64, f64, f64)) {
|
||||||
|
*self.position.write().unwrap() = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_velocity(self: &Arc<Self>, velocity: (f64, f64, f64)) {
|
||||||
|
*self.velocity.write().unwrap() = velocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_rotation(self: &Arc<Self>, rotation: (f32, f32)) {
|
||||||
|
*self.rotation.write().unwrap() = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(self: &Arc<Self>) -> (f64, f64, f64) {
|
||||||
|
self.position.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn velocity(self: &Arc<Self>) -> (f64, f64, f64) {
|
||||||
|
self.velocity.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotation(self: &Arc<Self>) -> (f32, f32) {
|
||||||
|
self.rotation.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
ServerError,
|
ServerError,
|
||||||
data::{ReadWriteNBT, text_component::TextComponent},
|
data::{ReadWriteNBT, text_component::TextComponent},
|
||||||
protocol::{
|
protocol::{
|
||||||
id::{clientbound, serverbound},
|
packet_id::{clientbound, serverbound},
|
||||||
*,
|
*,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -8,12 +8,9 @@ use rust_mc_proto::{DataReader, DataWriter, Packet};
|
|||||||
|
|
||||||
use crate::trigger_event;
|
use crate::trigger_event;
|
||||||
|
|
||||||
use super::{
|
use super::{ConnectionState, packet_id::*};
|
||||||
ConnectionState,
|
|
||||||
id::*,
|
|
||||||
play::{handle_configuration_state, handle_play_state},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// TODO: move brand to the config
|
||||||
pub const BRAND: &str = "rust_mc_serv";
|
pub const BRAND: &str = "rust_mc_serv";
|
||||||
|
|
||||||
pub fn handle_connection(
|
pub fn handle_connection(
|
||||||
@ -171,15 +168,15 @@ pub fn handle_connection(
|
|||||||
},
|
},
|
||||||
)?)?;
|
)?)?;
|
||||||
|
|
||||||
handle_configuration_state(client.clone())?;
|
|
||||||
|
|
||||||
client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
||||||
|
|
||||||
|
// На этом моменте пакет хандер ловит пакет и перед ним делает свое мракобесие
|
||||||
|
|
||||||
client.read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?;
|
client.read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?;
|
||||||
|
|
||||||
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
|
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
|
||||||
|
|
||||||
// Дальше работаем с режимом игры
|
// Тут работают уже приколы из пакет хандлера
|
||||||
handle_play_state(client)?;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Тип подключения не рукопожатный
|
// Тип подключения не рукопожатный
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod id;
|
pub mod packet_id;
|
||||||
pub mod play;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ConnectionState {
|
pub enum ConnectionState {
|
||||||
Handshake,
|
Handshake,
|
||||||
Status,
|
Status,
|
||||||
|
@ -1,378 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io::Cursor,
|
|
||||||
sync::Arc,
|
|
||||||
thread,
|
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
|
||||||
};
|
|
||||||
|
|
||||||
use rust_mc_proto::{DataReader, DataWriter, Packet, read_packet};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ServerError,
|
|
||||||
data::{ReadWriteNBT, text_component::TextComponent},
|
|
||||||
player::context::ClientContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::id::*;
|
|
||||||
|
|
||||||
pub fn send_update_tags(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
|
||||||
// TODO: rewrite this hardcode bullshit
|
|
||||||
|
|
||||||
client.write_packet(&Packet::from_bytes(
|
|
||||||
clientbound::configuration::UPDATE_TAGS,
|
|
||||||
include_bytes!("update-tags.bin"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_registry_data(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
|
||||||
// TODO: rewrite this hardcode bullshit
|
|
||||||
|
|
||||||
let mut registry_data = Cursor::new(include_bytes!("registry-data.bin"));
|
|
||||||
|
|
||||||
while let Ok(mut packet) = read_packet(&mut registry_data, None) {
|
|
||||||
packet.set_id(clientbound::configuration::REGISTRY_DATA);
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Добавки в Configuration стейт чтобы все работало
|
|
||||||
pub fn handle_configuration_state(
|
|
||||||
client: Arc<ClientContext>, // Контекст клиента
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS);
|
|
||||||
packet.write_varint(1)?;
|
|
||||||
packet.write_string("minecraft:vanilla")?;
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
|
|
||||||
let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS);
|
|
||||||
packet.write_varint(1)?;
|
|
||||||
packet.write_string("minecraft")?;
|
|
||||||
packet.write_string("core")?;
|
|
||||||
packet.write_string("1.21.5")?;
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
|
|
||||||
client.read_packet(&[serverbound::configuration::KNOWN_PACKS])?;
|
|
||||||
|
|
||||||
send_registry_data(client.clone())?;
|
|
||||||
send_update_tags(client.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
|
||||||
// Отправка пакета Login
|
|
||||||
let mut packet = Packet::empty(clientbound::play::LOGIN);
|
|
||||||
|
|
||||||
packet.write_int(0)?; // Entity ID
|
|
||||||
packet.write_boolean(false)?; // Is hardcore
|
|
||||||
packet.write_varint(4)?; // Dimension Names
|
|
||||||
packet.write_string("minecraft:overworld")?;
|
|
||||||
packet.write_string("minecraft:nether")?;
|
|
||||||
packet.write_string("minecraft:the_end")?;
|
|
||||||
packet.write_string("minecraft:overworld_caves")?;
|
|
||||||
packet.write_varint(0)?; // Max Players
|
|
||||||
packet.write_varint(8)?; // View Distance
|
|
||||||
packet.write_varint(5)?; // Simulation Distance
|
|
||||||
packet.write_boolean(false)?; // Reduced Debug Info
|
|
||||||
packet.write_boolean(true)?; // Enable respawn screen
|
|
||||||
packet.write_boolean(false)?; // Do limited crafting
|
|
||||||
|
|
||||||
packet.write_varint(0)?; // Dimension Type
|
|
||||||
packet.write_string("minecraft:overworld")?; // Dimension Name
|
|
||||||
packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed
|
|
||||||
packet.write_byte(0)?; // Game mode
|
|
||||||
packet.write_signed_byte(-1)?; // Previous Game mode
|
|
||||||
packet.write_boolean(false)?; // Is Debug
|
|
||||||
packet.write_boolean(true)?; // Is Flat
|
|
||||||
packet.write_boolean(false)?; // Has death location
|
|
||||||
packet.write_varint(20)?; // Portal cooldown
|
|
||||||
packet.write_varint(60)?; // Sea level
|
|
||||||
|
|
||||||
packet.write_boolean(false)?; // Enforces Secure Chat
|
|
||||||
|
|
||||||
client.write_packet(&packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_game_event(
|
|
||||||
client: Arc<ClientContext>,
|
|
||||||
event: u8,
|
|
||||||
value: f32,
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
let mut packet = Packet::empty(clientbound::play::GAME_EVENT);
|
|
||||||
|
|
||||||
packet.write_byte(event)?;
|
|
||||||
packet.write_float(value)?;
|
|
||||||
|
|
||||||
client.write_packet(&packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sync_player_pos(
|
|
||||||
client: Arc<ClientContext>,
|
|
||||||
x: f64,
|
|
||||||
y: f64,
|
|
||||||
z: f64,
|
|
||||||
vel_x: f64,
|
|
||||||
vel_y: f64,
|
|
||||||
vel_z: f64,
|
|
||||||
yaw: f32,
|
|
||||||
pitch: f32,
|
|
||||||
flags: i32,
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
let timestamp = (SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis()
|
|
||||||
& 0xFFFFFFFF) as i32;
|
|
||||||
|
|
||||||
let mut packet = Packet::empty(clientbound::play::SYNCHRONIZE_PLAYER_POSITION);
|
|
||||||
|
|
||||||
packet.write_varint(timestamp)?;
|
|
||||||
packet.write_double(x)?;
|
|
||||||
packet.write_double(y)?;
|
|
||||||
packet.write_double(z)?;
|
|
||||||
packet.write_double(vel_x)?;
|
|
||||||
packet.write_double(vel_y)?;
|
|
||||||
packet.write_double(vel_z)?;
|
|
||||||
packet.write_float(yaw)?;
|
|
||||||
packet.write_float(pitch)?;
|
|
||||||
packet.write_int(flags)?;
|
|
||||||
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_center_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> {
|
|
||||||
let mut packet = Packet::empty(clientbound::play::SET_CENTER_CHUNK);
|
|
||||||
|
|
||||||
packet.write_varint(x)?;
|
|
||||||
packet.write_varint(z)?;
|
|
||||||
|
|
||||||
client.write_packet(&packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_example_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> {
|
|
||||||
let mut packet = Packet::empty(clientbound::play::CHUNK_DATA_AND_UPDATE_LIGHT);
|
|
||||||
|
|
||||||
packet.write_int(x)?;
|
|
||||||
packet.write_int(z)?;
|
|
||||||
|
|
||||||
// heightmap
|
|
||||||
|
|
||||||
packet.write_varint(1)?; // heightmaps count
|
|
||||||
packet.write_varint(0)?; // MOTION_BLOCKING
|
|
||||||
packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256)
|
|
||||||
for _ in 0..256 {
|
|
||||||
packet.write_long(0)?; // height - 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// sending chunk data
|
|
||||||
|
|
||||||
let mut chunk_data = Vec::new();
|
|
||||||
|
|
||||||
// we want to fill the area from -64 to 0, so it will be 4 chunk sections
|
|
||||||
|
|
||||||
for _ in 0..4 {
|
|
||||||
chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks
|
|
||||||
|
|
||||||
// blocks paletted container
|
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
|
||||||
chunk_data.write_varint(10)?; // block state id in the registry (1 for stone)
|
|
||||||
|
|
||||||
// biomes palleted container
|
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
|
||||||
chunk_data.write_varint(27)?; // biome id in the registry
|
|
||||||
}
|
|
||||||
|
|
||||||
// air chunk sections
|
|
||||||
|
|
||||||
for _ in 0..20 {
|
|
||||||
chunk_data.write_short(0)?; // non-air blocks count, 0
|
|
||||||
|
|
||||||
// blocks paletted container
|
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
|
||||||
chunk_data.write_varint(0)?; // block state id in the registry (0 for air)
|
|
||||||
|
|
||||||
// biomes palleted container
|
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
|
||||||
chunk_data.write_varint(27)?; // biome id in the registry
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.write_usize_varint(chunk_data.len())?;
|
|
||||||
packet.write_bytes(&chunk_data)?;
|
|
||||||
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
|
|
||||||
// light data
|
|
||||||
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
packet.write_byte(0)?;
|
|
||||||
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_keep_alive(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
|
||||||
let timestamp = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs() as i64;
|
|
||||||
|
|
||||||
let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE);
|
|
||||||
packet.write_long(timestamp)?;
|
|
||||||
client.write_packet(&packet)?;
|
|
||||||
|
|
||||||
let mut packet = client.read_packet(&[serverbound::play::KEEP_ALIVE])?;
|
|
||||||
let timestamp2 = packet.read_long()?;
|
|
||||||
if timestamp2 != timestamp {
|
|
||||||
// Послать клиента нахуй
|
|
||||||
Err(ServerError::WrongPacket)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_system_message(
|
|
||||||
client: Arc<ClientContext>,
|
|
||||||
message: TextComponent,
|
|
||||||
is_action_bar: bool,
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE);
|
|
||||||
packet.write_nbt(&message)?;
|
|
||||||
packet.write_boolean(is_action_bar)?;
|
|
||||||
client.write_packet(&packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_example_chunks_in_distance(
|
|
||||||
client: Arc<ClientContext>,
|
|
||||||
chunks: &mut Vec<(i32, i32)>,
|
|
||||||
distance: i32,
|
|
||||||
center: (i32, i32),
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
for x in -distance + center.0..=distance + center.0 {
|
|
||||||
for z in -distance + center.1..=distance + center.1 {
|
|
||||||
if !chunks.contains(&(x, z)) {
|
|
||||||
send_example_chunk(client.clone(), x as i32, z as i32)?;
|
|
||||||
chunks.push((x, z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Отдельная функция для работы с самой игрой
|
|
||||||
pub fn handle_play_state(
|
|
||||||
client: Arc<ClientContext>, // Контекст клиента
|
|
||||||
) -> Result<(), ServerError> {
|
|
||||||
thread::spawn({
|
|
||||||
let client = client.clone();
|
|
||||||
|
|
||||||
move || {
|
|
||||||
let _ = client.run_read_loop();
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
send_login(client.clone())?;
|
|
||||||
sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?;
|
|
||||||
send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks
|
|
||||||
set_center_chunk(client.clone(), 0, 0)?;
|
|
||||||
|
|
||||||
let mut chunks = Vec::new();
|
|
||||||
|
|
||||||
let view_distance = client.client_info().unwrap().view_distance as i32;
|
|
||||||
|
|
||||||
send_example_chunks_in_distance(client.clone(), &mut chunks, view_distance, (0, 0))?;
|
|
||||||
|
|
||||||
thread::spawn({
|
|
||||||
let client = client.clone();
|
|
||||||
|
|
||||||
move || -> Result<(), ServerError> {
|
|
||||||
while client.is_alive() {
|
|
||||||
let mut packet = client.read_any_packet()?;
|
|
||||||
|
|
||||||
match packet.id() {
|
|
||||||
serverbound::play::SET_PLAYER_POSITION => {
|
|
||||||
let x = packet.read_double()?;
|
|
||||||
let y = packet.read_double()?;
|
|
||||||
let z = packet.read_double()?;
|
|
||||||
let _ = packet.read_byte()?; // flags
|
|
||||||
|
|
||||||
client.set_position((x, y, z));
|
|
||||||
}
|
|
||||||
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => {
|
|
||||||
let x = packet.read_double()?;
|
|
||||||
let y = packet.read_double()?;
|
|
||||||
let z = packet.read_double()?;
|
|
||||||
let yaw = packet.read_float()?;
|
|
||||||
let pitch = packet.read_float()?;
|
|
||||||
let _ = packet.read_byte()?; // flags
|
|
||||||
|
|
||||||
client.set_position((x, y, z));
|
|
||||||
client.set_rotation((yaw, pitch));
|
|
||||||
}
|
|
||||||
serverbound::play::SET_PLAYER_ROTATION => {
|
|
||||||
let yaw = packet.read_float()?;
|
|
||||||
let pitch = packet.read_float()?;
|
|
||||||
let _ = packet.read_byte()?; // flags
|
|
||||||
|
|
||||||
client.set_rotation((yaw, pitch));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
client.push_packet_back(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut ticks_alive = 0u64;
|
|
||||||
|
|
||||||
while client.is_alive() {
|
|
||||||
if ticks_alive % 200 == 0 {
|
|
||||||
// 10 secs timer
|
|
||||||
send_keep_alive(client.clone())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ticks_alive % 20 == 0 {
|
|
||||||
// 1 sec timer
|
|
||||||
let (x, y, z) = client.position();
|
|
||||||
|
|
||||||
let (chunk_x, chunk_z) = ((x / 16.0) as i64, (z / 16.0) as i64);
|
|
||||||
let (chunk_x, chunk_z) = (chunk_x as i32, chunk_z as i32);
|
|
||||||
|
|
||||||
set_center_chunk(client.clone(), chunk_x, chunk_z)?;
|
|
||||||
send_example_chunks_in_distance(
|
|
||||||
client.clone(),
|
|
||||||
&mut chunks,
|
|
||||||
view_distance,
|
|
||||||
(chunk_x, chunk_z),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
send_system_message(
|
|
||||||
client.clone(),
|
|
||||||
TextComponent::rainbow(format!("Pos: {} {} {}", x as i64, y as i64, z as i64)),
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
send_system_message(
|
|
||||||
client.clone(),
|
|
||||||
TextComponent::rainbow(format!("Ticks alive: {}", ticks_alive)),
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(50)); // 1 tick
|
|
||||||
ticks_alive += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user