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

Add packet SClickContainer and abstractions #60

Merged
merged 45 commits into from
Sep 8, 2024

Conversation

Bryntet
Copy link
Contributor

@Bryntet Bryntet commented Aug 24, 2024

Only the packet and abstraction has been implemented so far, the logic will be implemented shortly.

Bryntet and others added 30 commits August 24, 2024 19:15
we will store all opened containers there until closed by all players, and when closed we will save contents to the block in the world. We should implement Drop for OpenedContainer to achieve this.
@Snowiiii Snowiiii added the enhancement New feature or request label Sep 6, 2024
@Bryntet
Copy link
Contributor Author

Bryntet commented Sep 6, 2024

Tried to get double-clicking to work, but since everything else works, and I'm kind of tired of Inventory work, I'll just leave my PR as this.

Also, do note that due to how Pumpkin currently handles packets in quick succession (it doesn't, it sometimes drops them), dragging items only partially works.

@Bryntet Bryntet marked this pull request as ready for review September 6, 2024 19:53
@Bryntet
Copy link
Contributor Author

Bryntet commented Sep 6, 2024

In this PR, I have also added the command /echest which is very helpful when testing out the inventory system.

It is a global container, that all players can look at at the same time.

Comment on lines +51 to +52
pub open_containers: HashMap<u64, OpenContainer>,
pub drag_handler: DragHandler,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Luckly. I refactored the Server struct. Now we split most of the things game related into the world. We should put this into World aswell

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, containers should not have to be locked on World. World should save a unique ID for each container, and try to lock the container that way.

num-traits = "0.2"
num-derive = "0.4"


thiserror = "1.0.63"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really should make thiserror a workspace dependency. For now you should just use thiserror = "1.0"

thiserror = "1.0.63"
itertools = "0.13.0"
# For items
pumpkin-world = { path = "../pumpkin-world"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually prefer local crate imports above all other deps

pumpkin-inventory/src/container_click.rs Outdated Show resolved Hide resolved
pumpkin-inventory/src/container_click.rs Outdated Show resolved Hide resolved
Comment on lines +62 to +68
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
self.0.iter_mut().collect()
}

fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
self.0.iter().map(|slot| slot.as_ref()).collect()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really need to be an Vec?

Comment on lines +165 to +167
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.items.iter_mut().collect()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really need to be an Vec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gets combined with a separate Vec<&mut Option<ItemStack>> later, so we'd have to convert it into a Vec anyways

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing that as late as possible would be ideal, and it's not a requirement to allocate "right now"

Comment on lines +20 to +86
impl<'de> Deserialize<'de> for SClickContainer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = SClickContainer;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let window_id = seq
.next_element::<u8>()?
.ok_or(de::Error::custom("Failed to decode u8"))?;
let state_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;

let slot = seq
.next_element::<i16>()?
.ok_or(de::Error::custom("Failed to decode i16"))?;
let button = seq
.next_element::<i8>()?
.ok_or(de::Error::custom("Failed to decode i8"))?;
let mode = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
let length_of_array = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
let mut array_of_changed_slots = vec![];
for _ in 0..length_of_array.0 {
let slot_number = seq
.next_element::<i16>()?
.ok_or(de::Error::custom("Unable to parse slot"))?;
let slot = seq
.next_element::<Slot>()?
.ok_or(de::Error::custom("Unable to parse item"))?;
array_of_changed_slots.push((slot_number, slot));
}

let carried_item = seq
.next_element::<Slot>()?
.ok_or(de::Error::custom("Failed to decode carried item"))?;

Ok(SClickContainer {
window_id,
state_id,
slot,
button,
mode,
length_of_array,
array_of_changed_slots,
carried_item,
})
}
}

deserializer.deserialize_seq(Visitor)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you did this?. Do you need to Deserialize a custom way?. We use an custom ServerPacket impl theń

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See line 53 to 65 for the reason why it has to be manually deserialized.

match slot {
Some(item) => {
if item.item_id == item_in_pressed_slot.item_id
&& item.item_count != 64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stack sized does varie. We parse it from items.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought i put a TODO in the places where needed 👍

let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);

container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left)?;
*self.inventory.get_slot(changing_slot as usize)? = changing_item_slot;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also return this

&WindowType::Generic9x3
}

fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible for this to be a &[Option<&ItemStack>] or a &mut [Option<&ItemStack>] but more than that, I believe it's worth making Chest as a generic interface for any chest, rather than hardcoding it as a 27 item chest. Both double and single chests exist. Is there actually a need for a Chest to exist as more than an alias for a Container<WindowType::Generic9x3>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking is that maybe a plugin will want to have their own custom rules, and hook in to item presses and such, then they'd just have to define their own struct that implements Container

and the thought is that other containers that require special things be placed/taken from special places, will handle it here.

I get that Chest as it is now is only a wrapper, but since you can't impl a trait for a specific enum variant, it's much easier if you abstract it like this in the long run IMO

Comment on lines +7 to +11
crafting: [Option<ItemStack>; 4],
crafting_output: Option<ItemStack>,
items: [Option<ItemStack>; 36],
armor: [Option<ItemStack>; 4],
offhand: Option<ItemStack>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, why not make these Generic containers instead of handling them "manually"

Comment on lines +165 to +167
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.items.iter_mut().collect()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing that as late as possible would be ideal, and it's not a requirement to allocate "right now"

pumpkin/Cargo.toml Outdated Show resolved Hide resolved
@Snowiiii Snowiiii merged commit 8b29d9e into Pumpkin-MC:master Sep 8, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants