mirror of
https://github.com/mat-1/azalea.git
synced 2024-09-18 22:32:31 +00:00
start adding minecraft-chat
This commit is contained in:
parent
4a44c58444
commit
c16d55ccdd
14 changed files with 289 additions and 12 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -215,6 +215,12 @@ version = "2.3.1"
|
|||
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"
|
||||
|
@ -281,6 +287,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
|||
[[package]]
|
||||
name = "minecraft-chat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minecraft-client"
|
||||
|
@ -475,12 +484,35 @@ dependencies = [
|
|||
"quick-error",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
@ -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"
|
||||
|
|
6
minecraft-chat/src/base_component.rs
Normal file
6
minecraft-chat/src/base_component.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use crate::component::Component;
|
||||
|
||||
pub trait BaseComponent {
|
||||
const siblings: Vec<Component>;
|
||||
// style:
|
||||
}
|
38
minecraft-chat/src/component.rs
Normal file
38
minecraft-chat/src/component.rs
Normal file
|
@ -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 {}
|
||||
}
|
||||
}
|
26
minecraft-chat/src/events.rs
Normal file
26
minecraft-chat/src/events.rs
Normal file
|
@ -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,
|
||||
}
|
|
@ -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]
|
||||
|
|
1
minecraft-chat/src/mutable_component.rs
Normal file
1
minecraft-chat/src/mutable_component.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub struct MutableComponent {}
|
98
minecraft-chat/src/style.rs
Normal file
98
minecraft-chat/src/style.rs
Normal file
|
@ -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,
|
||||
}
|
17
minecraft-chat/src/text_component.rs
Normal file
17
minecraft-chat/src/text_component.rs
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
10
minecraft-chat/src/translatable_component.rs
Normal file
10
minecraft-chat/src/translatable_component.rs
Normal file
|
@ -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 }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) -> () {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue