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

Merge branch 'main' into inventory

This commit is contained in:
mat 2022-12-04 20:47:35 -06:00
commit 644bb721bb
14 changed files with 1564 additions and 1394 deletions

45
.github/workflows/doc.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: Doc
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
- run: cargo doc --workspace --no-deps
- uses: "finnp/create-file-action@master"
env:
FILE_NAME: "./target/doc/index.html"
FILE_DATA: '<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=''./azalea''"/></head></html>' # Redirect to default page
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './target/doc/'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

2702
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,13 @@ A collection of Rust crates for making Minecraft bots, clients, and tools.
## ⚠️ Azalea is still very unfinished, though most crates are in a somewhat useable state
I named this Azalea because it sounds like a cool word and this is a cool library.
This project was heavily inspired by [PrismarineJS](https://github.com/PrismarineJS).
## Matrix/Discord
If you'd like to chat about Azalea, you can join the Matrix space at [#azalea:matdoes.dev](https://matrix.to/#/#azalea:matdoes.dev) or the Discord server at [discord.gg/FaRey6ytmC](https://discord.gg/FaRey6ytmC).
## Why
I wanted a fun excuse to do something cool with Rust, and I also felt like I could do better than [Mineflayer](https://github.com/prismarinejs/mineflayer) in some areas.

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

@ -9,13 +9,16 @@ use azalea_protocol::packets::game::{
serverbound_chat_command_packet::ServerboundChatCommandPacket,
serverbound_chat_packet::ServerboundChatPacket,
};
use std::time::{SystemTime, UNIX_EPOCH};
use std::{
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
/// A chat packet, either a system message or a chat message.
#[derive(Debug, Clone, PartialEq)]
pub enum ChatPacket {
System(ClientboundSystemChatPacket),
Player(Box<ClientboundPlayerChatPacket>),
System(Arc<ClientboundSystemChatPacket>),
Player(Arc<ClientboundPlayerChatPacket>),
}
macro_rules! regex {

View file

@ -65,11 +65,11 @@ pub enum Event {
Chat(ChatPacket),
/// Happens 20 times per second, but only when the world is loaded.
Tick,
Packet(Box<ClientboundGamePacket>),
Packet(Arc<ClientboundGamePacket>),
/// Happens when a player is added, removed, or updated in the tab list.
UpdatePlayers(UpdatePlayersEvent),
/// Emits when the player dies.
Death(Option<Box<ClientboundPlayerCombatKillPacket>>),
Death(Option<Arc<ClientboundPlayerCombatKillPacket>>),
}
/// Happens when a player is added, removed, or updated in the tab list.
@ -421,8 +421,9 @@ impl Client {
client: &Client,
tx: &Sender<Event>,
) -> Result<(), HandleError> {
tx.send(Event::Packet(Box::new(packet.clone()))).await?;
match packet {
let packet = Arc::new(packet.clone());
tx.send(Event::Packet(packet.clone())).await?;
match &*packet {
ClientboundGamePacket::Login(p) => {
debug!("Got login packet");
@ -896,12 +897,13 @@ impl Client {
}
ClientboundGamePacket::PlayerChat(p) => {
debug!("Got player chat packet {:?}", p);
tx.send(Event::Chat(ChatPacket::Player(Box::new(p.clone()))))
tx.send(Event::Chat(ChatPacket::Player(Arc::new(p.clone()))))
.await?;
}
ClientboundGamePacket::SystemChat(p) => {
debug!("Got system chat packet {:?}", p);
tx.send(Event::Chat(ChatPacket::System(p.clone()))).await?;
tx.send(Event::Chat(ChatPacket::System(Arc::new(p.clone()))))
.await?;
}
ClientboundGamePacket::Sound(_p) => {
// debug!("Got sound packet {:?}", p);
@ -975,7 +977,7 @@ impl Client {
// because of https://github.com/rust-lang/rust/issues/57478
if !*client.dead.lock() {
*client.dead.lock() = true;
tx.send(Event::Death(Some(Box::new(p.clone())))).await?;
tx.send(Event::Death(Some(Arc::new(p.clone())))).await?;
}
}
}

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(())
}
}

View file

@ -327,14 +327,19 @@ impl WeakEntityStorage {
/// # Examples
///
/// ```rust
/// let mut storage = EntityStorage::new();
/// storage.insert(
/// # use std::sync::Arc;
/// # use azalea_world::{PartialEntityStorage, entity::{EntityData, EntityMetadata, metadata}};
/// # use azalea_core::Vec3;
/// # use uuid::Uuid;
/// #
/// let mut storage = PartialEntityStorage::default();
/// storage.insert(
/// 0,
/// Arc::new(EntityData::new(
/// uuid,
/// EntityData::new(
/// Uuid::nil(),
/// Vec3::default(),
/// EntityMetadata::Player(metadata::Player::default()),
/// )),
/// EntityMetadata::Player(metadata::Player::default()),
/// ),
/// );
/// for entity in storage.shared.read().entities() {
/// if let Some(entity) = entity.upgrade() {

1
rust-toolchain Normal file
View file

@ -0,0 +1 @@
nightly