1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

Serialize Component (#47)

* Serializing ClientboundStatusResponsePacket

Enable serialization of ClientboundStatusResponsePacket

* Update clientbound_status_response_packet.rs

Add options previewsChat and enforcesSecureChat

* Serialize Style and TextColor

* Serialize BaseComponent

* Serialize TextComponent

* Fix Style

* Serialize Component

* Fix multiple formats per message, fix reset tag

* Fix Style, again

* Use FlatMapSerializer

* Forgot italics

* Count struct fields, reorganize logic

* Serialize TranslatableComponent

* Rewrite TextComponent Serializer

* Fix using TextColor::Parse

* Code Cleanup

* Add default attribute, just in case

* Clippy

* use serde derive feature + preferred formatting choices

Co-authored-by: BuildTools <unconfigured@null.spigotmc.org>
Co-authored-by: mat <github@matdoes.dev>
This commit is contained in:
EightFactorial 2022-12-03 17:08:05 -08:00 committed by GitHub
parent 661c3622be
commit 3b93fc6412
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 32 deletions

12
Cargo.lock generated
View file

@ -1690,9 +1690,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
dependencies = [
"serde_derive",
]
@ -1709,9 +1709,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
dependencies = [
"proc-macro2",
"quote",
@ -1800,9 +1800,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.103"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",

View file

@ -3,14 +3,14 @@ description = "Parse Minecraft chat messages."
edition = "2021"
license = "MIT"
name = "azalea-chat"
version = "0.4.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-chat"
version = "0.4.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf = {path = "../azalea-buf", features = ["serde_json"], version = "^0.4.0" }
azalea-language = {path = "../azalea-language", version = "^0.4.0" }
azalea-buf = {path = "../azalea-buf", features = ["serde_json"], version = "^0.4.0"}
azalea-language = {path = "../azalea-language", version = "^0.4.0"}
once_cell = "1.16.0"
serde = "^1.0.130"
serde = {version = "^1.0.148", features = ["derive"]}
serde_json = "^1.0.72"

View file

@ -1,9 +1,12 @@
use crate::{style::Style, Component};
use serde::Serialize;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct BaseComponent {
// implements mutablecomponent
#[serde(skip_serializing_if = "Vec::is_empty")]
pub siblings: Vec<Component>,
#[serde(flatten)]
pub style: Style,
}

View file

@ -6,14 +6,15 @@ use crate::{
};
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use once_cell::sync::Lazy;
use serde::{de, Deserialize, Deserializer};
use serde::{de, Deserialize, Deserializer, Serialize};
use std::{
fmt::Display,
io::{Cursor, Write},
};
/// A chat component, basically anything you can see in chat.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Component {
Text(TextComponent),
Translatable(TranslatableComponent),
@ -262,11 +263,10 @@ impl McBufReadable for Component {
}
impl McBufWritable for Component {
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
// let json = serde_json::to_string(self).unwrap();
// json.write_into(_buf);
// Ok(())
todo!()
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let json = serde_json::to_string(self).unwrap();
json.write_into(buf)?;
Ok(())
}
}

View file

@ -2,6 +2,7 @@ use std::{collections::HashMap, fmt};
use azalea_buf::McBuf;
use once_cell::sync::Lazy;
use serde::{ser::SerializeStruct, Serialize, Serializer};
use serde_json::Value;
#[derive(Clone, PartialEq, Eq, Debug)]
@ -10,6 +11,19 @@ pub struct TextColor {
pub name: Option<String>,
}
impl Serialize for TextColor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.name.is_some() {
serializer.serialize_str(&self.name.as_ref().unwrap().to_ascii_lowercase())
} else {
serializer.serialize_str(&self.format())
}
}
}
impl TextColor {
pub fn parse(value: String) -> Option<TextColor> {
if value.starts_with('#') {
@ -276,17 +290,67 @@ impl TryFrom<ChatFormatting> for TextColor {
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Style {
// these are options instead of just bools because None is different than false in this case
// These are options instead of just bools because None is different than false in this case
pub color: Option<TextColor>,
pub bold: Option<bool>,
pub italic: Option<bool>,
pub underlined: Option<bool>,
pub strikethrough: Option<bool>,
pub obfuscated: Option<bool>,
/// Whether it should reset the formatting before applying these styles
/// Whether formatting should be reset before applying these styles
pub reset: bool,
}
impl Serialize for Style {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let len = if self.reset {
6
} else {
usize::from(self.color.is_some())
+ usize::from(self.bold.is_some())
+ usize::from(self.italic.is_some())
+ usize::from(self.underlined.is_some())
+ usize::from(self.strikethrough.is_some())
+ usize::from(self.obfuscated.is_some())
};
let mut state = serializer.serialize_struct("Style", len)?;
if let Some(color) = &self.color {
state.serialize_field("color", color)?;
} else if self.reset {
state.serialize_field("color", "white")?;
}
if let Some(bold) = &self.bold {
state.serialize_field("bold", bold)?;
} else if self.reset {
state.serialize_field("bold", &false)?;
}
if let Some(italic) = &self.italic {
state.serialize_field("italic", italic)?;
} else if self.reset {
state.serialize_field("italic", &false)?;
}
if let Some(underlined) = &self.underlined {
state.serialize_field("underlined", underlined)?;
} else if self.reset {
state.serialize_field("underlined", &false)?;
}
if let Some(strikethrough) = &self.strikethrough {
state.serialize_field("strikethrough", strikethrough)?;
} else if self.reset {
state.serialize_field("strikethrough", &false)?;
}
if let Some(obfuscated) = &self.obfuscated {
state.serialize_field("obfuscated", obfuscated)?;
} else if self.reset {
state.serialize_field("obfuscated", &false)?;
}
state.end()
}
}
impl Style {
pub fn empty() -> Self {
Self::default()

View file

@ -1,6 +1,6 @@
use std::fmt::Display;
use crate::{base_component::BaseComponent, style::ChatFormatting, Component};
use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSerializer};
use std::fmt::Display;
/// A component that contains text that's the same in all locales.
#[derive(Clone, Debug, Default, PartialEq)]
@ -9,6 +9,21 @@ pub struct TextComponent {
pub text: String,
}
impl Serialize for TextComponent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(None)?;
state.serialize_entry("text", &self.text)?;
Serialize::serialize(&self.base, FlatMapSerializer(&mut state))?;
if !self.base.siblings.is_empty() {
state.serialize_entry("extra", &self.base.siblings)?;
}
state.end()
}
}
const LEGACY_FORMATTING_CODE_SYMBOL: char = '§';
/// Convert a legacy color code string into a Component

View file

@ -3,8 +3,10 @@ use std::fmt::{self, Display, Formatter};
use crate::{
base_component::BaseComponent, style::Style, text_component::TextComponent, Component,
};
use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSerializer};
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum StringOrComponent {
String(String),
Component(Component),
@ -18,6 +20,19 @@ pub struct TranslatableComponent {
pub args: Vec<StringOrComponent>,
}
impl Serialize for TranslatableComponent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(None)?;
state.serialize_entry("translate", &self.key)?;
Serialize::serialize(&self.base, FlatMapSerializer(&mut state))?;
state.serialize_entry("with", &self.args)?;
state.end()
}
}
impl TranslatableComponent {
pub fn new(key: String, args: Vec<StringOrComponent>) -> Self {
Self {

View file

@ -1,23 +1,23 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use azalea_chat::Component;
use azalea_protocol_macros::ClientboundStatusPacket;
use serde::Deserialize;
use serde_json::Value;
use serde::{Deserialize, Serialize};
use serde_json::{value::Serializer, Value};
use std::io::{Cursor, Write};
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Version {
pub name: Component,
pub name: String,
pub protocol: i32,
}
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SamplePlayer {
pub id: String,
pub name: String,
}
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Players {
pub max: i32,
pub online: i32,
@ -26,12 +26,22 @@ pub struct Players {
}
// the entire packet is just json, which is why it has deserialize
#[derive(Clone, Debug, Deserialize, ClientboundStatusPacket)]
#[derive(Clone, Debug, Serialize, Deserialize, ClientboundStatusPacket)]
pub struct ClientboundStatusResponsePacket {
pub description: Component,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub favicon: Option<String>,
pub players: Players,
pub version: Version,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "previewsChat")]
pub previews_chat: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "enforcesSecureChat")]
pub enforces_secure_chat: Option<bool>,
}
impl McBufReadable for ClientboundStatusResponsePacket {
@ -44,7 +54,11 @@ impl McBufReadable for ClientboundStatusResponsePacket {
}
impl McBufWritable for ClientboundStatusResponsePacket {
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!()
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let status_string = ClientboundStatusResponsePacket::serialize(self, Serializer)
.unwrap()
.to_string();
status_string.write_into(buf)?;
Ok(())
}
}