mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
chest
This commit is contained in:
parent
b44dc94274
commit
6286e953a6
17 changed files with 282 additions and 44 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -181,6 +181,7 @@ dependencies = [
|
|||
"azalea-chat",
|
||||
"azalea-client",
|
||||
"azalea-core",
|
||||
"azalea-inventory",
|
||||
"azalea-physics",
|
||||
"azalea-protocol",
|
||||
"azalea-registry",
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
disconnect::{DisconnectEvent, DisconnectPlugin},
|
||||
events::{Event, EventPlugin, LocalPlayerEvents},
|
||||
interact::{CurrentSequenceNumber, InteractPlugin},
|
||||
inventory::{InventoryComponent, InventoryPlugin},
|
||||
inventory_plugin::{InventoryComponent, InventoryPlugin},
|
||||
local_player::{
|
||||
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
|
||||
LocalPlayer, PhysicsState, SendPacketEvent,
|
||||
|
@ -50,6 +50,7 @@ use bevy_ecs::{
|
|||
entity::Entity,
|
||||
schedule::IntoSystemConfig,
|
||||
schedule::{LogLevel, ScheduleBuildSettings, ScheduleLabel},
|
||||
system::{ResMut, Resource},
|
||||
world::World,
|
||||
};
|
||||
use bevy_log::LogPlugin;
|
||||
|
@ -59,7 +60,10 @@ use log::{debug, error};
|
|||
use parking_lot::{Mutex, RwLock};
|
||||
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
|
||||
use thiserror::Error;
|
||||
use tokio::{sync::mpsc, time};
|
||||
use tokio::{
|
||||
sync::{broadcast, mpsc},
|
||||
time,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// `Client` has the things that a user interacting with the library will want.
|
||||
|
@ -627,6 +631,36 @@ pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::UnboundedSender<(
|
|||
}
|
||||
}
|
||||
|
||||
/// A resource that contains a [`broadcast::Sender`] that will be sent every
|
||||
/// time the ECS schedule is run.
|
||||
///
|
||||
/// This is useful for running code every schedule from async user code.
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut receiver = {
|
||||
/// let ecs = client.ecs.lock();
|
||||
/// let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
|
||||
/// schedule_broadcast.subscribe()
|
||||
/// };
|
||||
/// while receiver.recv().await.is_ok() {
|
||||
/// // do something
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Resource, Deref)]
|
||||
pub struct RanScheduleBroadcast(broadcast::Sender<()>);
|
||||
|
||||
fn send_ran_schedule_event(ran_schedule_broadcast: ResMut<RanScheduleBroadcast>) {
|
||||
let _ = ran_schedule_broadcast.0.send(());
|
||||
}
|
||||
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
|
||||
pub struct RanSchedulePlugin;
|
||||
impl Plugin for RanSchedulePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(RanScheduleBroadcast(broadcast::channel(1).0))
|
||||
.add_system(send_ran_schedule_event);
|
||||
}
|
||||
}
|
||||
|
||||
/// This plugin group will add all the default plugins necessary for Azalea to
|
||||
/// work.
|
||||
pub struct DefaultPlugins;
|
||||
|
@ -647,5 +681,6 @@ impl PluginGroup for DefaultPlugins {
|
|||
.add(DisconnectPlugin)
|
||||
.add(PlayerMovePlugin)
|
||||
.add(InteractPlugin)
|
||||
.add(RanSchedulePlugin)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,10 +30,11 @@ impl Plugin for InteractPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<BlockInteractEvent>().add_systems(
|
||||
(
|
||||
handle_block_interact_event,
|
||||
update_hit_result_component.after(clamp_look_direction),
|
||||
handle_block_interact_event,
|
||||
)
|
||||
.before(handle_send_packet_event),
|
||||
.before(handle_send_packet_event)
|
||||
.chain(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,63 @@
|
|||
use azalea_chat::FormattedText;
|
||||
use azalea_core::BlockPos;
|
||||
use azalea_inventory::{ItemSlot, Menu};
|
||||
use azalea_registry::MenuKind;
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_ecs::{component::Component, entity::Entity, event::EventReader, system::Query};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
event::EventReader,
|
||||
schedule::IntoSystemConfigs,
|
||||
system::{Commands, Query},
|
||||
};
|
||||
|
||||
use crate::{client::RanScheduleBroadcast, Client};
|
||||
|
||||
pub struct InventoryPlugin;
|
||||
impl Plugin for InventoryPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<ClientSideCloseContainerEvent>()
|
||||
.add_system(handle_client_side_close_container_event);
|
||||
.add_event::<MenuOpenedEvent>()
|
||||
.add_systems(
|
||||
(
|
||||
handle_menu_opened_event,
|
||||
handle_client_side_close_container_event,
|
||||
)
|
||||
.chain(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct WaitingForInventoryOpen;
|
||||
|
||||
impl Client {
|
||||
pub async fn open_container(&mut self, pos: BlockPos) -> Option<Menu> {
|
||||
self.ecs
|
||||
.lock()
|
||||
.entity_mut(self.entity)
|
||||
.insert(WaitingForInventoryOpen);
|
||||
self.block_interact(pos);
|
||||
|
||||
let mut receiver = {
|
||||
let ecs = self.ecs.lock();
|
||||
let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
|
||||
schedule_broadcast.subscribe()
|
||||
};
|
||||
while receiver.recv().await.is_ok() {
|
||||
let ecs = self.ecs.lock();
|
||||
if ecs.get::<WaitingForInventoryOpen>(self.entity).is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ecs = self.ecs.lock();
|
||||
let inventory = ecs.get::<InventoryComponent>(self.entity);
|
||||
if let Some(inventory) = inventory {
|
||||
inventory.container_menu.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +84,7 @@ pub struct InventoryComponent {
|
|||
pub carried: ItemSlot,
|
||||
/// An identifier used by the server to track client inventory desyncs.
|
||||
pub state_id: u32,
|
||||
// minecraft also has these fields, but i don't need they're necessary?:
|
||||
// minecraft also has these fields, but i don't think they're necessary?:
|
||||
// private final NonNullList<ItemStack> remoteSlots;
|
||||
// private final IntList remoteDataSlots;
|
||||
// private ItemStack remoteCarried;
|
||||
|
@ -70,9 +121,33 @@ impl Default for InventoryComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sent from the server when a menu (like a chest or crafting table) was
|
||||
/// opened by the client.
|
||||
pub struct MenuOpenedEvent {
|
||||
pub entity: Entity,
|
||||
pub window_id: u32,
|
||||
pub menu_type: MenuKind,
|
||||
pub title: FormattedText,
|
||||
}
|
||||
fn handle_menu_opened_event(
|
||||
mut commands: Commands,
|
||||
mut events: EventReader<MenuOpenedEvent>,
|
||||
mut query: Query<&mut InventoryComponent>,
|
||||
) {
|
||||
for event in events.iter() {
|
||||
commands
|
||||
.entity(event.entity)
|
||||
.remove::<WaitingForInventoryOpen>();
|
||||
|
||||
let mut inventory = query.get_mut(event.entity).unwrap();
|
||||
inventory.id = event.window_id as i8;
|
||||
inventory.container_menu = Some(Menu::from_kind(event.menu_type));
|
||||
}
|
||||
}
|
||||
|
||||
/// Close a container without notifying the server.
|
||||
///
|
||||
/// Note that this also gets fired from [`CloseContainerEvent`].
|
||||
/// Note that this also gets fired when we get a [`CloseContainerEvent`].
|
||||
pub struct ClientSideCloseContainerEvent {
|
||||
pub entity: Entity,
|
||||
}
|
|
@ -19,7 +19,7 @@ mod entity_query;
|
|||
mod events;
|
||||
mod get_mc_dir;
|
||||
pub mod interact;
|
||||
pub mod inventory;
|
||||
pub mod inventory_plugin;
|
||||
mod local_player;
|
||||
mod movement;
|
||||
pub mod packet_handling;
|
||||
|
|
|
@ -39,7 +39,7 @@ use crate::{
|
|||
chat::{ChatPacket, ChatReceivedEvent},
|
||||
client::TabList,
|
||||
disconnect::DisconnectEvent,
|
||||
inventory::{ClientSideCloseContainerEvent, InventoryComponent},
|
||||
inventory_plugin::{ClientSideCloseContainerEvent, InventoryComponent, MenuOpenedEvent},
|
||||
local_player::{GameProfileComponent, LocalGameMode, LocalPlayer},
|
||||
ClientInformation, PlayerInfo,
|
||||
};
|
||||
|
@ -971,7 +971,17 @@ fn process_packet_events(ecs: &mut World) {
|
|||
ClientboundGamePacket::MerchantOffers(_) => {}
|
||||
ClientboundGamePacket::MoveVehicle(_) => {}
|
||||
ClientboundGamePacket::OpenBook(_) => {}
|
||||
ClientboundGamePacket::OpenScreen(_) => {}
|
||||
ClientboundGamePacket::OpenScreen(p) => {
|
||||
let mut system_state: SystemState<EventWriter<MenuOpenedEvent>> =
|
||||
SystemState::new(ecs);
|
||||
let mut menu_opened_events = system_state.get_mut(ecs);
|
||||
menu_opened_events.send(MenuOpenedEvent {
|
||||
entity: player_entity,
|
||||
window_id: p.container_id,
|
||||
menu_type: p.menu_type,
|
||||
title: p.title,
|
||||
})
|
||||
}
|
||||
ClientboundGamePacket::OpenSignEditor(_) => {}
|
||||
ClientboundGamePacket::Ping(_) => {}
|
||||
ClientboundGamePacket::PlaceGhostRecipe(_) => {}
|
||||
|
|
|
@ -5,6 +5,8 @@ use quote::quote;
|
|||
pub fn generate(input: &DeclareMenus) -> TokenStream {
|
||||
let mut slot_mut_match_variants = quote! {};
|
||||
let mut len_match_variants = quote! {};
|
||||
let mut kind_match_variants = quote! {};
|
||||
let mut contents_match_variants = quote! {};
|
||||
|
||||
let mut hotbar_slot_start = 0;
|
||||
let mut hotbar_slot_end = 0;
|
||||
|
@ -12,6 +14,8 @@ pub fn generate(input: &DeclareMenus) -> TokenStream {
|
|||
for menu in &input.menus {
|
||||
slot_mut_match_variants.extend(generate_match_variant_for_slot_mut(menu));
|
||||
len_match_variants.extend(generate_match_variant_for_len(menu));
|
||||
kind_match_variants.extend(generate_match_variant_for_kind(menu));
|
||||
contents_match_variants.extend(generate_match_variant_for_contents(menu));
|
||||
|
||||
// this part is only used to generate `Player::is_hotbar_slot`
|
||||
if menu.name == "Player" {
|
||||
|
@ -55,6 +59,19 @@ pub fn generate(input: &DeclareMenus) -> TokenStream {
|
|||
#len_match_variants
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_kind(kind: azalea_registry::MenuKind) -> Self {
|
||||
match kind {
|
||||
#kind_match_variants
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the contents of the menu, not including the player's inventory.
|
||||
pub fn contents(&self) -> Vec<ItemSlot> {
|
||||
match self {
|
||||
#contents_match_variants
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +131,67 @@ pub fn generate_match_variant_for_len(menu: &Menu) -> TokenStream {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn generate_match_variant_for_kind(menu: &Menu) -> TokenStream {
|
||||
// azalea_registry::MenuKind::Player => Menu::Player(Player::default()),
|
||||
// azalea_registry::MenuKind::Generic9x3 => Menu::Generic9x3 { contents:
|
||||
// Default::default(), player: Default::default() },
|
||||
|
||||
let menu_name = &menu.name;
|
||||
let menu_field_names = if menu.name == "Player" {
|
||||
return quote! {};
|
||||
} else {
|
||||
let mut menu_field_names = quote! {};
|
||||
for field in &menu.fields {
|
||||
let field_name = &field.name;
|
||||
menu_field_names.extend(quote! { #field_name: Default::default(), })
|
||||
}
|
||||
quote! { { #menu_field_names } }
|
||||
};
|
||||
|
||||
quote! {
|
||||
azalea_registry::MenuKind::#menu_name => Menu::#menu_name #menu_field_names,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_match_variant_for_contents(menu: &Menu) -> TokenStream {
|
||||
// Menu::Generic9x3(m) => {
|
||||
// let mut contents = Vec::new();
|
||||
// contents.extend(player.m.iter().copied());
|
||||
// ...
|
||||
// contents
|
||||
// },
|
||||
// Menu::Generic9x3(m) => {
|
||||
// let mut contents = Vec::new();
|
||||
// contents.extend(m.contents.iter().copied());
|
||||
// contents
|
||||
// },
|
||||
|
||||
let mut instructions = quote! {};
|
||||
let mut length = 0;
|
||||
for field in &menu.fields {
|
||||
let field_name = &field.name;
|
||||
if field_name == "player" {
|
||||
continue;
|
||||
}
|
||||
instructions.extend(if field.length == 1 {
|
||||
quote! { items.push(#field_name.clone()); }
|
||||
} else {
|
||||
quote! { items.extend(#field_name.iter().cloned()); }
|
||||
});
|
||||
length += field.length;
|
||||
}
|
||||
|
||||
generate_matcher(
|
||||
menu,
|
||||
"e! {
|
||||
let mut items = Vec::with_capacity(#length);
|
||||
#instructions
|
||||
items
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_matcher(menu: &Menu, match_arms: &TokenStream, needs_fields: bool) -> TokenStream {
|
||||
let menu_name = &menu.name;
|
||||
let menu_field_names = if needs_fields {
|
||||
|
|
|
@ -27,6 +27,21 @@ impl<const N: usize> Default for SlotList<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
/// Get the [`Player`] from this [`Menu`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the menu isn't `Menu::Player`.
|
||||
pub fn as_player(&self) -> &Player {
|
||||
if let Menu::Player(player) = &self {
|
||||
player
|
||||
} else {
|
||||
unreachable!("Called `Menu::as_player` on a menu that wasn't `Player`.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the player inventory part is always the last 36 slots (except in the Player
|
||||
// menu), so we don't have to explicitly specify it
|
||||
|
||||
|
@ -142,18 +157,3 @@ declare_menus! {
|
|||
result: 1,
|
||||
},
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
/// Get the [`Player`] from this [`Menu`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the menu isn't `Menu::Player`.
|
||||
pub fn as_player(&self) -> &Player {
|
||||
if let Menu::Player(player) = &self {
|
||||
player
|
||||
} else {
|
||||
unreachable!("Called `Menu::as_player` on a menu that wasn't `Player`.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,15 @@ pub enum ItemSlot {
|
|||
Present(ItemSlotData),
|
||||
}
|
||||
|
||||
impl ItemSlot {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
matches!(self, ItemSlot::Empty)
|
||||
}
|
||||
pub fn is_present(&self) -> bool {
|
||||
matches!(self, ItemSlot::Present(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// An item in an inventory, with a count and NBT. Usually you want [`ItemSlot`]
|
||||
/// or [`azalea_registry::Item`] instead.
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
|
|
|
@ -6,6 +6,6 @@ use azalea_protocol_macros::ClientboundGamePacket;
|
|||
pub struct ClientboundOpenScreenPacket {
|
||||
#[var]
|
||||
pub container_id: u32,
|
||||
pub menu_type: azalea_registry::Menu,
|
||||
pub menu_type: azalea_registry::MenuKind,
|
||||
pub title: FormattedText,
|
||||
}
|
||||
|
|
|
@ -3010,7 +3010,7 @@ enum MemoryModuleKind {
|
|||
}
|
||||
|
||||
registry! {
|
||||
enum Menu {
|
||||
enum MenuKind {
|
||||
Generic9x1 => "minecraft:generic_9x1",
|
||||
Generic9x2 => "minecraft:generic_9x2",
|
||||
Generic9x3 => "minecraft:generic_9x3",
|
||||
|
|
|
@ -18,6 +18,7 @@ azalea-block = { version = "0.6.0", path = "../azalea-block" }
|
|||
azalea-chat = { version = "0.6.0", path = "../azalea-chat" }
|
||||
azalea-client = { version = "0.6.0", path = "../azalea-client" }
|
||||
azalea-core = { version = "0.6.0", path = "../azalea-core" }
|
||||
azalea-inventory = { version = "0.1.0", path = "../azalea-inventory" }
|
||||
azalea-physics = { version = "0.6.0", path = "../azalea-physics" }
|
||||
azalea-protocol = { version = "0.6.0", path = "../azalea-protocol" }
|
||||
azalea-registry = { version = "0.6.0", path = "../azalea-registry" }
|
||||
|
|
|
@ -48,7 +48,10 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|||
return Ok(());
|
||||
};
|
||||
bot.goto(chest_block.into());
|
||||
let chest = bot.open_container(&chest_block).await.unwrap();
|
||||
let Some(chest) = bot.open_container(&chest_block).await else {
|
||||
println!("Couldn't open chest");
|
||||
return Ok(());
|
||||
};
|
||||
bot.take_amount_from_container(&chest, 5, |i| i.id == "#minecraft:planks")
|
||||
.await;
|
||||
chest.close().await;
|
||||
|
|
|
@ -6,7 +6,8 @@ use azalea::ecs::query::With;
|
|||
use azalea::entity::metadata::Player;
|
||||
use azalea::entity::{EyeHeight, Position};
|
||||
use azalea::interact::HitResultComponent;
|
||||
use azalea::inventory::InventoryComponent;
|
||||
use azalea::inventory::ItemSlot;
|
||||
use azalea::inventory_plugin::InventoryComponent;
|
||||
use azalea::pathfinder::BlockPosGoal;
|
||||
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
|
||||
use azalea::{Account, Client, Event};
|
||||
|
@ -149,17 +150,17 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
|||
println!("inventory: {:?}", inventory.menu());
|
||||
}
|
||||
"findblock" => {
|
||||
let target_pos = bot.world().read().find_block(
|
||||
bot.position(),
|
||||
&azalea_registry::Block::DiamondBlock.into(),
|
||||
);
|
||||
let target_pos = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_block(bot.position(), &azalea::Block::DiamondBlock.into());
|
||||
bot.chat(&format!("target_pos: {target_pos:?}",));
|
||||
}
|
||||
"gotoblock" => {
|
||||
let target_pos = bot.world().read().find_block(
|
||||
bot.position(),
|
||||
&azalea_registry::Block::DiamondBlock.into(),
|
||||
);
|
||||
let target_pos = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_block(bot.position(), &azalea::Block::DiamondBlock.into());
|
||||
if let Some(target_pos) = target_pos {
|
||||
// +1 to stand on top of the block
|
||||
bot.goto(BlockPosGoal::from(target_pos.up(1)));
|
||||
|
@ -171,7 +172,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
|||
let target_pos = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_block(bot.position(), &azalea_registry::Block::Lever.into());
|
||||
.find_block(bot.position(), &azalea::Block::Lever.into());
|
||||
let Some(target_pos) = target_pos else {
|
||||
bot.chat("no lever found");
|
||||
return Ok(())
|
||||
|
@ -184,6 +185,27 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
|||
let hit_result = bot.get_component::<HitResultComponent>();
|
||||
bot.chat(&format!("hit_result: {hit_result:?}",));
|
||||
}
|
||||
"chest" => {
|
||||
let target_pos = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_block(bot.position(), &azalea::Block::Chest.into());
|
||||
let Some(target_pos) = target_pos else {
|
||||
bot.chat("no chest found");
|
||||
return Ok(())
|
||||
};
|
||||
bot.look_at(target_pos.center());
|
||||
let container = bot.open_container(target_pos).await;
|
||||
if let Some(container) = container {
|
||||
for item in container.contents() {
|
||||
if let ItemSlot::Present(item) = item {
|
||||
println!("item: {:?}", item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("no container found");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|||
bot.goto(pathfinder::Goals::NearXZ(5, azalea::BlockXZ(0, 0)))
|
||||
.await;
|
||||
let chest = bot
|
||||
.open_container(&bot.world().find_block(azalea_registry::Block::Chest))
|
||||
.open_container(&bot.world().find_block(azalea::Block::Chest))
|
||||
.await
|
||||
.unwrap();
|
||||
bot.take_amount_from_container(&chest, 5, |i| i.id == "#minecraft:planks")
|
||||
|
@ -46,9 +46,7 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|||
chest.close().await;
|
||||
|
||||
let crafting_table = bot
|
||||
.open_crafting_table(
|
||||
&bot.world.find_block(azalea_registry::Block::CraftingTable),
|
||||
)
|
||||
.open_crafting_table(&bot.world.find_block(azalea::Block::CraftingTable))
|
||||
.await
|
||||
.unwrap();
|
||||
bot.craft(&crafting_table, &bot.recipe_for("minecraft:sticks"))
|
||||
|
|
|
@ -10,6 +10,7 @@ use app::{App, Plugin, PluginGroup};
|
|||
pub use azalea_block as blocks;
|
||||
pub use azalea_client::*;
|
||||
pub use azalea_core::{BlockPos, Vec3};
|
||||
pub use azalea_inventory as inventory;
|
||||
pub use azalea_protocol as protocol;
|
||||
pub use azalea_registry::{Block, EntityKind};
|
||||
pub use azalea_world::{entity, Instance};
|
||||
|
|
|
@ -16,12 +16,16 @@ def generate_registries(registries: dict):
|
|||
# Stone => "minecraft:stone"
|
||||
# });
|
||||
|
||||
registry_name = registry_name.split(':')[1]
|
||||
|
||||
if registry_name.endswith('_type'):
|
||||
# change _type to _kind because that's Rustier (and because _type
|
||||
# is a reserved keyword)
|
||||
registry_name = registry_name[:-5] + '_kind'
|
||||
elif registry_name == 'menu':
|
||||
registry_name = 'menu_kind'
|
||||
|
||||
registry_struct_name = to_camel_case(registry_name.split(':')[1])
|
||||
registry_struct_name = to_camel_case(registry_name)
|
||||
|
||||
registry_code = []
|
||||
registry_code.append(f'enum {registry_struct_name} {{')
|
||||
|
|
Loading…
Add table
Reference in a new issue