Skip to content

Commit cb0d2c6

Browse files
authored
[ECS] Inventory interactions (#189)
- add helper to convert slot ids - implement system to handle click container events - update inventory_test example with block platform and some chests to open - add toggle_gamemode_on_sneak system to example - fix query filter that was causing clients to get spammed with OpenScreen - move state_id to `Client` component - only send modified slots when observed inventories are changed - force all click container packets to be handled before update packets are built and sent - mark inventories as dirty instead of just sending the contents - add handle_set_slot_creative to handle SetCreativeModeSlot events - exclude clients with currently open inventories from being updated by `update_player_inventories` ### Test plan 1. Start the `inventory_test` example ```bash cargo run --example inventory_test ``` 2. Join the server 3. Walk to the small brick square 4. The copper, iron, and gold blocks have chests on top of them (they are invisible until we have block entities) 5. Give yourself some items 6. Place those items in one of the inventories 7. Give yourself some more items 8. Sneak to change game mode to survival 9. Open the same inventory and move some items between your inventory and the chest's inventory. 10. Repeat 5-9, but have another client join and observe the chest you're interacting with. 11. The second client should be able to see the inventory update in real time.
1 parent c58faad commit cb0d2c6

File tree

4 files changed

+324
-94
lines changed

4 files changed

+324
-94
lines changed

crates/valence_new/examples/inventory_test.rs

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use bevy_ecs::prelude::*;
22
use bevy_ecs::schedule::ShouldRun;
33
use tracing::info;
4-
use valence_new::client::event::default_event_handler;
4+
use valence_new::client::event::{default_event_handler, StartSneaking, UseItemOnBlock};
55
use valence_new::client::{despawn_disconnected_clients, Client};
66
use valence_new::config::{Config, ConnectionMode};
77
use valence_new::dimension::DimensionId;
8+
use valence_new::instance::Chunk;
89
use valence_new::inventory::{Inventory, InventoryKind, OpenInventory};
910
use valence_new::protocol::types::GameMode;
1011
use valence_new::server::Server;
11-
use valence_protocol::{ItemKind, ItemStack};
12+
use valence_protocol::{BlockState, ItemKind, ItemStack};
1213

1314
#[derive(Resource)]
1415
struct GameState {
@@ -17,26 +18,56 @@ struct GameState {
1718
}
1819

1920
fn main() -> anyhow::Result<()> {
20-
tracing_subscriber::fmt().init();
21+
tracing_subscriber::fmt()
22+
.with_max_level(tracing::Level::DEBUG)
23+
.init();
2124

2225
valence_new::run_server(
2326
Config::default().with_connection_mode(ConnectionMode::Offline),
2427
SystemStage::parallel()
2528
.with_system(setup.with_run_criteria(ShouldRun::once))
2629
.with_system(init_clients)
27-
.with_system(open_inventory_test)
28-
.with_system(blink_items)
2930
.with_system(default_event_handler())
30-
.with_system(despawn_disconnected_clients),
31+
.with_system(despawn_disconnected_clients)
32+
// .with_system(open_inventory_test)
33+
// .with_system(blink_items)
34+
.with_system(open_inventory_on_interact)
35+
.with_system(toggle_gamemode_on_sneak),
3136
(),
3237
)
3338
}
3439

3540
fn setup(world: &mut World) {
36-
let instance = world
41+
let mut instance = world
3742
.resource::<Server>()
3843
.new_instance(DimensionId::default());
3944

45+
// Create spawn platform.
46+
for z in -5..5 {
47+
for x in -5..5 {
48+
let mut chunk = Chunk::new(24);
49+
for z in 0..16 {
50+
for x in 0..16 {
51+
chunk.set_block_state(x, 10, z, BlockState::STONE);
52+
}
53+
}
54+
55+
if x == 0 && z == 0 {
56+
for sx in 0..3 {
57+
for sz in 0..3 {
58+
chunk.set_block_state(sx, 10, sz, BlockState::BRICKS);
59+
}
60+
chunk.set_block_state(sx, 11, 0, BlockState::CHEST);
61+
}
62+
chunk.set_block_state(0, 10, 0, BlockState::COPPER_BLOCK);
63+
chunk.set_block_state(1, 10, 0, BlockState::IRON_BLOCK);
64+
chunk.set_block_state(2, 10, 0, BlockState::GOLD_BLOCK);
65+
}
66+
67+
instance.insert_chunk([x, z], chunk);
68+
}
69+
}
70+
4071
let id = world.spawn(instance).id();
4172
world.insert_resource(GameState {
4273
instance: id,
@@ -46,7 +77,7 @@ fn setup(world: &mut World) {
4677
// create inventories to view
4778
let mut inventories = [
4879
Inventory::new(InventoryKind::Generic9x2),
49-
Inventory::new(InventoryKind::Generic9x3),
80+
Inventory::new(InventoryKind::Generic9x6),
5081
Inventory::new(InventoryKind::Crafting),
5182
];
5283

@@ -94,3 +125,34 @@ fn blink_items(mut inventories: Query<&mut Inventory>) {
94125
}
95126
}
96127
}
128+
129+
fn open_inventory_on_interact(
130+
mut commands: Commands,
131+
inventories: Query<(Entity, With<Inventory>, Without<Client>)>,
132+
mut events: EventReader<UseItemOnBlock>,
133+
) {
134+
for event in events.iter() {
135+
let inventory_idx = event.position.x as usize % 3;
136+
info!("opening inventory {}", inventory_idx);
137+
let (target_inventory, _, _) = inventories.iter().skip(inventory_idx).next().unwrap();
138+
commands
139+
.entity(event.client)
140+
.insert(OpenInventory::new(target_inventory));
141+
}
142+
}
143+
144+
fn toggle_gamemode_on_sneak(
145+
mut clients: Query<&mut Client>,
146+
mut events: EventReader<StartSneaking>,
147+
) {
148+
for event in events.iter() {
149+
if let Ok(mut client) = clients.get_component_mut::<Client>(event.client) {
150+
let mode = client.game_mode();
151+
client.set_game_mode(match mode {
152+
GameMode::Survival => GameMode::Creative,
153+
GameMode::Creative => GameMode::Survival,
154+
_ => GameMode::Creative,
155+
});
156+
}
157+
}
158+
}

crates/valence_new/src/client.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt;
22
use std::net::IpAddr;
3+
use std::num::Wrapping;
34

45
use anyhow::{bail, Context};
56
use bevy_ecs::prelude::*;
@@ -79,6 +80,10 @@ pub struct Client {
7980
pub(crate) cursor_item_modified: bool,
8081
/// The current window ID. Incremented when inventories are opened.
8182
pub(crate) window_id: u8,
83+
pub(crate) inventory_state_id: Wrapping<i32>,
84+
/// Tracks what slots have been modified by this client in this tick, so we
85+
/// don't need to send updates for them.
86+
pub(crate) inventory_slots_modified: u64,
8287
}
8388

8489
impl Client {
@@ -124,6 +129,8 @@ impl Client {
124129
cursor_item: None,
125130
cursor_item_modified: false,
126131
window_id: 0,
132+
inventory_state_id: Wrapping(0),
133+
inventory_slots_modified: 0,
127134
}
128135
}
129136

0 commit comments

Comments
 (0)