-
-
Notifications
You must be signed in to change notification settings - Fork 169
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
Conversation
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.
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. |
In this PR, I have also added the command It is a global container, that all players can look at at the same time. |
pub open_containers: HashMap<u64, OpenContainer>, | ||
pub drag_handler: DragHandler, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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"
pumpkin-inventory/Cargo.toml
Outdated
thiserror = "1.0.63" | ||
itertools = "0.13.0" | ||
# For items | ||
pumpkin-world = { path = "../pumpkin-world"} |
There was a problem hiding this comment.
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
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() | ||
} |
There was a problem hiding this comment.
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?
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> { | ||
self.items.iter_mut().collect() | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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"
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) | ||
} | ||
} |
There was a problem hiding this comment.
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ń
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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>> { |
There was a problem hiding this comment.
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>
?
There was a problem hiding this comment.
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
crafting: [Option<ItemStack>; 4], | ||
crafting_output: Option<ItemStack>, | ||
items: [Option<ItemStack>; 36], | ||
armor: [Option<ItemStack>; 4], | ||
offhand: Option<ItemStack>, |
There was a problem hiding this comment.
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"
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> { | ||
self.items.iter_mut().collect() | ||
} |
There was a problem hiding this comment.
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"
…les on handle_item_change
Only the packet and abstraction has been implemented so far, the logic will be implemented shortly.