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:
commit
644bb721bb
14 changed files with 1564 additions and 1394 deletions
45
.github/workflows/doc.yml
vendored
Normal file
45
.github/workflows/doc.yml
vendored
Normal 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
2702
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
1
rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
|||
nightly
|
Loading…
Add table
Reference in a new issue