diff options
| -rw-r--r-- | Cargo.lock | 32 | ||||
| -rw-r--r-- | minecraft-chat/Cargo.toml | 3 | ||||
| -rw-r--r-- | minecraft-chat/src/base_component.rs | 6 | ||||
| -rw-r--r-- | minecraft-chat/src/component.rs | 38 | ||||
| -rw-r--r-- | minecraft-chat/src/events.rs | 26 | ||||
| -rw-r--r-- | minecraft-chat/src/lib.rs | 8 | ||||
| -rw-r--r-- | minecraft-chat/src/mutable_component.rs | 1 | ||||
| -rw-r--r-- | minecraft-chat/src/style.rs | 98 | ||||
| -rw-r--r-- | minecraft-chat/src/text_component.rs | 17 | ||||
| -rw-r--r-- | minecraft-chat/src/translatable_component.rs | 10 | ||||
| -rw-r--r-- | minecraft-protocol/src/connection.rs | 6 | ||||
| -rw-r--r-- | minecraft-protocol/src/mc_buf.rs | 48 | ||||
| -rw-r--r-- | minecraft-protocol/src/packets/client_intention_packet.rs | 4 | ||||
| -rw-r--r-- | minecraft-protocol/src/packets/clientbound_status_response_packet.rs | 4 |
14 files changed, 289 insertions, 12 deletions
@@ -216,6 +216,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" [[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -281,6 +287,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "minecraft-chat" version = "0.1.0" +dependencies = [ + "serde_json", +] [[package]] name = "minecraft-client" @@ -476,12 +485,35 @@ dependencies = [ ] [[package]] +name = "ryu" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" + +[[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "serde_json" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/minecraft-chat/Cargo.toml b/minecraft-chat/Cargo.toml index 4607dc99..bca63666 100644 --- a/minecraft-chat/Cargo.toml +++ b/minecraft-chat/Cargo.toml @@ -1,8 +1,9 @@ [package] +edition = "2018" name = "minecraft-chat" version = "0.1.0" -edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +serde_json = "^1.0.72" diff --git a/minecraft-chat/src/base_component.rs b/minecraft-chat/src/base_component.rs new file mode 100644 index 00000000..2ed5646d --- /dev/null +++ b/minecraft-chat/src/base_component.rs @@ -0,0 +1,6 @@ +use crate::component::Component; + +pub trait BaseComponent { + const siblings: Vec<Component>; + // style: +} diff --git a/minecraft-chat/src/component.rs b/minecraft-chat/src/component.rs new file mode 100644 index 00000000..7955eeda --- /dev/null +++ b/minecraft-chat/src/component.rs @@ -0,0 +1,38 @@ +use serde_json; + +use crate::{ + base_component::BaseComponent, text_component::TextComponent, + translatable_component::TranslatableComponent, +}; + +pub struct Component {} + +// public static class Serializer +pub impl Component { + pub fn new(json: serde_json::Value) -> Self { + let component: BaseComponent; + // if it's primitive, make it a text component + if !json.is_array() && !json.is_object() { + return TextComponent::new(json.as_str().unwrap()); + } + + // if it's an object, do things with { text } and stuff + if json.is_object() { + // if it has text, + if json.get("text").is_some() { + let text = json.get("text").unwrap().to_string(); + } + } else if json.get("translate").is_some() { + let translate = json.get("translate").unwrap().to_string(); + } else if json.get("with").is_some() { + let with = json.get("with").unwrap().as_array().unwrap(); + let mut with_array = Vec::with_capacity(with.len()); + for i in 0..with.len() { + with_array.push(Component::new(with[i].clone()).deserialize(with[i].clone())); + } + let mut translatable_component = TranslatableComponent::new(translate, with_array); + } + + Component {} + } +} diff --git a/minecraft-chat/src/events.rs b/minecraft-chat/src/events.rs new file mode 100644 index 00000000..a547169e --- /dev/null +++ b/minecraft-chat/src/events.rs @@ -0,0 +1,26 @@ +enum ClickAction { + OPEN_URL = Action::new("open_url", true), + OPEN_FILE = Action::new("open_file", false), + RUN_COMMAND = Action::new("run_command", true), + SUGGEST_COMMAND = Action::new("suggest_command", true), + CHANGE_PAGE = Action::new("change_page", true), + COPY_TO_CLIPBOARD = Action::new("copy_to_clipboard", true), +} + +struct ClickAction { + pub name: String, + pub allow_from_server: bool, +} + +impl ClickAction { + fn new(name: &str, allow_from_server: bool) -> Self { + Self { + name: name.to_string(), + allow_from_server, + } + } +} + +struct ClickEvent { + action: ClickAction, +} diff --git a/minecraft-chat/src/lib.rs b/minecraft-chat/src/lib.rs index 31e1bb20..cc887f66 100644 --- a/minecraft-chat/src/lib.rs +++ b/minecraft-chat/src/lib.rs @@ -1,3 +1,11 @@ +//! Things for working with Minecraft chat messages. + +pub mod base_component; +pub mod component; +pub mod mutable_component; +pub mod text_component; +pub mod translatable_component; + #[cfg(test)] mod tests { #[test] diff --git a/minecraft-chat/src/mutable_component.rs b/minecraft-chat/src/mutable_component.rs new file mode 100644 index 00000000..4fd3124d --- /dev/null +++ b/minecraft-chat/src/mutable_component.rs @@ -0,0 +1 @@ +pub struct MutableComponent {} diff --git a/minecraft-chat/src/style.rs b/minecraft-chat/src/style.rs new file mode 100644 index 00000000..63422305 --- /dev/null +++ b/minecraft-chat/src/style.rs @@ -0,0 +1,98 @@ +struct TextColor { + value: u32, + name: Option<String>, +} + +const PREFIX_CODE: char = '\u{00a7}'; + +struct ChatFormatting { + name: String, + code: char, + is_format: bool, + id: i32, + color: Option<u32>, +} + +impl ChatFormatting { + fn new(name: &str, code: char, is_format: bool, id: i32, color: Option<u32>) -> ChatFormatting { + ChatFormatting { + name: name.to_string(), + code, + is_format, + id, + color, + } + } +} + +enum ChatFormatting { + BLACK = ChatFormatting::new("BLACK", '0', false, 0, Some(0)), + DARK_BLUE = ChatFormatting::new("DARK_BLUE", '1', false, 1, Some(170)), + DARK_GREEN = ChatFormatting::new("DARK_GREEN", '2', false, 2, Some(43520)), + DARK_AQUA = ChatFormatting::new("DARK_AQUA", '3', false, 3, Some(43690)), + DARK_RED = ChatFormatting::new("DARK_RED", '4', false, 4, Some(1114112)), + DARK_PURPLE = ChatFormatting::new("DARK_PURPLE", '5', false, 5, Some(11141290)), + GOLD = ChatFormatting::new("GOLD", '6', false, 6, Some(16755200)), + GRAY = ChatFormatting::new("GRAY", '7', false, 7, Some(11184810)), + DARK_GRAY = ChatFormatting::new("DARK_GRAY", '8', false, 8, Some(5592405)), + BLUE = ChatFormatting::new("BLUE", '9', false, 9, Some(5592575)), + GREEN = ChatFormatting::new("GREEN", 'a', false, 10, Some(5635925)), + AQUA = ChatFormatting::new("AQUA", 'b', false, 11, Some(5636095)), + RED = ChatFormatting::new("RED", 'c', false, 12, Some(16733525)), + LIGHT_PURPLE = ChatFormatting::new("LIGHT_PURPLE", 'd', false, 13, Some(16733695)), + YELLOW = ChatFormatting::new("YELLOW", 'e', false, 14, Some(16777045)), + WHITE = ChatFormatting::new("WHITE", 'f', false, 15, Some(16777215)), + OBFUSCATED = ChatFormatting::new("OBFUSCATED", 'k', true, -1, None), + BOLD = ChatFormatting::new("BOLD", 'l', true, -1, None), + STRIKETHROUGH = ChatFormatting::new("STRIKETHROUGH", 'm', true, -1, None), + UNDERLINE = ChatFormatting::new("UNDERLINE", 'n', true, -1, None), + ITALIC = ChatFormatting::new("ITALIC", 'o', true, -1, None), + RESET = ChatFormatting::new("RESET", 'r', -1, -1, None), +} + +impl TextColor { + fn new(value: u32, name: Option<String>) -> Self { + Self { value, name } + } + + fn format(&self) -> String { + format!("#{:06X}", self.value) + } + + fn to_string(&self) -> String { + if let Some(name) = &self.name { + name.clone() + } else { + self.format() + } + } +} + +struct Style { + // @Nullable + // final TextColor color; + // @Nullable + // final Boolean bold; + // @Nullable + // final Boolean italic; + // @Nullable + // final Boolean underlined; + // @Nullable + // final Boolean strikethrough; + // @Nullable + // final Boolean obfuscated; + // @Nullable + // final ClickEvent clickEvent; + // @Nullable + // final HoverEvent hoverEvent; + // @Nullable + // final String insertion; + // @Nullable + // final ResourceLocation font; + color: TextColor, + bold: bool, + italic: bool, + underlined: bool, + strikethrough: bool, + obfuscated: bool, +} diff --git a/minecraft-chat/src/text_component.rs b/minecraft-chat/src/text_component.rs new file mode 100644 index 00000000..381befbb --- /dev/null +++ b/minecraft-chat/src/text_component.rs @@ -0,0 +1,17 @@ +use crate::base_component::BaseComponent; + +pub struct TextComponent { + pub text: String, +} + +impl TextComponent { + pub fn new(text: &str) -> TextComponent { + TextComponent { + text: text.to_string(), + } + } + + pub fn to_string(&self) -> String { + self.text.clone() + } +} diff --git a/minecraft-chat/src/translatable_component.rs b/minecraft-chat/src/translatable_component.rs new file mode 100644 index 00000000..e9099e55 --- /dev/null +++ b/minecraft-chat/src/translatable_component.rs @@ -0,0 +1,10 @@ +pub struct TranslatableComponent { + pub key: String, + pub args: Vec<String>, +} + +impl TranslatableComponent { + pub fn new(key: String, args: Vec<String>) -> Self { + Self { key, args } + } +} diff --git a/minecraft-protocol/src/connection.rs b/minecraft-protocol/src/connection.rs index a162bb6b..e0d781c8 100644 --- a/minecraft-protocol/src/connection.rs +++ b/minecraft-protocol/src/connection.rs @@ -64,7 +64,7 @@ impl Connection { // TODO // otherwise, read the rest of the packet and throw it away - let mut packet_data = Vec::with_capacity((packet_size - packet_id_size as u32) as usize); + let mut packet_data = Vec::with_capacity((packet_size - packet_id_size as i32) as usize); buf.read_buf(&mut packet_data).await.unwrap(); println!("packet {:?}", packet_data); @@ -80,7 +80,7 @@ impl Connection { // write the packet id let mut id_and_data_buf = vec![]; - mc_buf::write_varint(&mut id_and_data_buf, packet.get_id()); + mc_buf::write_varint(&mut id_and_data_buf, packet.get_id() as i32); packet.write(&mut id_and_data_buf); // write the packet data @@ -88,7 +88,7 @@ impl Connection { // make a new buffer that has the length at the beginning // and id+data at the end let mut complete_buf: Vec<u8> = Vec::new(); - mc_buf::write_varint(&mut complete_buf, id_and_data_buf.len() as u32); + mc_buf::write_varint(&mut complete_buf, id_and_data_buf.len() as i32); complete_buf.append(&mut id_and_data_buf); // finally, write and flush to the stream diff --git a/minecraft-protocol/src/mc_buf.rs b/minecraft-protocol/src/mc_buf.rs index 1e6e5b08..a58905b7 100644 --- a/minecraft-protocol/src/mc_buf.rs +++ b/minecraft-protocol/src/mc_buf.rs @@ -33,14 +33,14 @@ pub fn write_bytes(buf: &mut Vec<u8>, bytes: &[u8]) { /// Read a single varint from the reader and return the value, along with the number of bytes read pub async fn read_varint<T: AsyncRead + std::marker::Unpin>( buf: &mut BufReader<T>, -) -> Result<(u32, u8), String> { +) -> Result<(i32, u8), String> { let mut buffer = [0]; let mut ans = 0; for i in 0..4 { buf.read_exact(&mut buffer) .await .or_else(|_| Err("Invalid VarInt".to_string()))?; - ans |= ((buffer[0] & 0b0111_1111) as u32) << 7 * i; + ans |= ((buffer[0] & 0b0111_1111) as i32) << 7 * i; if buffer[0] & 0b1000_0000 == 0 { return Ok((ans, i + 1)); } @@ -48,14 +48,14 @@ pub async fn read_varint<T: AsyncRead + std::marker::Unpin>( Ok((ans, 5)) } -pub fn write_varint(buf: &mut Vec<u8>, mut value: u32) { +pub fn write_varint(buf: &mut Vec<u8>, mut value: i32) { let mut buffer = [0]; if value == 0 { buf.write(&buffer).unwrap(); } while value != 0 { buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (u32::max_value() >> 6); + value = (value >> 7) & (i32::max_value() >> 6); if value != 0 { buffer[0] |= 0b1000_0000; } @@ -104,10 +104,48 @@ pub fn write_utf_with_len(buf: &mut Vec<u8>, string: &String, len: usize) { len ); } - write_varint(buf, string.len() as u32); + write_varint(buf, string.len() as i32); write_bytes(buf, string.as_bytes()); } +pub async fn read_utf<T: AsyncRead + std::marker::Unpin>( + buf: &mut BufReader<T>, + max_length: u32, +) -> Result<String, String> { + let (length, length_varint_length) = read_varint(buf).await?; + // i don't know why it's multiplied by 4 but it's like that in mojang's code so + if length < 0 { + return Err( + "The received encoded string buffer length is less than zero! Weird string!" + .to_string(), + ); + } + if length as u32 > max_length * 4 { + return Err(format!( + "The received encoded string buffer length is longer than maximum allowed ({} > {})", + length, + max_length * 4 + )); + } + + // this is probably quite inefficient, idk how to do it better + let mut string = String::new(); + let mut buffer = vec![0; length as usize]; + buf.read_exact(&mut buffer) + .await + .or_else(|_| Err("Invalid UTF-8".to_string()))?; + + string.push_str(std::str::from_utf8(&buffer).unwrap()); + if string.len() > length as usize { + return Err(format!( + "The received string length is longer than maximum allowed ({} > {})", + length, max_length + )); + } + + Ok(string) +} + pub fn write_utf(buf: &mut Vec<u8>, string: &String) { write_utf_with_len(buf, string, MAX_STRING_LENGTH as usize); } diff --git a/minecraft-protocol/src/packets/client_intention_packet.rs b/minecraft-protocol/src/packets/client_intention_packet.rs index a35e65dc..424c645f 100644 --- a/minecraft-protocol/src/packets/client_intention_packet.rs +++ b/minecraft-protocol/src/packets/client_intention_packet.rs @@ -21,10 +21,10 @@ impl<'a> Packet for ClientIntentionPacket<'a> { // implement "from_reader" for "ClientIntentionPacket" fn write(&self, buf: &mut Vec<u8>) { - mc_buf::write_varint(buf, self.protocol_version); + mc_buf::write_varint(buf, self.protocol_version as i32); mc_buf::write_utf(buf, &self.hostname); mc_buf::write_short(buf, self.port); - mc_buf::write_varint(buf, self.intention.clone() as u32); + mc_buf::write_varint(buf, self.intention.clone() as i32); } fn parse<T: tokio::io::AsyncRead + std::marker::Unpin>(&self, buf: T) -> () {} diff --git a/minecraft-protocol/src/packets/clientbound_status_response_packet.rs b/minecraft-protocol/src/packets/clientbound_status_response_packet.rs index 8baa2a4d..ddca9b1f 100644 --- a/minecraft-protocol/src/packets/clientbound_status_response_packet.rs +++ b/minecraft-protocol/src/packets/clientbound_status_response_packet.rs @@ -4,7 +4,8 @@ use super::Packet; #[derive(Hash)] pub struct ServerboundStatusRequestPacket { - status: ServerStatus, + // status: ServerStatus, + status: String, } // implement "Packet" for "ClientIntentionPacket" @@ -16,6 +17,7 @@ impl Packet for ServerboundStatusRequestPacket { // implement "from_reader" for "ClientIntentionPacket" fn write(&self, _buf: &mut Vec<u8>) {} fn parse<T: tokio::io::AsyncRead + std::marker::Unpin>(&self, buf: T) -> () { + mc_buf::read_utf; // this.status = GsonHelper.fromJson(GSON, friendlyByteBuf.readUtf(32767), ServerStatus.class); } } |
