Skip to content

Conversation

@dyc3
Copy link
Collaborator

@dyc3 dyc3 commented Jun 6, 2023

Description

  • refactor handle_click_slot to primarily emit ClickSlot events, and handle those in a new system
  • fix cursor item updates not being sent if an inventory is open
  • very rough inventory menu behavior
  • only allow ClickMode::Click
  • emit MenuClickEvents for inventory menu clicks

related: #307

This PR makes it possible to do inventory menus. I don't particularly like the approach so far, and it doesn't quite cover all the use cases. I think I'll need to do some refactoring of how inventories work before continuing work on this.

Playground
use valence::client::despawn_disconnected_clients;
use valence::client::hand_swing::HandSwingEvent;
use valence::inventory::menu::{InventoryMenu, InventoryMenuPlugin, MenuClickEvent, OpenMenu};
use valence::network::ConnectionMode;
use valence::prelude::*;

#[allow(unused_imports)]
use crate::extras::*;

const SPAWN_Y: i32 = 64;

pub fn build_app(app: &mut App) {
    app.insert_resource(NetworkSettings {
        connection_mode: ConnectionMode::Offline,
        ..Default::default()
    })
    .add_plugins(DefaultPlugins)
    .add_startup_system(setup)
    .add_system(init_clients)
    .add_system(despawn_disconnected_clients)
    .add_system(toggle_gamemode_on_sneak.in_schedule(EventLoopSchedule))
    .add_plugin(InventoryMenuPlugin::default())
    .add_system(open_menu.in_schedule(EventLoopSchedule))
    .add_system(handle_menu.in_schedule(EventLoopSchedule));
}

fn setup(
    mut commands: Commands,
    server: Res<Server>,
    biomes: Query<&Biome>,
    dimensions: Query<&DimensionType>,
) {
    let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server);

    for z in -5..5 {
        for x in -5..5 {
            instance.insert_chunk([x, z], Chunk::default());
        }
    }

    for z in -25..25 {
        for x in -25..25 {
            instance.set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK);
        }
    }

    commands.spawn(instance);
}

fn init_clients(
    mut clients: Query<(&mut Location, &mut Position), Added<Client>>,
    instances: Query<Entity, With<Instance>>,
) {
    for (mut loc, mut pos) in &mut clients {
        loc.0 = instances.single();
        pos.set([0.5, SPAWN_Y as f64 + 1.0, 0.5]);
    }
}

// Add more systems here!

fn open_menu(mut events: EventReader<HandSwingEvent>, mut commands: Commands) {
    for event in events.iter() {
        println!("HandSwingEvent: {:?}", event);
        // let (client) = clients.get(event.client);
        if event.hand == Hand::Main {
            println!("opening menu");
            let mut inv = Inventory::new(InventoryKind::Generic3x3);
            inv.set_slot(3, Some(ItemStack::new(ItemKind::RedWool, 1, None)));
            inv.set_slot(5, Some(ItemStack::new(ItemKind::BlueWool, 1, None)));

            let menu = commands.spawn((inv, InventoryMenu {})).id();
            commands
                .entity(event.client)
                .insert((OpenInventory::new(menu), OpenMenu {}));
        }
    }
}

fn handle_menu(
    mut menu_clicks: EventReader<MenuClickEvent>,
    mut clients: Query<(&mut Client, &OpenInventory), With<OpenMenu>>,
    mut commands: Commands,
) {
    for click in menu_clicks.iter() {
        println!("MenuClickEvent: {:?}", click);

        let (mut client, open_inv) = clients.get_mut(click.client).unwrap();

        if click.slot_id == 3 {
            client.send_message("You clicked the red wool!");
        } else if click.slot_id == 5 {
            client.send_message("You clicked the blue wool!");
        } else {
            client.send_message("You clicked something else!");
        }

        commands.entity(click.client).remove::<OpenMenu>();
        commands.entity(click.client).remove::<OpenInventory>();
        commands.entity(open_inv.entity).despawn();
    }
}

@rj00a
Copy link
Member

rj00a commented Jun 7, 2023

I still believe it should be possible to implement menus from userspace without using raw packets. If users have the ability to reliably undo inventory actions (which they should), then it follows that menus are doable. We should then move this menu plugin to a separate crate (valence_ui?).

Regarding refactoring,

  • We need to clean up the inventory events a bit. See this for reference.
    • append Event to the names
    • add an Entity handle of the inventory being interacted with (?)
    • remove details like state_id
    • abstraction for buttons
    • combine creative inventory actions with non-creative actions (?)
    • maybe more...
  • Make the held item slot a separate modifiable component with change detection. (Use an enum for this?, rename to "selected hotbar slot" to avoid confusion?)
  • Tweak tests so we can make things actually private instead of #[doc(hidden)].

rj00a pushed a commit that referenced this pull request Jun 7, 2023
## Description

This moves the `held_item_slot` field in `ClientInventoryState` to a new
component: `HeldItem`

related: pr #355
@maxomatic458 maxomatic458 mentioned this pull request Oct 8, 2024
dyc3 added a commit that referenced this pull request Oct 11, 2024
# Objective
- ability to make inventories read only (player should be able to click
things in the inventory and still emit a click event, this can be useful
for creating inventory menus.
- closes  #427
- related #307 #355 
# Solution

- adds a public ``readonly: bool`` field to the ``Inventory`` component,
that will make any interactions with this item impossible (includes:
moving, shift moving, hotbar moving, dropping) if a player inventory is
readonly, then the player will also not be able to drop items (even when
not in the inventory), so the drop event will not be emitted (this could
be changed if requested)
- when implementing this i discovered a bug where a player is not able
to put a item from a open inventory in the offhand (by hitting F) that
will cause a desync. On the client the item will be in the offhand, but
if you try to interact with that it dissapears. (unrelated to this PR
and will not be fixed in this PR)

---------

Co-authored-by: Carson McManus <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants