Skip to content
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
08ac5ea
try Crafting now accepts slice (sync.zig version)
Wunka Jan 15, 2026
0eaf344
same priority system
Wunka Jan 16, 2026
a5e00db
part 1
Wunka Jan 19, 2026
79ca1df
kinda working craftFrom
Wunka Jan 19, 2026
b6e03b5
its now ClientSide
Wunka Jan 19, 2026
e68e4a6
make priority right
Wunka Jan 19, 2026
5248ecc
remove check for sync (as its now client side)
Wunka Jan 19, 2026
d5249fb
format
Wunka Jan 19, 2026
e17b9d3
fix comments
Wunka Jan 19, 2026
92604f8
validRecipe helper function
Wunka Jan 20, 2026
5fa6137
Size -> Count
Wunka Jan 22, 2026
f577203
update to use put_items_into
Wunka Jan 24, 2026
1133fe1
did not save the remove...
Wunka Jan 24, 2026
bb616b5
format
Wunka Jan 24, 2026
d5ea8e3
yeah... it seems I choose a wrong option while rebasing...
Wunka Jan 24, 2026
9e11e57
change asserts to fit with the new .crafting
Wunka Jan 24, 2026
a441aca
small changes to align to with version before rebase
Wunka Jan 24, 2026
1d1071a
fix validRecipe
Wunka Jan 24, 2026
95f6d92
remove chachedInventory
Wunka Jan 24, 2026
5886c22
use Recipe for CraftFrom
Wunka Jan 24, 2026
b1c5dc3
move canHoldcheck
Wunka Jan 24, 2026
b00b2b5
Update src/sync.zig
Wunka Jan 24, 2026
9311229
readd recipe source
Wunka Jan 24, 2026
1cf6c33
Merge remote-tracking branch 'upstream/master' into craftFromSync
Wunka Jan 24, 2026
15801bc
apply changes to put_items_into
Wunka Jan 24, 2026
f71c896
small fix
Wunka Jan 24, 2026
d3fc30a
move CraftFrom
Wunka Jan 24, 2026
1bc1d8b
add getPossibleHoldingAmount
Wunka Jan 24, 2026
aa36664
reverse deletions
Wunka Jan 24, 2026
05de414
fix comment
Wunka Jan 24, 2026
6e0ec72
Update src/sync.zig
Wunka Jan 24, 2026
c6bda0b
remove recipe again
Wunka Jan 24, 2026
a82b8a9
Update src/Inventory.zig
Wunka Jan 25, 2026
95df9e0
small fixes and create remove_items_from
Wunka Jan 25, 2026
94e7b46
Inventories general structure
Wunka Jan 25, 2026
f6463cf
change the inner namespaces so the use Inventories
Wunka Jan 25, 2026
c072856
use the new Inventories struct
Wunka Jan 25, 2026
32ab97c
Merge remote-tracking branch 'upstream/master' into craftFromSync
Wunka Jan 25, 2026
40bee1e
add errdefer and readd remove
Wunka Jan 25, 2026
dd6a338
Update src/Inventory.zig
Wunka Jan 26, 2026
56edac5
readd removed code... why...
Wunka Jan 26, 2026
28b7e64
fixed dumb thing and added the failing check
Wunka Jan 26, 2026
db8891c
Update src/sync.zig
Wunka Jan 27, 2026
4ca7804
list?
Wunka Jan 27, 2026
f41dc25
check revived
Wunka Jan 27, 2026
3cf6816
Update src/items.zig
Wunka Jan 27, 2026
7b86c70
revert change
Wunka Jan 28, 2026
a6079d2
remove being able to take the a source item from recipe menu
Wunka Jan 28, 2026
6258aed
added immutable check
Wunka Jan 28, 2026
a5a2d6f
Update src/sync.zig
Wunka Jan 28, 2026
bff8dfd
allow recipes with 0 source tiems and use == instead of eql
Wunka Jan 28, 2026
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
72 changes: 52 additions & 20 deletions src/Inventory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ pub const ServerSide = struct { // MARK: ServerSide
pub fn createInventory(user: *main.server.User, clientId: InventoryId, len: usize, typ: Inventory.Type, source: Source) !void {
sync.threadContext.assertCorrectContext(.server);
switch (source) {
.recipe, .blockInventory, .playerInventory, .hand => {
.blockInventory, .playerInventory, .hand => {
switch (source) {
.playerInventory, .hand => |id| {
if (id != user.id) {
Expand All @@ -249,7 +249,7 @@ pub const ServerSide = struct { // MARK: ServerSide
return;
}
}
if (source != .recipe) return error.Invalid;
return error.Invalid;
},
.other => {},
.alreadyFreed => unreachable,
Expand All @@ -265,14 +265,6 @@ pub const ServerSide = struct { // MARK: ServerSide
switch (source) {
.blockInventory => unreachable, // Should be loaded by the block entity
.playerInventory, .hand => unreachable, // Should be loaded on player creation
.recipe => |recipe| {
for (0..recipe.sourceAmounts.len) |i| {
inventory.inv._items[i].amount = recipe.sourceAmounts[i];
inventory.inv._items[i].item = .{.baseItem = recipe.sourceItems[i]};
}
inventory.inv._items[inventory.inv._items.len - 1].amount = recipe.resultAmount;
inventory.inv._items[inventory.inv._items.len - 1].item = .{.baseItem = recipe.resultItem};
},
.other => {},
.alreadyFreed => unreachable,
}
Expand Down Expand Up @@ -369,23 +361,22 @@ pub const SourceType = enum(u8) {
alreadyFreed = 0,
playerInventory = 1,
hand = 3,
recipe = 4,
blockInventory = 5,
other = 0xff, // TODO: List every type separately here.
};
pub const Source = union(SourceType) {
alreadyFreed: void,
playerInventory: u32,
hand: u32,
recipe: *const main.items.Recipe,
blockInventory: Vec3i,
other: void,
};

pub const ClientInventory = struct { // MARK: ClientInventory
const ClientType = enum {
serverShared,
creative,
const ClientType = union(enum) {
serverShared: void,
creative: void,
crafting: *const main.items.Recipe,
};
super: Inventory,
type: ClientType,
Expand Down Expand Up @@ -416,6 +407,7 @@ pub const ClientInventory = struct { // MARK: ClientInventory
carried.fillFromCreative(0, dest.getItem(destSlot));
return;
}
std.debug.assert(dest.type == .serverShared);
main.sync.ClientSide.executeCommand(.{.depositOrSwap = .{.dest = .{.inv = dest.super, .slot = destSlot}, .source = .{.inv = carried.super, .slot = 0}}});
}

Expand Down Expand Up @@ -452,18 +444,18 @@ pub const ClientInventory = struct { // MARK: ClientInventory
}

pub fn depositToAny(source: ClientInventory, sourceSlot: u32, destinations: []const ClientInventory, amount: u16) void {
std.debug.assert(source.type != .creative);
std.debug.assert(source.type == .serverShared);
for (destinations) |inv| std.debug.assert(inv.super.type == .normal);
main.sync.ClientSide.executeCommand(.{.depositToAny = .init(destinations, .{.inv = source.super, .slot = sourceSlot}, amount)});
}

pub fn dropStack(source: ClientInventory, sourceSlot: u32) void {
if (source.type == .creative) return;
if (source.type != .serverShared) return;
main.sync.ClientSide.executeCommand(.{.drop = .{.source = .{.inv = source.super, .slot = sourceSlot}}});
}

pub fn dropOne(source: ClientInventory, sourceSlot: u32) void {
if (source.type == .creative) return;
if (source.type != .serverShared) return;
main.sync.ClientSide.executeCommand(.{.drop = .{.source = .{.inv = source.super, .slot = sourceSlot}, .desiredAmount = 1}});
}

Expand All @@ -475,6 +467,14 @@ pub const ClientInventory = struct { // MARK: ClientInventory
main.sync.ClientSide.executeCommand(.{.fillFromCreative = .{.dest = .{.inv = dest.super, .slot = destSlot}, .item = item, .amount = amount}});
}

pub fn craftFrom(source: ClientInventory, destinations: []const ClientInventory, craftingInv: ClientInventory) void {
std.debug.assert(source.type == .serverShared);
for (destinations) |inv| std.debug.assert(inv.type == .serverShared);
std.debug.assert(craftingInv.type == .crafting);

main.sync.ClientSide.executeCommand(.{.craftFrom = .init(destinations, &.{source}, craftingInv.type.crafting)});
}

pub fn placeBlock(self: ClientInventory, slot: u32) void {
std.debug.assert(self.type == .serverShared);
main.renderer.MeshSelection.placeBlock(self, slot);
Expand Down Expand Up @@ -506,12 +506,10 @@ const Inventory = @This(); // MARK: Inventory

pub const TypeEnum = enum(u8) {
normal = 0,
crafting = 2,
workbench = 3,
};
pub const Type = union(TypeEnum) {
normal: void,
crafting: void,
workbench: ToolTypeIndex,

pub fn shouldDepositToUserOnClose(self: Type) bool {
Expand Down Expand Up @@ -795,4 +793,38 @@ pub const Inventories = struct { // MARK: Inventories
}
return remainingAmount;
}

pub fn removeItems(self: Inventories, ctx: sync.Command.Context, itemAmount: u16, baseItem: main.items.BaseItemIndex) void {
var fullSlot: ?u32 = null;
var fullInv: ?Inventory = null;
var remainingAmount: usize = itemAmount;
for (self.inventories) |source| {
for (0..source._items.len) |reverseIndex| {
const i: usize = source._items.len - reverseIndex - 1;
const otherStack: *ItemStack = &source._items[i];
if (otherStack.item == .baseItem and baseItem == otherStack.item.baseItem) {
if (otherStack.amount == otherStack.item.stackSize()) {
if (fullSlot == null) {
fullSlot = @intCast(i);
fullInv = source;
}
continue;
}
const amount = @min(remainingAmount, otherStack.amount);
ctx.execute(.{.delete = .{
.source = .{.inv = source, .slot = @intCast(i)},
.amount = amount,
}});
remainingAmount -= amount;
if (remainingAmount == 0) return;
}
}
}
if (remainingAmount > 0 and fullSlot != null) {
ctx.execute(.{.delete = .{
.source = .{.inv = fullInv.?, .slot = fullSlot.?},
.amount = @min(remainingAmount, baseItem.stackSize()),
}});
}
}
};
12 changes: 6 additions & 6 deletions src/gui/gui.zig
Original file line number Diff line number Diff line change
Expand Up @@ -648,10 +648,10 @@ pub const inventory = struct { // MARK: inventory
isCrafting = false;
return;
};
if (itemSlot.mode == .immutable) return;
const mainGuiButton = main.KeyBoard.key("mainGuiButton");
const secondaryGuiButton = main.KeyBoard.key("secondaryGuiButton");

if (itemSlot.inventory.super.type == .crafting and itemSlot.mode == .takeOnly and mainGuiButton.pressed and (recipeItem != .null or itemSlot.pressed)) {
if (itemSlot.inventory.type == .crafting and itemSlot.mode == .takeOnly and mainGuiButton.pressed and (recipeItem != .null or itemSlot.pressed)) {
const item = itemSlot.inventory.getItem(itemSlot.itemSlot);
if (recipeItem == .null and item != .null) recipeItem = item.clone();
if (!std.meta.eql(item, recipeItem)) return;
Expand All @@ -665,9 +665,9 @@ 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));
main.game.Player.inventory.craftFrom(&.{main.game.Player.inventory}, itemSlot.inventory);
} else {
itemSlot.inventory.depositOrSwap(itemSlot.itemSlot, carried);
main.game.Player.inventory.craftFrom(&.{carried}, itemSlot.inventory);
}
}
return;
Expand Down Expand Up @@ -730,7 +730,7 @@ pub const inventory = struct { // MARK: inventory
carried.distribute(targetInventories, targetSlots);
leftClickSlots.clearRetainingCapacity();
} else if (hoveredItemSlot) |hovered| {
if (hovered.inventory.super.type == .crafting and hovered.mode == .takeOnly) return;
if (hovered.inventory.type == .crafting) return;
hovered.inventory.depositOrSwap(hovered.itemSlot, carried);
} else if (!hoveredAWindow) {
carried.dropStack(0);
Expand All @@ -739,7 +739,7 @@ pub const inventory = struct { // MARK: inventory
if (rightClickSlots.items.len != 0) {
rightClickSlots.clearRetainingCapacity();
} else if (hoveredItemSlot) |hovered| {
if (hovered.inventory.super.type == .crafting and hovered.mode == .takeOnly) return;
if (hovered.inventory.type == .crafting) return;
if (hovered.inventory.type == .creative) {
carried.deposit(0, hovered.inventory, hovered.itemSlot, 1);
} else {
Expand Down
17 changes: 14 additions & 3 deletions src/gui/windows/inventory_crafting.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ fn findAvailableRecipes(list: *VerticalList) bool {
_ = availableItems.swapRemove(i);
}
}
for (inventories.items) |inv| {
inv.deinit(main.globalAllocator);
}
inventories.clearRetainingCapacity();
// Find all recipes the player can make:
outer: for (items.recipes()) |*recipe| {
Expand All @@ -91,10 +94,15 @@ fn findAvailableRecipes(list: *VerticalList) bool {
continue :outer; // Ingredient not found.
}
// All ingredients found: Add it to the list.
if (recipe.cachedInventory == null) {
recipe.cachedInventory = ClientInventory.init(main.globalAllocator, recipe.sourceItems.len + 1, .crafting, .serverShared, .{.recipe = recipe}, .{});
const inv = ClientInventory.init(main.globalAllocator, recipe.sourceItems.len + 1, .normal, .{.crafting = recipe}, .other, .{});

for (0..recipe.sourceAmounts.len) |index| {
inv.super._items[index].amount = recipe.sourceAmounts[index];
inv.super._items[index].item = .{.baseItem = recipe.sourceItems[index]};
}
const inv = recipe.cachedInventory.?;
inv.super._items[inv.super._items.len - 1].amount = recipe.resultAmount;
inv.super._items[inv.super._items.len - 1].item = .{.baseItem = recipe.resultItem};

inventories.append(inv);
const rowList = HorizontalList.init();
const maxColumns: u32 = 4;
Expand Down Expand Up @@ -157,6 +165,9 @@ pub fn onClose() void {
}
availableItems.deinit();
itemAmount.deinit();
for (inventories.items) |inv| {
inv.deinit(main.globalAllocator);
}
inventories.deinit();
}

Expand Down
46 changes: 42 additions & 4 deletions src/items.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,48 @@ pub const Recipe = struct { // MARK: Recipe
sourceAmounts: []u16,
resultItem: BaseItemIndex,
resultAmount: u16,
cachedInventory: ?Inventory.ClientInventory = null,

fn getValidRecipe(self: Recipe) error{Invalid}!*Recipe {
outer: for (main.items.recipes()) |*recipe| {
if (recipe.resultItem != self.resultItem) continue;
if (recipe.resultAmount != self.resultAmount) continue;
if (recipe.sourceItems.len != self.sourceItems.len) continue;
for (recipe.sourceItems, recipe.sourceAmounts, self.sourceItems, self.sourceAmounts) |recipeItem, recipeAmount, selfItem, selfAmount| {
if (recipeItem != selfItem) continue :outer;
if (recipeAmount != selfAmount) continue :outer;
}
return recipe;
}
return error.Invalid;
}

pub fn toBytes(self: *const Recipe, writer: *BinaryWriter) void {
writer.writeEnum(BaseItemIndex, self.resultItem);
writer.writeVarInt(u16, self.resultAmount);
writer.writeVarInt(usize, self.sourceItems.len);
for (self.sourceItems, self.sourceAmounts) |item, amount| {
writer.writeEnum(BaseItemIndex, item);
writer.writeVarInt(u16, amount);
}
}

pub fn fromBytes(reader: *BinaryReader) !*Recipe {
const resultItem = try reader.readEnum(BaseItemIndex);
const resultAmount = try reader.readVarInt(u16);
const sourceCount = try reader.readVarInt(usize);

var sourceItems: main.List(BaseItemIndex) = .initCapacity(main.stackAllocator, @min(256, sourceCount));
defer sourceItems.deinit();
var sourceAmounts: main.List(u16) = .initCapacity(main.stackAllocator, @min(256, sourceCount));
defer sourceAmounts.deinit();

while (reader.remaining.len > 0 and sourceItems.items.len < sourceCount) {
sourceItems.append(try reader.readEnum(BaseItemIndex));
sourceAmounts.append(try reader.readVarInt(u16));
}

return getValidRecipe(.{.sourceItems = sourceItems.items, .sourceAmounts = sourceAmounts.items, .resultItem = resultItem, .resultAmount = resultAmount});
}
};

var toolTypeList: ListUnmanaged(ToolType) = .{};
Expand Down Expand Up @@ -1265,9 +1306,6 @@ pub fn clearRecipeCachedInventories() void {
for (recipeList.items) |recipe| {
main.globalAllocator.free(recipe.sourceItems);
main.globalAllocator.free(recipe.sourceAmounts);
if (recipe.cachedInventory) |inv| {
inv.deinit(main.globalAllocator);
}
}
}

Expand Down
Loading