aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml13
-rw-r--r--azalea/Cargo.toml6
-rw-r--r--azalea/README.md62
-rw-r--r--azalea/src/builder.rs13
-rw-r--r--azalea/src/swarm/builder.rs4
-rw-r--r--docs-rs/README.md17
-rw-r--r--docs-rs/trait-tags.html177
7 files changed, 253 insertions, 39 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 434fe6ac..7eb9ab40 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -115,6 +115,19 @@ azalea-world = { path = "azalea-world", version = "0.14.0" }
# https://users.rust-lang.org/t/to-string-vs-to-owned-for-string-literals/1441
str_to_string = "warn"
+[workspace.lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] }
+
+[workspace.metadata.docs.rs]
+rustc-args = ["--cfg", "docsrs_dep"]
+rustdoc-args = [
+ "--cfg",
+ "docsrs_dep",
+ "--html-after-content",
+ "docs-rs/trait-tags.html",
+]
+cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
+
# --- Profile Settings ---
[profile.release]
diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml
index 15627bc1..e4adb426 100644
--- a/azalea/Cargo.toml
+++ b/azalea/Cargo.toml
@@ -59,6 +59,12 @@ serde = ["dep:serde", "azalea-registry/serde", "azalea-world/serde"]
packet-event = ["azalea-client/packet-event"]
online-mode = ["azalea-client/online-mode"]
+[[example]]
+name = "testbot"
+# setting this to true for one example makes it work for all of them.
+# see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#scrape-examples
+doc-scrape-examples = true
+
[[bench]]
name = "pathfinder"
harness = false
diff --git a/azalea/README.md b/azalea/README.md
index e44036c3..ca081f8e 100644
--- a/azalea/README.md
+++ b/azalea/README.md
@@ -1,35 +1,6 @@
Azalea is a framework for creating Minecraft bots.
-Also see the project [README](https://github.com/azalea-rs/azalea) for a higher-level overview of Azalea.
-
-# Installation
-
-First, install Rust nightly with `rustup install nightly` and `rustup default nightly`.
-
-Then, use one of the following commands to add Azalea to your project:
-
-- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea`
-- Latest "stable" release: `cargo add azalea`
-
-## Optimization
-
-For faster compile times, create a `.cargo/config.toml` file in your project and copy
-[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds.toml)
-into it. You may have to install the LLD linker.
-
-For faster performance in debug mode, add the following code to your
-Cargo.toml:
-
-```toml
-[profile.dev]
-opt-level = 1
-[profile.dev.package."*"]
-opt-level = 3
-```
-
-# Documentation
-
-The documentation for the latest Azalea crates.io release is available at [docs.rs/azalea](https://docs.rs/azalea/latest/azalea/) and the docs for the latest bleeding-edge (git) version are at [azalea.matdoes.dev](https://azalea.matdoes.dev/azalea/).
+See the project [README](https://github.com/azalea-rs/azalea) for a higher-level overview of Azalea.
# Examples
@@ -81,6 +52,35 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
There are more examples in the [examples directory](https://github.com/azalea-rs/azalea/tree/main/azalea/examples).
You may also find it helpful to read the code for [other people's Azalea bots](https://github.com/azalea-rs/azalea#real-world-bots-using-azalea).
+# Installation
+
+First, install Rust nightly with `rustup install nightly` and `rustup default nightly`.
+
+Then, use one of the following commands to add Azalea to your project:
+
+- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea`
+- Latest "stable" release: `cargo add azalea`
+
+## Optimization
+
+For faster compile times, create a `.cargo/config.toml` file in your project and copy
+[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds.toml)
+into it. You may have to install the LLD linker.
+
+For faster performance in debug mode, add the following code to your
+Cargo.toml:
+
+```toml
+[profile.dev]
+opt-level = 1
+[profile.dev.package."*"]
+opt-level = 3
+```
+
+# Documentation
+
+The documentation for the latest Azalea crates.io release is available at [docs.rs/azalea](https://docs.rs/azalea/latest/azalea/) and the docs for the latest bleeding-edge (git) version are at [azalea.matdoes.dev](https://azalea.matdoes.dev/azalea/).
+
# Swarms
Azalea lets you create "swarms", which are a group of bots in the same world that can perform actions together. See [testbot](https://github.com/azalea-rs/azalea/blob/main/azalea/examples/testbot/main.rs) for an example. Also, if you're using swarms, you should also `use` both `azalea::prelude::*` and `azalea::swarm::prelude::*`.
@@ -89,7 +89,7 @@ Azalea lets you create "swarms", which are a group of bots in the same world tha
Azalea uses [Bevy ECS](https://docs.rs/bevy_ecs) internally to store information about the world and clients. Bevy plugins are more powerful than async handler functions, but more difficult to use. See [pathfinder](https://github.com/azalea-rs/azalea/blob/main/azalea/src/pathfinder/mod.rs) as an example of how to make a plugin. You can then enable a plugin by adding `.add_plugin(ExamplePlugin)` in your client/swarm builder.
-Everything in Azalea is implemented as a Bevy plugin, which means you can disable default behaviors (like, physics or chat signing) by disabling built-in plugins. See [`SwarmBuilder::new_without_plugins`] to learn how to do that.
+Everything in Azalea is implemented as a Bevy plugin, which means you can disable default behaviors (like, physics or chat signing) by disabling built-in plugins. See [`SwarmBuilder::new_without_plugins`](swarm::SwarmBuilder::new_without_plugins) to learn how to do that.
Also note that just because something is an entity in the ECS doesn't mean that it's a Minecraft entity. You can filter for that by having `With<MinecraftEntityId>` as a filter.
diff --git a/azalea/src/builder.rs b/azalea/src/builder.rs
index d0318424..91fb9d5e 100644
--- a/azalea/src/builder.rs
+++ b/azalea/src/builder.rs
@@ -11,8 +11,8 @@ use crate::{
swarm::{self, SwarmBuilder},
};
-/// A builder for creating new [`Client`]s. This is the recommended way of
-/// making a bot.
+/// A builder for creating new [`Client`](crate::Client)s. This is the
+/// recommended way of making a bot.
///
/// ```no_run
/// # use azalea::prelude::*;
@@ -84,8 +84,9 @@ impl ClientBuilder<NoState, ()> {
}
}
- /// Set the function that's called every time a bot receives an [`Event`].
- /// This is the way to handle normal per-bot events.
+ /// Set the function that's called every time a bot receives an
+ /// [`Event`](crate::Client). This is the way to handle normal per-bot
+ /// events.
///
/// Currently, you can have up to one client handler.
///
@@ -156,8 +157,8 @@ where
self
}
- /// Build this `ClientBuilder` into an actual [`Client`] and join the given
- /// server.
+ /// Build this `ClientBuilder` into an actual [`Client`](crate::Client) and
+ /// join the given server.
///
/// If the client can't join, it'll keep retrying forever until it can.
///
diff --git a/azalea/src/swarm/builder.rs b/azalea/src/swarm/builder.rs
index 412ec67e..4eae7eab 100644
--- a/azalea/src/swarm/builder.rs
+++ b/azalea/src/swarm/builder.rs
@@ -137,8 +137,8 @@ where
SS: Default + Send + Sync + Clone + Resource + 'static,
{
/// Set the function that's called every time a bot receives an
- /// [`enum@Event`]. This is the intended way to handle normal per-bot
- /// events.
+ /// [`Event`](crate::Event). This is the intended way to handle
+ /// normal per-bot events.
///
/// Currently you can have up to one handler.
///
diff --git a/docs-rs/README.md b/docs-rs/README.md
new file mode 100644
index 00000000..3be1087a
--- /dev/null
+++ b/docs-rs/README.md
@@ -0,0 +1,17 @@
+## Docs.rs Extensions
+
+This directory includes some templates and styling to extend and modify [rustdoc]'s output
+for Azalea's documentation on [docs.rs].
+
+See [Bevy's documentation](https://github.com/bevyengine/bevy/tree/main/docs-rs) for more info.
+
+## Local Testing
+
+Build the documentation with the extension enabled like this:
+
+```bash
+RUSTDOCFLAGS="--html-after-content docs-rs/trait-tags.html --cfg docsrs_dep" RUSTFLAGS="--cfg docsrs_dep" cargo doc --no-deps --package <package_name>
+```
+
+[rustdoc]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html
+[docs.rs]: https://docs.rs
diff --git a/docs-rs/trait-tags.html b/docs-rs/trait-tags.html
new file mode 100644
index 00000000..e34614ee
--- /dev/null
+++ b/docs-rs/trait-tags.html
@@ -0,0 +1,177 @@
+<script>
+ // Adds tags to documentation pages for common Bevy traits like `Component` or `Resource`.
+ // This makes it easier to see at a glance what types are used for.
+ //
+ // This extension is passed to `rustdoc` using the `--html-after-content` flag.
+
+ // Traits that we want to show as tags.
+ // Order determines sort order of items in listings.
+ const bevyTraits = [
+ 'Plugin',
+ 'PluginGroup',
+ 'Component',
+ 'Resource',
+ 'Asset',
+ 'Event',
+ 'Message',
+ 'ScheduleLabel',
+ 'SystemSet',
+ 'SystemParam',
+ 'Relationship',
+ 'RelationshipTarget'
+ ];
+
+ // Find all traits that are implemented by the current type.
+ const implementedBevyTraits = findImplementedBevyTraits(document);
+
+ // If we found any implemented traits, add them as tags to the top of the page.
+ if (implementedBevyTraits.size > 0) {
+ // Create a container for the tags.
+ const heading = document.body.querySelector(".main-heading h1");
+ const tagContainer = document.createElement('div');
+ tagContainer.className = 'bevy-tag-container';
+ heading.appendChild(tagContainer);
+
+ // Check if an implemented trait has a `type Mutability = Immutable` associated type.
+ // This is used to determine if a `Component` is immutable or not.
+ // TODO: Ideally we should just check the associated types of the `Component` trait,
+ // but the docs.rs layout makes it tricky to do so in a robust way.
+ const associatedTypeHeader = document.querySelectorAll(".trait-impl.associatedtype .code-header");
+ const isImmutable = [...associatedTypeHeader].some(el => el.innerText.includes('type Mutability = Immutable'));
+
+ // Create a tag for each implemented trait.
+ for (let [tagName, href] of implementedBevyTraits) {
+ if (tagName == 'Component' & isImmutable) {
+ tagName = 'Immutable Component';
+ }
+
+ // Create the tag and append it to the container.
+ tagContainer.appendChild(createBevyTag(tagName, href));
+ }
+ }
+
+ function findImplementedBevyTraits(doc) {
+ // Traits that are implemented by the current type.
+ // The key is the trait name, and the value is the href to the trait's documentation.
+ const implementedTraits = new Map();
+
+ // Find all trait implementation headers.
+ const allTraitHeaders = doc.body.querySelectorAll(
+ '#trait-implementations-list .impl .code-header, #blanket-implementations-list .impl .code-header'
+ );
+
+ for (const header of allTraitHeaders) {
+ // We can extract the trait name by removing any generics and splitting the string by spaces.
+ // This results in ['impl', 'TraitName', 'for', 'TypeName'].
+ const traitName = removeGenerics(header.innerText).split(' ')[1].trim();
+
+ // Find the link to the trait if the anchor element exists.
+ // Otherwise, the trait is just in plain text.
+ const traitLinkEl = [...header.children].find(el => el.getAttribute('href')?.includes(`trait.${traitName}.html`));
+ const href = traitLinkEl?.getAttribute('href');
+
+ implementedTraits.set(traitName, href);
+ }
+
+ const implementedBevyTraits = new Map(
+ [...implementedTraits].filter(([traitName, _]) => bevyTraits.find((x) => x == traitName))
+ );
+
+ return implementedBevyTraits;
+ }
+
+ // Helper function to remove generics from a string of Rust code.
+ // For example, 'Vec<T>' would become 'Vec'.
+ function removeGenerics(str) {
+ // Remove the innermost generics.
+ const newStr = str.replace(/<([^<>])*>/g, '');
+
+ // If there are still generics, perform the removal again recursively.
+ if (newStr !== str) {
+ return removeGenerics(newStr);
+ }
+
+ // No more generics to remove.
+ return newStr;
+ }
+
+ // Helper function to create a tag element with the given name and href,
+ // if available.
+ function createBevyTag(tagName, href) {
+ const el = document.createElement('a');
+ const kebabCaseName = tagName.toLowerCase().replace(' ', '-');
+
+ if (href) {
+ el.setAttribute('href', href);
+ }
+
+ el.innerText = tagName;
+ el.className = `bevy-tag ${kebabCaseName}-tag`;
+ return el;
+ }
+</script>
+
+<style>
+ .bevy-tag-container {
+ padding: 0.5rem 0;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ }
+
+ .bevy-tag {
+ display: flex;
+ align-items: center;
+ width: fit-content;
+ height: 1.5rem;
+ padding: 0 0.5rem;
+ border-radius: 0.75rem;
+ font-size: 1rem;
+ font-weight: normal;
+ color: white;
+ }
+
+ .bevy-tag {
+ background-color: var(--tag-color);
+ }
+
+ .component-tag,
+ .immutable-component-tag {
+ --tag-color: oklch(50% 27% 80);
+ }
+
+ .resource-tag {
+ --tag-color: oklch(50% 27% 110);
+ }
+
+ .asset-tag {
+ --tag-color: oklch(50% 27% 0);
+ }
+
+ .event-tag {
+ --tag-color: oklch(50% 27% 310);
+ }
+
+ .message-tag {
+ --tag-color: oklch(50% 27% 190);
+ }
+
+ .plugin-tag,
+ .plugingroup-tag {
+ --tag-color: oklch(50% 27% 50);
+ }
+
+ .schedulelabel-tag,
+ .systemset-tag {
+ --tag-color: oklch(50% 27% 270);
+ }
+
+ .systemparam-tag {
+ --tag-color: oklch(50% 27% 240);
+ }
+
+ .relationship-tag,
+ .relationshiptarget-tag {
+ --tag-color: oklch(50% 27% 150);
+ }
+</style> \ No newline at end of file