mirror of
https://github.com/GIKExe/rust_mc_serv.git
synced 2025-06-24 10:22:57 +03:00
Compare commits
15 Commits
a746a0d9b4
...
a1b0da37d9
Author | SHA1 | Date | |
---|---|---|---|
a1b0da37d9 | |||
424b33d47c | |||
abb0e019af | |||
4df4f2ec91 | |||
8081720ea6 | |||
caea03c0c7 | |||
6dcb061271 | |||
03b1a25c06 | |||
32e7737131 | |||
03a23eb267 | |||
9d6c0cd04d | |||
7f6bc59d14 | |||
dc02f22545 | |||
934ca660c4 | |||
0266958443 |
@ -1,5 +1,3 @@
|
|||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rust_mc_serv"
|
name = "rust_mc_serv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -46,7 +46,9 @@ 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
|
// Добавляем дефолтную обработку режима Play
|
||||||
|
server.add_packet_handler(Box::new(PlayHandler));
|
||||||
|
server.add_listener(Box::new(PlayListener));
|
||||||
|
|
||||||
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)); // Добавляем пример пакет хандлера
|
||||||
|
@ -38,6 +38,7 @@ impl ServerContext {
|
|||||||
self
|
self
|
||||||
.clients
|
.clients
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|o| o.entity_info_opt().is_some())
|
||||||
.find(|o| {
|
.find(|o| {
|
||||||
let info = o.player_info();
|
let info = o.player_info();
|
||||||
if let Some(info) = info {
|
if let Some(info) = info {
|
||||||
@ -53,6 +54,7 @@ impl ServerContext {
|
|||||||
self
|
self
|
||||||
.clients
|
.clients
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|o| o.entity_info_opt().is_some())
|
||||||
.find(|o| {
|
.find(|o| {
|
||||||
let info = o.player_info();
|
let info = o.player_info();
|
||||||
if let Some(info) = info {
|
if let Some(info) = info {
|
||||||
@ -69,6 +71,7 @@ impl ServerContext {
|
|||||||
.clients
|
.clients
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|o| o.player_info().is_some())
|
.filter(|o| o.player_info().is_some())
|
||||||
|
.filter(|o| o.entity_info_opt().is_some())
|
||||||
.map(|o| o.clone())
|
.map(|o| o.clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,35 @@ impl TextComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rainbow_offset(text: String, offset: i64) -> TextComponent {
|
||||||
|
if text.is_empty() {
|
||||||
|
return TextComponent::new(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = text
|
||||||
|
.char_indices()
|
||||||
|
.map(|(i, c)| {
|
||||||
|
let hue = (((i as i64 + offset) % text.chars().count() as i64) as f32)
|
||||||
|
/ (text.chars().count() as f32)
|
||||||
|
* 360.0;
|
||||||
|
let hsl = Hsl::new(hue, 1.0, 0.5);
|
||||||
|
let rgb: Srgb = hsl.into_color();
|
||||||
|
let r = (rgb.red * 255.0).round() as u8;
|
||||||
|
let g = (rgb.green * 255.0).round() as u8;
|
||||||
|
let b = (rgb.blue * 255.0).round() as u8;
|
||||||
|
let mut component = TextComponent::new(c.to_string());
|
||||||
|
component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b));
|
||||||
|
component
|
||||||
|
})
|
||||||
|
.collect::<Vec<TextComponent>>();
|
||||||
|
|
||||||
|
let mut parent = children[0].clone();
|
||||||
|
if children.len() > 1 {
|
||||||
|
parent.extra = Some(children[1..].to_vec());
|
||||||
|
}
|
||||||
|
parent
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rainbow(text: String) -> TextComponent {
|
pub fn rainbow(text: String) -> TextComponent {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
return TextComponent::new(text);
|
return TextComponent::new(text);
|
@ -5,7 +5,7 @@ use rust_mc_proto::{DataReader, DataWriter, Packet};
|
|||||||
|
|
||||||
use super::ServerError;
|
use super::ServerError;
|
||||||
|
|
||||||
pub mod text_component;
|
pub mod component;
|
||||||
|
|
||||||
// Трейт для чтения NBT-совместимых приколов
|
// Трейт для чтения NBT-совместимых приколов
|
||||||
pub trait ReadWriteNBT<T>: DataReader + DataWriter {
|
pub trait ReadWriteNBT<T>: DataReader + DataWriter {
|
||||||
|
@ -37,9 +37,27 @@ macro_rules! trigger_event {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Игнорирует результат листенеров
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! trigger_event_ignore {
|
||||||
|
($client:ident, $event:ident $(, $arg_ty:expr)* $(,)?) => {{
|
||||||
|
paste::paste! {
|
||||||
|
for handler in $client.server.listeners(
|
||||||
|
|o| o.[<on_ $event _priority>]()
|
||||||
|
).iter() {
|
||||||
|
let _ = handler.[<on_ $event>](
|
||||||
|
$client.clone()
|
||||||
|
$(, $arg_ty)*
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Listener: Sync + Send {
|
pub trait Listener: Sync + Send {
|
||||||
generate_handlers!(status, &mut String);
|
generate_handlers!(status, &mut String);
|
||||||
generate_handlers!(plugin_message, &str, &[u8]);
|
generate_handlers!(plugin_message, &str, &[u8]);
|
||||||
|
generate_handlers!(disconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PacketHandler: Sync + Send {
|
pub trait PacketHandler: Sync + Send {
|
||||||
|
@ -98,6 +98,8 @@ pub fn start_server(server: Arc<ServerContext>) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trigger_event_ignore!(client, disconnect);
|
||||||
|
|
||||||
// Удаляем клиента из списка клиентов
|
// Удаляем клиента из списка клиентов
|
||||||
server.clients.remove(&client.addr);
|
server.clients.remove(&client.addr);
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ use rust_mc_serv::{
|
|||||||
ServerError,
|
ServerError,
|
||||||
config::Config,
|
config::Config,
|
||||||
context::ServerContext,
|
context::ServerContext,
|
||||||
data::text_component::TextComponent,
|
data::component::TextComponent,
|
||||||
event::{Listener, PacketHandler},
|
event::{Listener, PacketHandler},
|
||||||
play::PlayHandler,
|
play::{PlayHandler, PlayListener},
|
||||||
player::context::ClientContext,
|
player::context::ClientContext,
|
||||||
protocol::ConnectionState,
|
protocol::ConnectionState,
|
||||||
start_server,
|
start_server,
|
||||||
@ -154,7 +154,9 @@ fn main() {
|
|||||||
// Передается во все подключения
|
// Передается во все подключения
|
||||||
let mut server = ServerContext::new(config);
|
let mut server = ServerContext::new(config);
|
||||||
|
|
||||||
server.add_packet_handler(Box::new(PlayHandler)); // Добавляем дефолтную обработку режима Play
|
// Добавляем дефолтную обработку режима Play
|
||||||
|
server.add_packet_handler(Box::new(PlayHandler));
|
||||||
|
server.add_listener(Box::new(PlayListener));
|
||||||
|
|
||||||
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)); // Добавляем пример пакет хандлера
|
||||||
|
@ -7,7 +7,7 @@ use rust_mc_proto::{DataReader, DataWriter, Packet};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ServerError,
|
ServerError,
|
||||||
data::{ReadWriteNBT, text_component::TextComponent},
|
data::{ReadWriteNBT, component::TextComponent},
|
||||||
player::context::ClientContext,
|
player::context::ClientContext,
|
||||||
protocol::packet_id::{clientbound, serverbound},
|
protocol::packet_id::{clientbound, serverbound},
|
||||||
};
|
};
|
||||||
@ -25,6 +25,19 @@ pub fn send_game_event(
|
|||||||
client.write_packet(&packet)
|
client.write_packet(&packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_entity_event(
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
entity_id: i32,
|
||||||
|
status: u8,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::ENTITY_EVENT);
|
||||||
|
|
||||||
|
packet.write_int(entity_id)?;
|
||||||
|
packet.write_byte(status)?;
|
||||||
|
|
||||||
|
client.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sync_player_pos(
|
pub fn sync_player_pos(
|
||||||
client: Arc<ClientContext>,
|
client: Arc<ClientContext>,
|
||||||
x: f64,
|
x: f64,
|
||||||
|
181
src/play/mod.rs
181
src/play/mod.rs
@ -3,16 +3,16 @@ use std::{sync::Arc, thread, time::Duration};
|
|||||||
|
|
||||||
use config::handle_configuration_state;
|
use config::handle_configuration_state;
|
||||||
use helper::{
|
use helper::{
|
||||||
send_game_event, send_keep_alive, send_system_message, set_center_chunk, sync_player_pos,
|
send_entity_event, send_game_event, send_keep_alive, send_system_message, set_center_chunk,
|
||||||
unload_chunk,
|
sync_player_pos, unload_chunk,
|
||||||
};
|
};
|
||||||
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::event::Listener;
|
||||||
use crate::player::context::EntityInfo;
|
use crate::player::context::EntityInfo;
|
||||||
use crate::{
|
use crate::{
|
||||||
ServerError, data::text_component::TextComponent, event::PacketHandler,
|
ServerError, data::component::TextComponent, event::PacketHandler, player::context::ClientContext,
|
||||||
player::context::ClientContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::protocol::{ConnectionState, packet_id::*};
|
use crate::protocol::{ConnectionState, packet_id::*};
|
||||||
@ -56,6 +56,14 @@ impl PacketHandler for PlayHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PlayListener;
|
||||||
|
|
||||||
|
impl Listener for PlayListener {
|
||||||
|
fn on_disconnect(&self, client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
|
handle_disconnect(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> {
|
||||||
// Отправка пакета Login
|
// Отправка пакета Login
|
||||||
let mut packet = Packet::empty(clientbound::play::LOGIN);
|
let mut packet = Packet::empty(clientbound::play::LOGIN);
|
||||||
@ -119,7 +127,7 @@ pub fn send_example_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<
|
|||||||
|
|
||||||
// blocks paletted container
|
// blocks paletted container
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
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)
|
chunk_data.write_varint(1)?; // block state id in the registry
|
||||||
|
|
||||||
// biomes palleted container
|
// biomes palleted container
|
||||||
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
|
||||||
@ -187,6 +195,27 @@ pub fn send_example_chunks_in_distance(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_player(
|
||||||
|
receiver: Arc<ClientContext>,
|
||||||
|
player: Arc<ClientContext>,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
let mut packet = Packet::empty(clientbound::play::PLAYER_INFO_REMOVE);
|
||||||
|
|
||||||
|
packet.write_varint(1)?;
|
||||||
|
packet.write_uuid(&player.entity_info().uuid)?;
|
||||||
|
|
||||||
|
receiver.write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = Packet::empty(clientbound::play::REMOVE_ENTITIES);
|
||||||
|
|
||||||
|
packet.write_varint(1)?;
|
||||||
|
packet.write_varint(player.entity_info().entity_id)?; // Entity ID
|
||||||
|
|
||||||
|
receiver.write_packet(&packet)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_player(
|
pub fn send_player(
|
||||||
receiver: Arc<ClientContext>,
|
receiver: Arc<ClientContext>,
|
||||||
player: Arc<ClientContext>,
|
player: Arc<ClientContext>,
|
||||||
@ -239,18 +268,26 @@ pub fn get_offline_uuid(name: &str) -> Uuid {
|
|||||||
Uuid::new_v3(&namespace, (&name[2..]).as_bytes())
|
Uuid::new_v3(&namespace, (&name[2..]).as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_rainbow_message(
|
||||||
|
client: &Arc<ClientContext>,
|
||||||
|
message: String,
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
send_system_message(client.clone(), TextComponent::rainbow(message), false)
|
||||||
|
}
|
||||||
|
|
||||||
// Отдельная функция для работы с самой игрой
|
// Отдельная функция для работы с самой игрой
|
||||||
pub fn handle_play_state(
|
pub fn handle_play_state(
|
||||||
client: Arc<ClientContext>, // Контекст клиента
|
client: Arc<ClientContext>, // Контекст клиента
|
||||||
) -> Result<(), ServerError> {
|
) -> Result<(), ServerError> {
|
||||||
client.set_entity_info(EntityInfo::new(
|
let player_name = client.player_info().unwrap().name;
|
||||||
client
|
let player_uuid = get_offline_uuid(&client.player_info().unwrap().name); // TODO: authenticated uuid
|
||||||
.server
|
let entity_id = client
|
||||||
.world
|
.server
|
||||||
.entity_id_counter
|
.world
|
||||||
.fetch_add(1, Ordering::SeqCst),
|
.entity_id_counter
|
||||||
get_offline_uuid(&client.player_info().unwrap().name), // TODO: authenticated uuid
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
));
|
|
||||||
|
client.set_entity_info(EntityInfo::new(entity_id, player_uuid));
|
||||||
|
|
||||||
client.entity_info().set_position((8.0, 0.0, 8.0)); // set 8 0 8 as position
|
client.entity_info().set_position((8.0, 0.0, 8.0)); // set 8 0 8 as position
|
||||||
|
|
||||||
@ -266,6 +303,8 @@ pub fn handle_play_state(
|
|||||||
send_login(client.clone())?;
|
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)?;
|
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
|
send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks
|
||||||
|
// send_game_event(client.clone(), 3, 1.0)?; // 3 - Set gamemode, 1.0 - creative
|
||||||
|
send_entity_event(client.clone(), entity_id, 28)?; // 28 - give op level 4
|
||||||
set_center_chunk(client.clone(), 0, 0)?;
|
set_center_chunk(client.clone(), 0, 0)?;
|
||||||
|
|
||||||
let mut chunks = Vec::new();
|
let mut chunks = Vec::new();
|
||||||
@ -276,24 +315,18 @@ pub fn handle_play_state(
|
|||||||
|
|
||||||
// sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.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(
|
// send_rainbow_message(&client, format!("Your IP: {}", client.addr))?;
|
||||||
client.clone(),
|
// send_rainbow_message(
|
||||||
TextComponent::rainbow(format!("Your Name: {}", client.player_info().unwrap().name)),
|
// &client,
|
||||||
false,
|
// format!("Your brand: {}", client.client_info().unwrap().brand),
|
||||||
)?;
|
// )?;
|
||||||
send_system_message(
|
// send_rainbow_message(
|
||||||
client.clone(),
|
// &client,
|
||||||
TextComponent::rainbow(format!(
|
// format!("Your locale: {}", client.client_info().unwrap().locale),
|
||||||
"Your Entity ID: {}",
|
// )?;
|
||||||
client.entity_info().entity_id
|
// send_rainbow_message(&client, format!("Your UUID: {}", client.entity_info().uuid))?;
|
||||||
)),
|
// send_rainbow_message(&client, format!("Your Name: {}", &player_name))?;
|
||||||
false,
|
// send_rainbow_message(&client, format!("Your Entity ID: {}", entity_id))?;
|
||||||
)?;
|
|
||||||
send_system_message(
|
|
||||||
client.clone(),
|
|
||||||
TextComponent::rainbow(format!("Your UUID: {}", client.entity_info().uuid)),
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for player in client.server.players() {
|
for player in client.server.players() {
|
||||||
if client.addr == player.addr {
|
if client.addr == player.addr {
|
||||||
@ -301,6 +334,7 @@ pub fn handle_play_state(
|
|||||||
}
|
}
|
||||||
send_player(client.clone(), player.clone())?;
|
send_player(client.clone(), player.clone())?;
|
||||||
send_player(player.clone(), client.clone())?;
|
send_player(player.clone(), client.clone())?;
|
||||||
|
send_rainbow_message(&player, format!("{} joined the game", player_name))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread::spawn({
|
thread::spawn({
|
||||||
@ -312,9 +346,57 @@ pub fn handle_play_state(
|
|||||||
serverbound::play::SET_PLAYER_POSITION,
|
serverbound::play::SET_PLAYER_POSITION,
|
||||||
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION,
|
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION,
|
||||||
serverbound::play::SET_PLAYER_ROTATION,
|
serverbound::play::SET_PLAYER_ROTATION,
|
||||||
|
serverbound::play::CHAT_MESSAGE,
|
||||||
|
serverbound::play::CLICK_CONTAINER,
|
||||||
|
serverbound::play::CHAT_COMMAND,
|
||||||
|
serverbound::play::SIGNED_CHAT_COMMAND,
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
match packet.id() {
|
match packet.id() {
|
||||||
|
serverbound::play::CLICK_CONTAINER => {
|
||||||
|
let _ = packet.read_varint()?; // window id
|
||||||
|
let _ = packet.read_varint()?; // state id
|
||||||
|
let slot = packet.read_short()?; // slot
|
||||||
|
let _ = packet.read_byte()?; // button
|
||||||
|
let _ = packet.read_varint()?; // mode
|
||||||
|
// i cannot read item slots now
|
||||||
|
|
||||||
|
send_rainbow_message(&client, format!("index clicked: {slot}"))?;
|
||||||
|
}
|
||||||
|
serverbound::play::CHAT_COMMAND | serverbound::play::SIGNED_CHAT_COMMAND => {
|
||||||
|
let command = packet.read_string()?;
|
||||||
|
|
||||||
|
if command == "gamemode creative" {
|
||||||
|
send_game_event(client.clone(), 3, 1.0)?; // 3 - Set gamemode
|
||||||
|
send_rainbow_message(&client, format!("gamemode creative installed"))?;
|
||||||
|
} else if command == "gamemode survival" {
|
||||||
|
send_game_event(client.clone(), 3, 0.0)?; // 3 - Set gamemode
|
||||||
|
send_rainbow_message(&client, format!("gamemode survival installed"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serverbound::play::CHAT_MESSAGE => {
|
||||||
|
let message_text = packet.read_string()?;
|
||||||
|
// skip remaining data coz they suck
|
||||||
|
|
||||||
|
let mut message =
|
||||||
|
TextComponent::rainbow(format!("{} said: ", client.player_info().unwrap().name));
|
||||||
|
|
||||||
|
message.italic = Some(true);
|
||||||
|
|
||||||
|
let text_message = TextComponent::builder()
|
||||||
|
.color("white")
|
||||||
|
.text(&message_text)
|
||||||
|
.italic(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if let Some(extra) = &mut message.extra {
|
||||||
|
extra.push(text_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
for player in client.server.players() {
|
||||||
|
send_system_message(player, message.clone(), false)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
serverbound::play::SET_PLAYER_POSITION => {
|
serverbound::play::SET_PLAYER_POSITION => {
|
||||||
let x = packet.read_double()?;
|
let x = packet.read_double()?;
|
||||||
let y = packet.read_double()?;
|
let y = packet.read_double()?;
|
||||||
@ -433,15 +515,22 @@ pub fn handle_play_state(
|
|||||||
|
|
||||||
// text animation
|
// text animation
|
||||||
{
|
{
|
||||||
let animation_text = format!("Ticks alive: {} жёпа", ticks_alive);
|
if ticks_alive > 40 {
|
||||||
let animation_index = ((ticks_alive + 40) % 300) as usize;
|
let animation_text = format!(
|
||||||
let animation_end = animation_text.len() + 20;
|
"жёпа .-.-.-.-.-.- Ticks passed during the aliveness of the connection: {} ticks (1/20 of second) -.-.-.-.-.-. жёпа",
|
||||||
|
ticks_alive
|
||||||
|
);
|
||||||
|
|
||||||
if animation_index < animation_end {
|
let now_length = ((ticks_alive - 40 + 1) as usize).min(animation_text.chars().count());
|
||||||
let now_length = (animation_index + 1).min(animation_text.chars().count());
|
|
||||||
let now_text = animation_text.chars().take(now_length).collect();
|
let now_text = animation_text.chars().take(now_length).collect();
|
||||||
|
|
||||||
send_system_message(client.clone(), TextComponent::rainbow(now_text), true)?;
|
let mut text = TextComponent::rainbow_offset(now_text, -(ticks_alive as i64));
|
||||||
|
|
||||||
|
text.bold = Some(true);
|
||||||
|
text.italic = Some(true);
|
||||||
|
text.underlined = Some(true);
|
||||||
|
|
||||||
|
send_system_message(client.clone(), text, true)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,3 +540,21 @@ pub fn handle_play_state(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_disconnect(
|
||||||
|
client: Arc<ClientContext>, // Контекст клиента
|
||||||
|
) -> Result<(), ServerError> {
|
||||||
|
for player in client.server.players() {
|
||||||
|
if client.addr == player.addr {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_player(player.clone(), client.clone())?;
|
||||||
|
send_rainbow_message(
|
||||||
|
&player,
|
||||||
|
format!("{} left the game", client.player_info().unwrap().name),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -107,6 +107,10 @@ impl ClientContext {
|
|||||||
self.player_info.read().unwrap().clone()
|
self.player_info.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entity_info_opt(self: &Arc<Self>) -> Option<Arc<EntityInfo>> {
|
||||||
|
self.entity_info.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn entity_info(self: &Arc<Self>) -> Arc<EntityInfo> {
|
pub fn entity_info(self: &Arc<Self>) -> Arc<EntityInfo> {
|
||||||
self.entity_info.read().unwrap().clone().unwrap()
|
self.entity_info.read().unwrap().clone().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use rust_mc_proto::{DataReader, DataWriter, Packet};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ServerError,
|
ServerError,
|
||||||
data::{ReadWriteNBT, text_component::TextComponent},
|
data::{ReadWriteNBT, component::TextComponent},
|
||||||
protocol::{
|
protocol::{
|
||||||
packet_id::{clientbound, serverbound},
|
packet_id::{clientbound, serverbound},
|
||||||
*,
|
*,
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use std::{io::Read, sync::Arc};
|
use std::{
|
||||||
|
io::{Cursor, Read},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ServerError,
|
ServerError,
|
||||||
@ -129,7 +132,7 @@ pub fn handle_connection(
|
|||||||
packet.get_mut().read_to_end(&mut data).unwrap();
|
packet.get_mut().read_to_end(&mut data).unwrap();
|
||||||
|
|
||||||
if identifier == "minecraft:brand" {
|
if identifier == "minecraft:brand" {
|
||||||
break String::from_utf8_lossy(&data).to_string();
|
break Cursor::new(data).read_string()?;
|
||||||
} else {
|
} else {
|
||||||
trigger_event!(client, plugin_message, &identifier, &data);
|
trigger_event!(client, plugin_message, &identifier, &data);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user