Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rough click slot packet validation #293

Merged
merged 51 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e524c06
somewhat minimal validation
dyc3 Mar 19, 2023
11fbb4f
pass in open inventory to use to validate
dyc3 Mar 19, 2023
d91bc9f
add more validations
dyc3 Mar 19, 2023
682e8df
more validations
dyc3 Mar 19, 2023
7dde012
add InventoryWindow and InventoryWindowMut type
dyc3 Mar 19, 2023
f092bea
fix calculate_net_item_delta (i think?)
dyc3 Mar 19, 2023
470476b
fix some unit tests
dyc3 Mar 19, 2023
dabbe40
fix more unit tests
dyc3 Mar 19, 2023
5b7ab98
more validations
dyc3 Mar 19, 2023
2a86557
add unit test for alchemy denial
dyc3 Mar 20, 2023
fa978d7
add some sanity tests and a doc comment
dyc3 Mar 20, 2023
715d006
fix validation for drag operations
dyc3 Mar 20, 2023
feb01e7
add unit test for handling dragging operations
dyc3 Mar 20, 2023
6f7e2ea
fix inventory desync on click and drag packets, fixes #270
dyc3 Mar 20, 2023
806eb9f
validate item stack amounts
dyc3 Mar 20, 2023
e1ceddb
move InventoryWindow helpers to inventory, make pub
dyc3 Mar 20, 2023
bf6deee
add doc comments for inventory window helpers and add to prelude
dyc3 Mar 20, 2023
716ecff
validate transmutation for shift click and hotbar clicks
dyc3 Mar 20, 2023
edc89b3
validate transmutations for regular clicks
dyc3 Mar 20, 2023
64aa265
refactor drop via click validation
dyc3 Mar 20, 2023
6ef19ea
validate transmuting for dropkey clicks
dyc3 Mar 20, 2023
f21b1d3
fix clippy lints
dyc3 Mar 20, 2023
5df4eaf
fix more lints
dyc3 Mar 20, 2023
a19280a
Update crates/valence/src/inventory/validate.rs
dyc3 Mar 21, 2023
7d77e04
Update crates/valence/src/inventory/validate.rs
dyc3 Mar 21, 2023
279e5f2
replace magic number with constant
dyc3 Mar 21, 2023
5c633e0
add comment
dyc3 Mar 21, 2023
956eacb
convert validate_click_slot_impossible to return Result
dyc3 Mar 21, 2023
85519c3
update unit tests
dyc3 Mar 21, 2023
8d0963b
update call site
dyc3 Mar 21, 2023
c1f37b7
refactor validate_click_slot_item_duplication to return Result
dyc3 Mar 21, 2023
b789812
small refactor
dyc3 Mar 21, 2023
f5a1e56
fix splitting stacks failing validation
dyc3 Mar 21, 2023
1d8045e
send inventory packet upon failing validation
dyc3 Mar 21, 2023
f3d521d
fix clippy lints
dyc3 Mar 21, 2023
057ba5a
fix typo
dyc3 Mar 21, 2023
182a24f
add feature flag to let users opt out of item duplication checks
dyc3 Mar 21, 2023
f809ba6
revert changes to chest example
dyc3 Mar 22, 2023
ebc0c30
refine how the maximum stack size is determined
dyc3 Mar 22, 2023
d417dad
add resource for inventory settings
dyc3 Mar 22, 2023
f49e094
fix unit tests
dyc3 Mar 22, 2023
eee7d81
Merge branch 'main' into inv-packet-validation
dyc3 Mar 22, 2023
34287f6
remove nbt checks because they were causing problems, add todo
dyc3 Mar 22, 2023
5de9190
fix some assertions
dyc3 Mar 23, 2023
5a2b2a1
add a couple more new unit tests
dyc3 Mar 23, 2023
e7c43b3
clamp max stack size to actual max stack size
dyc3 Mar 23, 2023
5406f8a
fix assertion so `allow_place_overfull_stack_click` passes
dyc3 Mar 23, 2023
31a17a6
fix assertions for shift clicking
dyc3 Mar 23, 2023
2759d3d
update call site
dyc3 Mar 23, 2023
3d03b41
fix call sites in unit tests
dyc3 Mar 23, 2023
2890c9b
Merge branch 'main' into inv-packet-validation
dyc3 Mar 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 74 additions & 8 deletions crates/valence/src/client/event.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cmp;

use anyhow::bail;
Expand All @@ -8,7 +9,7 @@ use bevy_ecs::schedule::ScheduleLabel;
use bevy_ecs::system::{SystemParam, SystemState};
use glam::{DVec3, Vec3};
use paste::paste;
use tracing::warn;
use tracing::{debug, warn};
use uuid::Uuid;
use valence_protocol::block_pos::BlockPos;
use valence_protocol::ident::Ident;
Expand All @@ -27,15 +28,19 @@ use valence_protocol::packet::c2s::play::update_structure_block::{
use valence_protocol::packet::c2s::play::{
AdvancementTabC2s, ClientStatusC2s, ResourcePackStatusC2s, UpdatePlayerAbilitiesC2s,
};
use valence_protocol::packet::s2c::play::InventoryS2c;
use valence_protocol::packet::C2sPlayPacket;
use valence_protocol::types::{Difficulty, Direction, Hand};
use valence_protocol::var_int::VarInt;

use super::{
CursorItem, KeepaliveState, PlayerActionSequence, PlayerInventoryState, TeleportState,
};
use crate::client::Client;
use crate::component::{Look, OnGround, Ping, Position};
use crate::inventory::Inventory;
use crate::inventory::{Inventory, InventorySettings};
use crate::packet::WritePacket;
use crate::prelude::OpenInventory;

#[derive(Clone, Debug)]
pub struct QueryBlockNbt {
Expand Down Expand Up @@ -671,6 +676,7 @@ pub(crate) struct EventLoopQuery {
keepalive_state: &'static mut KeepaliveState,
cursor_item: &'static mut CursorItem,
inventory: &'static mut Inventory,
open_inventory: Option<&'static mut OpenInventory>,
position: &'static mut Position,
look: &'static mut Look,
on_ground: &'static mut OnGround,
Expand All @@ -682,10 +688,17 @@ pub(crate) struct EventLoopQuery {
/// An exclusive system for running the event loop schedule.
fn run_event_loop(
world: &mut World,
state: &mut SystemState<(Query<EventLoopQuery>, ClientEvents, Commands)>,
state: &mut SystemState<(
Query<EventLoopQuery>,
ClientEvents,
Commands,
Query<&Inventory, Without<Client>>,
Res<InventorySettings>,
)>,
mut clients_to_check: Local<Vec<Entity>>,
) {
let (mut clients, mut events, mut commands) = state.get_mut(world);
let (mut clients, mut events, mut commands, mut inventories, inventory_settings) =
state.get_mut(world);

update_all_event_buffers(&mut events);

Expand All @@ -703,7 +716,7 @@ fn run_event_loop(

q.client.dec.queue_bytes(bytes);

match handle_one_packet(&mut q, &mut events) {
match handle_one_packet(&mut q, &mut events, &mut inventories, &inventory_settings) {
Ok(had_packet) => {
if had_packet {
// We decoded one packet, but there might be more.
Expand All @@ -723,15 +736,16 @@ fn run_event_loop(
while !clients_to_check.is_empty() {
world.run_schedule(EventLoopSchedule);

let (mut clients, mut events, mut commands) = state.get_mut(world);
let (mut clients, mut events, mut commands, mut inventories, inventory_settings) =
state.get_mut(world);

clients_to_check.retain(|&entity| {
let Ok(mut q) = clients.get_mut(entity) else {
// Client must have been deleted during the last run of the schedule.
return false;
};

match handle_one_packet(&mut q, &mut events) {
match handle_one_packet(&mut q, &mut events, &mut inventories, &inventory_settings) {
Ok(had_packet) => had_packet,
Err(e) => {
warn!("failed to dispatch events for client {:?}: {e:?}", q.entity);
Expand All @@ -748,6 +762,8 @@ fn run_event_loop(
fn handle_one_packet(
q: &mut EventLoopQueryItem,
events: &mut ClientEvents,
inventories: &mut Query<&Inventory, Without<Client>>,
inventory_settings: &Res<InventorySettings>,
) -> anyhow::Result<bool> {
let Some(pkt) = q.client.dec.try_next_packet::<C2sPlayPacket>()? else {
// No packets to decode.
Expand Down Expand Up @@ -847,7 +863,57 @@ fn handle_one_packet(
});
}
C2sPlayPacket::ClickSlotC2s(p) => {
if p.slot_idx < 0 {
let open_inv = q
.open_inventory
.as_ref()
.and_then(|open| inventories.get_mut(open.entity).ok());
if let Err(msg) =
crate::inventory::validate_click_slot_impossible(&p, &q.inventory, open_inv)
{
debug!(
"client {:#?} invalid click slot packet: \"{}\" {:#?}",
q.entity, msg, p
);
let inventory = open_inv.unwrap_or(&q.inventory);
q.client.write_packet(&InventoryS2c {
window_id: if open_inv.is_some() {
q.player_inventory_state.window_id
} else {
0
},
state_id: VarInt(q.player_inventory_state.state_id.0),
slots: Cow::Borrowed(inventory.slot_slice()),
carried_item: Cow::Borrowed(&q.cursor_item.0),
});
return Ok(true);
}
if inventory_settings.enable_item_dupe_check {
if let Err(msg) = crate::inventory::validate_click_slot_item_duplication(
&p,
&q.inventory,
open_inv,
&q.cursor_item,
) {
debug!(
"client {:#?} click slot packet tried to incorrectly modify items: \"{}\" \
{:#?}",
q.entity, msg, p
);
let inventory = open_inv.unwrap_or(&q.inventory);
q.client.write_packet(&InventoryS2c {
window_id: if open_inv.is_some() {
q.player_inventory_state.window_id
} else {
0
},
state_id: VarInt(q.player_inventory_state.state_id.0),
slots: Cow::Borrowed(inventory.slot_slice()),
carried_item: Cow::Borrowed(&q.cursor_item.0),
});
return Ok(true);
}
}
if p.slot_idx < 0 && p.mode == ClickMode::Click {
if let Some(stack) = q.cursor_item.0.take() {
events.2.drop_item_stack.send(DropItemStack {
client: entity,
Expand Down
Loading