diff --git a/Cargo.lock b/Cargo.lock index 58003be..9e158fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,73 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "cc" +version = "1.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crc32fast" version = "1.4.2" @@ -23,6 +84,57 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "flate2" version = "1.1.1" @@ -33,12 +145,110 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", + "serde", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + [[package]] name = "memchr" version = "2.7.4" @@ -54,6 +264,33 @@ dependencies = [ "adler2", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -89,8 +326,15 @@ dependencies = [ "rust_mc_proto", "serde", "serde_json", + "serde_with", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" version = "1.0.20" @@ -129,6 +373,48 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.101" @@ -140,6 +426,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "unicode-ident" version = "1.0.18" @@ -151,3 +468,120 @@ name = "uuid" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml index 570035b..16f3fdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ edition = "2024" [dependencies] rust_mc_proto = "0.1.19" -serde = "1.0.219" # used in text component +serde = { version = "1.0.219", features = ["derive"] } # used in text component serde_json = "1.0.140" +serde_with = { version = "3.12.0", features = ["macros"] } \ No newline at end of file diff --git a/src/data.rs b/src/data.rs index 269a3fa..8c0e0a0 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,13 +1,17 @@ use std::{error::Error, fmt::Display}; +use serde::{Deserialize, Serialize}; -use rust_mc_proto::ProtocolError; +use rust_mc_proto::{DataReader, DataWriter, ProtocolError}; +use serde_with::skip_serializing_none; // Ошибки сервера #[derive(Debug)] pub enum ServerError { UnknownPacket(String), Protocol(ProtocolError), - ConnectionClosed + ConnectionClosed, + SerTextComponent, + DeTextComponent } impl Display for ServerError { @@ -22,7 +26,7 @@ impl Error for ServerError {} impl From for ServerError { fn from(error: ProtocolError) -> ServerError { match error { - // Если просто закрыто соединение, пеерделываем в нашу ошибку этого + // Если просто закрыто соединение, переделываем в нашу ошибку этого ProtocolError::ConnectionClosedError => { ServerError::ConnectionClosed }, @@ -32,4 +36,157 @@ impl From for ServerError { }, } } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[skip_serializing_none] +pub struct TextComponent { + pub text: String, + pub color: Option, + pub bold: Option, + pub italic: Option, + pub underlined: Option, + pub strikethrough: Option, + pub obfuscated: Option, + pub extra: Option>, +} + +impl TextComponent { + pub fn builder() -> TextComponentBuilder { + TextComponentBuilder::new() + } + + pub fn to_string(self) -> Result { + self.try_into() + } + + pub fn from_string(text: String) -> Result { + Self::try_from(text) + } +} + +pub trait WriteTextComponent { + fn write_text_component(&mut self, component: &TextComponent) -> Result<(), ServerError>; +} + +impl WriteTextComponent for T { + fn write_text_component(&mut self, component: &TextComponent) -> Result<(), ServerError> { + Ok(self.write_string(TryInto::::try_into(component.clone())?.as_str())?) + } +} + +pub trait ReadTextComponent { + fn read_text_component(&mut self) -> Result; +} + +impl ReadTextComponent for T { + fn read_text_component(&mut self) -> Result { + TextComponent::try_from(self.read_string()?) + } +} + +impl TryInto for TextComponent { + type Error = ServerError; + + fn try_into(self) -> Result { + serde_json::to_string(&self) + .map_err(|_| ServerError::SerTextComponent) + } +} + +impl TryFrom for TextComponent { + type Error = ServerError; + + fn try_from(value: String) -> Result { + serde_json::from_str(&value) + .map_err(|_| ServerError::DeTextComponent) + } +} + +impl TryFrom<&str> for TextComponent { + type Error = ServerError; + + fn try_from(value: &str) -> Result { + serde_json::from_str(&value) + .map_err(|_| ServerError::DeTextComponent) + } +} + +pub struct TextComponentBuilder { + text: String, + color: Option, + bold: Option, + italic: Option, + underlined: Option, + strikethrough: Option, + obfuscated: Option, + extra: Option>, +} + +impl TextComponentBuilder { + pub fn new() -> Self { + Self { + text: String::new(), + color: None, + bold: None, + italic: None, + underlined: None, + strikethrough: None, + obfuscated: None, + extra: None, + } + } + + pub fn text(mut self, text: String) -> Self { + self.text = text; + self + } + + pub fn color(mut self, color: String) -> Self { + self.color = Some(color); + self + } + + pub fn bold(mut self, bold: bool) -> Self { + self.bold = Some(bold); + self + } + + pub fn italic(mut self, italic: bool) -> Self { + self.italic = Some(italic); + self + } + + pub fn underlined(mut self, underlined: bool) -> Self { + self.underlined = Some(underlined); + self + } + + pub fn strikethrough(mut self, strikethrough: bool) -> Self { + self.strikethrough = Some(strikethrough); + self + } + + pub fn obfuscated(mut self, obfuscated: bool) -> Self { + self.obfuscated = Some(obfuscated); + self + } + + pub fn extra(mut self, extra: Vec) -> Self { + self.extra = Some(extra); + self + } + + pub fn build(self) -> TextComponent { + TextComponent { + text: self.text, + color: self.color, + bold: self.bold, + italic: self.italic, + underlined: self.underlined, + strikethrough: self.strikethrough, + obfuscated: self.obfuscated, + extra: self.extra + } + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 12678fa..2a1c6ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::{io::{Read, Write}, net::TcpListener, thread, time::Duration}; use rust_mc_proto::{DataReader, DataWriter, MinecraftConnection, Packet}; -use data::ServerError; +use data::{ServerError, TextComponent}; use pohuy::Pohuy; pub mod pohuy; @@ -29,8 +29,13 @@ fn main() { stream.set_write_timeout(Some(Duration::from_secs(5))).pohuy(); // Обработка подключения - // Если ошибка -> похуй - handle_connection(MinecraftConnection::new(&stream)).pohuy(); + // Если ошибка -> выводим + match handle_connection(MinecraftConnection::new(&stream)) { + Ok(_) => {}, + Err(error) => { + println!("Ошибка подключения: {error:?}"); + }, + }; println!("Отключение: {}", addr); }); @@ -60,35 +65,45 @@ fn handle_connection( match packet.id() { 0x00 => { // Запрос статуса - conn.write_packet(&Packet::build(0x00, |packet| { - // Отправка статуса - // В будущем это надо будет переделать чтобы это отправлялось через Listener'ы а не самим ядром сервера - // Хотя можно сделать и дефолтное значение через конфиг - packet.write_string(&format!( - // Пример статуса с дебаг-инфой - "{{ - \"version\": {{ - \"name\": \"1.21.5\", - \"protocol\": {protocol_version} - }}, - \"players\": {{ - \"max\": 100, - \"online\": 5, - \"sample\": [ - {{ - \"name\": \"thinkofdeath\", - \"id\": \"4566e69f-c907-48ee-8d71-d7ba5aa00d20\" - }} - ] - }}, - \"description\": {{ - \"text\": \"pv: {protocol_version}, sp: {server_port}\nsa: {server_address}\" - }}, - \"favicon\": \"data:image/png;base64,\", - \"enforcesSecureChat\": false - }}" - )) - })?)?; + let mut packet = Packet::empty(0x00); + + // Отправка статуса + // В будущем это надо будет переделать чтобы это отправлялось через Listener'ы а не самим ядром сервера + // Хотя можно сделать и дефолтное значение через конфиг + packet.write_string(&format!( + // Пример статуса + "{{ + \"version\": {{ + \"name\": \"1.21.5\", + \"protocol\": {protocol_version} + }}, + \"players\": {{ + \"max\": 100, + \"online\": 5, + \"sample\": [ + {{ + \"name\": \"thinkofdeath\", + \"id\": \"4566e69f-c907-48ee-8d71-d7ba5aa00d20\" + }} + ] + }}, + \"description\": {}, + \"favicon\": \"data:image/png;base64,\", + \"enforcesSecureChat\": false + }}", + + // В MOTD пихаем дебаг инфу + TextComponent::builder() + .text(format!("pv: {protocol_version}, sp: {server_port}\nsa: {server_address}")) + .color("red".to_string()) + .bold(true) + .italic(true) + .underlined(true) + .build() + .to_string()? + ))?; + + conn.write_packet(&packet)?; }, 0x01 => { // Пинг conn.write_packet(&packet)?; @@ -104,9 +119,16 @@ fn handle_connection( 2 | 3 => { // Тип подключения - игра // Отключение игрока с сообщением // Заглушка так сказать - conn.write_packet(&Packet::build(0x00, |packet| { - packet.write_string("{\"text\": \"This server is in developement!!\", \"color\": \"red\", \"bold\": true}") - })?)?; + let mut packet = Packet::empty(0x00); + + packet.write_string(&TextComponent::builder() + .text(format!("This server is in developement!!")) + .color("gold".to_string()) + .bold(true) + .build() + .to_string()?)?; + + conn.write_packet(&packet)?; // TODO: Чтение Configuration (возможно с примешиванием Listener'ов) // TODO: Обработчик пакетов Play (тоже трейт), который уже будет дергать Listener'ы