Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
71 changes: 46 additions & 25 deletions src/Inventory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1670,16 +1670,21 @@ pub const Command = struct { // MARK: Command
};

const DepositToAny = struct { // MARK: DepositToAny
dest: Inventory,
destinations: []Inventory,
allocated: bool = false,
source: InventoryAndSlot,
amount: u16,

fn run(self: DepositToAny, ctx: Context) error{serverFailure}!void {
if(self.dest.type == .creative) return;
if(self.dest.type == .crafting) return;
if(self.dest.type == .workbench) return;
defer if(self.allocated) main.stackAllocator.free(self.destinations);
if(self.destinations.len == 0) return;
Copy link
Member

Choose a reason for hiding this comment

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

When would this even happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A modified Client can send anything. I just want to put a check in before it later causes bugs.

Copy link
Member

Choose a reason for hiding this comment

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

If this is about safety of client data, then you should put this check in the deserialization function.

Copy link
Member

Choose a reason for hiding this comment

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

Client data should generally be sanitized during or immediately after receiving.

for(self.destinations) |dest| {
if(dest.type == .creative) return;
if(dest.type == .crafting) return;
if(dest.type == .workbench) return;
}
if(self.source.inv.type == .crafting) {
ctx.cmd.tryCraftingTo(ctx.allocator, self.dest, self.source, ctx.side, ctx.user);
ctx.cmd.tryCraftingTo(ctx.allocator, self.destinations[0], self.source, ctx.side, ctx.user);
return;
}
const sourceStack = self.source.ref();
Expand All @@ -1688,41 +1693,57 @@ pub const Command = struct { // MARK: Command

var remainingAmount = self.amount;
var selectedEmptySlot: ?u32 = null;
for(self.dest._items, 0..) |*destStack, destSlot| {
if(destStack.item == .null and selectedEmptySlot == null) {
selectedEmptySlot = @intCast(destSlot);
}
if(std.meta.eql(destStack.item, sourceStack.item)) {
const amount = @min(sourceStack.item.stackSize() - destStack.amount, remainingAmount);
if(amount == 0) continue;
ctx.execute(.{.move = .{
.dest = .{.inv = self.dest, .slot = @intCast(destSlot)},
.source = self.source,
.amount = amount,
}});
remainingAmount -= amount;
if(remainingAmount == 0) break;
var selectedEmptyInv: u8 = 0;
Copy link
Member

Choose a reason for hiding this comment

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

Why limit this to a u8? It should be usize, there is nothing gained by using a u8, other than future bugs from the intCast.

for(self.destinations, 0..) |dest, destInv| {
for(dest._items, 0..) |*destStack, destSlot| {
if(destStack.item == .null and selectedEmptySlot == null) {
selectedEmptySlot = @intCast(destSlot);
selectedEmptyInv = @intCast(destInv);
}
if(std.meta.eql(destStack.item, sourceStack.item)) {
const amount = @min(sourceStack.item.stackSize() - destStack.amount, remainingAmount);
if(amount == 0) continue;
ctx.execute(.{.move = .{
.dest = .{.inv = dest, .slot = @intCast(destSlot)},
.source = self.source,
.amount = amount,
}});
remainingAmount -= amount;
if(remainingAmount == 0) break;
}
}
}
if(remainingAmount > 0 and selectedEmptySlot != null) {
ctx.execute(.{.move = .{
.dest = .{.inv = self.dest, .slot = selectedEmptySlot.?},
.dest = .{.inv = self.destinations[selectedEmptyInv], .slot = selectedEmptySlot.?},
.source = self.source,
.amount = remainingAmount,
}});
}
}

fn serialize(self: DepositToAny, writer: *utils.BinaryWriter) void {
writer.writeEnum(InventoryId, self.dest.id);
writer.writeInt(u8, @intCast(self.destinations.len));
Copy link
Member

Choose a reason for hiding this comment

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

Use a varint and send a usize
Putting an arbitrary limit on the number and crashing when exceeded is not helpful.
If you want to limit this then you should have a good reason for it, and 256 might definitely be in reach for a deposit to nearby chests function.

for(self.destinations) |dest| {
writer.writeEnum(InventoryId, dest.id);
}
self.source.write(writer);
writer.writeInt(u16, self.amount);
}

fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositToAny {
const destId = try reader.readEnum(InventoryId);
const destinationsSize = try reader.readInt(u8);
var destinations = main.stackAllocator.alloc(Inventory, destinationsSize);
errdefer main.stackAllocator.free(destinations);

for(destinations) |*dest| {
const invId = try reader.readEnum(InventoryId);
dest.* = Sync.getInventory(invId, side, user) orelse return error.InventoryNotFound;
}

return .{
.dest = Sync.getInventory(destId, side, user) orelse return error.InventoryNotFound,
.destinations = destinations[0..],
.allocated = true,
.source = try InventoryAndSlot.read(reader, side, user),
.amount = try reader.readInt(u16),
};
Expand Down Expand Up @@ -2178,8 +2199,8 @@ pub fn depositOrDrop(dest: Inventory, source: Inventory) void {
Sync.ClientSide.executeCommand(.{.depositOrDrop = .{.dest = dest, .source = source, .dropLocation = undefined}});
}

pub fn depositToAny(source: Inventory, sourceSlot: u32, dest: Inventory, amount: u16) void {
Sync.ClientSide.executeCommand(.{.depositToAny = .{.dest = dest, .source = .{.inv = source, .slot = sourceSlot}, .amount = amount}});
pub fn depositToAny(source: Inventory, sourceSlot: u32, destinations: []Inventory, amount: u16) void {
Sync.ClientSide.executeCommand(.{.depositToAny = .{.destinations = destinations, .source = .{.inv = source, .slot = sourceSlot}, .amount = amount}});
}

pub fn dropStack(source: Inventory, sourceSlot: u32) void {
Expand Down
9 changes: 6 additions & 3 deletions src/gui/gui.zig
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,8 @@ pub const inventory = struct { // MARK: inventory
nextCraftingAction = nextCraftingAction.addDuration(craftingCooldown);
craftingCooldown.nanoseconds -= @divTrunc((craftingCooldown.nanoseconds -% minCraftingCooldown.nanoseconds)*craftingCooldown.nanoseconds, std.time.ns_per_s);
if(mainGuiButton.modsOnPress.shift) {
itemSlot.inventory.depositToAny(itemSlot.itemSlot, main.game.Player.inventory, itemSlot.inventory.getAmount(itemSlot.itemSlot));
var destinations = [_]Inventory{main.game.Player.inventory};
itemSlot.inventory.depositToAny(itemSlot.itemSlot, destinations[0..], itemSlot.inventory.getAmount(itemSlot.itemSlot));
} else {
itemSlot.inventory.depositOrSwap(itemSlot.itemSlot, carried);
}
Expand All @@ -683,12 +684,14 @@ pub const inventory = struct { // MARK: inventory
var iterator = std.mem.reverseIterator(openWindows.items);
while(iterator.next()) |window| {
if(window.shiftClickableInventory) |inv| {
itemSlot.inventory.depositToAny(itemSlot.itemSlot, inv, itemSlot.inventory.getAmount(itemSlot.itemSlot));
var destinations = [_]Inventory{inv};
itemSlot.inventory.depositToAny(itemSlot.itemSlot, destinations[0..], itemSlot.inventory.getAmount(itemSlot.itemSlot));
break;
}
}
} else {
itemSlot.inventory.depositToAny(itemSlot.itemSlot, main.game.Player.inventory, itemSlot.inventory.getAmount(itemSlot.itemSlot));
var destinations = [_]Inventory{main.game.Player.inventory};
itemSlot.inventory.depositToAny(itemSlot.itemSlot, destinations[0..], itemSlot.inventory.getAmount(itemSlot.itemSlot));
}
return;
}
Expand Down