mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Refactor how recipes are handled:
- They now have a fixed source type - The server is responsible for filling the recipe items, disabling use of fillFromCreative for recipes - The server now checks if the recipe is known (→ recipes cannot be exploited to get unobtainable items) - The inventory is now cached on the client, instead of being recreated every time the recipes update (→less traffic)
This commit is contained in:
parent
2b3f38827e
commit
c9bac6377e
@ -302,7 +302,7 @@ pub const Sync = struct { // MARK: Sync
|
||||
fn createInventory(user: *main.server.User, clientId: u32, len: usize, typ: Inventory.Type, source: Source) void {
|
||||
main.utils.assertLocked(&mutex);
|
||||
switch(source) {
|
||||
.sharedTestingInventory => {
|
||||
.sharedTestingInventory, .recipe => {
|
||||
for(inventories.items) |*inv| {
|
||||
if(std.meta.eql(inv.source, source)) {
|
||||
inv.addUser(user, clientId);
|
||||
@ -332,6 +332,14 @@ pub const Sync = struct { // MARK: Sync
|
||||
|
||||
inventory.inv.loadFromZon(inventoryZon);
|
||||
},
|
||||
.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,
|
||||
}
|
||||
@ -1063,10 +1071,17 @@ pub const Command = struct { // MARK: Command
|
||||
.playerInventory, .hand => |val| {
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big);
|
||||
},
|
||||
else => {}
|
||||
}
|
||||
switch(self.source) {
|
||||
.playerInventory, .sharedTestingInventory, .hand, .other => {},
|
||||
.recipe => |val| {
|
||||
std.mem.writeInt(u16, data.addMany(2)[0..2], val.resultAmount, .big);
|
||||
data.appendSlice(val.resultItem.id);
|
||||
data.append(0);
|
||||
for(0..val.sourceItems.len) |i| {
|
||||
std.mem.writeInt(u16, data.addMany(2)[0..2], val.sourceAmounts[i], .big);
|
||||
data.appendSlice(val.sourceItems[i].id);
|
||||
data.append(0);
|
||||
}
|
||||
},
|
||||
.sharedTestingInventory, .other => {},
|
||||
.alreadyFreed => unreachable,
|
||||
}
|
||||
}
|
||||
@ -1082,6 +1097,32 @@ pub const Command = struct { // MARK: Command
|
||||
.playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)},
|
||||
.sharedTestingInventory => .{.sharedTestingInventory = {}},
|
||||
.hand => .{.hand = std.mem.readInt(u32, data[14..18], .big)},
|
||||
.recipe => .{.recipe = blk: {
|
||||
var itemList = main.List(struct{amount: u16, item: *const main.items.BaseItem}).initCapacity(main.stackAllocator, len);
|
||||
defer itemList.deinit();
|
||||
var index: usize = 14;
|
||||
while(index + 2 < data.len) {
|
||||
const resultAmount = std.mem.readInt(u16, data[index..][0..2], .big);
|
||||
index += 2;
|
||||
const resultItem = if(std.mem.indexOfScalarPos(u8, data, index, 0)) |endIndex| blk2: {
|
||||
const itemId = data[index..endIndex];
|
||||
index = endIndex + 1;
|
||||
break :blk2 main.items.getByID(itemId) orelse return error.Invalid;
|
||||
} else return error.Invalid;
|
||||
itemList.append(.{.amount = resultAmount, .item = resultItem});
|
||||
}
|
||||
if(itemList.items.len != len) return error.Invalid;
|
||||
// Find the recipe in our list:
|
||||
outer: for(main.items.recipes()) |*recipe| {
|
||||
if(recipe.resultAmount == itemList.items[0].amount and recipe.resultItem == itemList.items[0].item and recipe.sourceItems.len == itemList.items.len - 1) {
|
||||
for(itemList.items[1..], 0..) |item, i| {
|
||||
if(item.amount != recipe.sourceAmounts[i] or item.item != recipe.sourceItems[i]) continue :outer;
|
||||
}
|
||||
break :blk recipe;
|
||||
}
|
||||
}
|
||||
return error.Invalid;
|
||||
}},
|
||||
.other => .{.other = {}},
|
||||
.alreadyFreed => unreachable,
|
||||
};
|
||||
@ -1655,6 +1696,7 @@ const SourceType = enum(u8) {
|
||||
playerInventory = 1,
|
||||
sharedTestingInventory = 2,
|
||||
hand = 3,
|
||||
recipe = 4,
|
||||
other = 0xff, // TODO: List every type separately here.
|
||||
};
|
||||
const Source = union(SourceType) {
|
||||
@ -1662,6 +1704,7 @@ const Source = union(SourceType) {
|
||||
playerInventory: u32,
|
||||
sharedTestingInventory: void,
|
||||
hand: u32,
|
||||
recipe: *const main.items.Recipe,
|
||||
other: void,
|
||||
};
|
||||
|
||||
|
@ -80,9 +80,6 @@ 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| {
|
||||
@ -95,7 +92,10 @@ fn findAvailableRecipes(list: *VerticalList) bool {
|
||||
continue :outer; // Ingredient not found.
|
||||
}
|
||||
// All ingredients found: Add it to the list.
|
||||
const inv = Inventory.init(main.globalAllocator, recipe.sourceItems.len + 1, .crafting, .other);
|
||||
if(recipe.cachedInventory == null) {
|
||||
recipe.cachedInventory = Inventory.init(main.globalAllocator, recipe.sourceItems.len + 1, .crafting, .{.recipe = recipe});
|
||||
}
|
||||
const inv = recipe.cachedInventory.?;
|
||||
inventories.append(inv);
|
||||
const rowList = HorizontalList.init();
|
||||
const maxColumns: u32 = 4;
|
||||
@ -107,14 +107,12 @@ fn findAvailableRecipes(list: *VerticalList) bool {
|
||||
if(col < remainder) itemsThisColumn += 1;
|
||||
const columnList = VerticalList.init(.{0, 0}, std.math.inf(f32), 0);
|
||||
for(0..itemsThisColumn) |_| {
|
||||
inv.fillAmountFromCreative(i, .{.baseItem = recipe.sourceItems[i]}, recipe.sourceAmounts[i]);
|
||||
columnList.add(ItemSlot.init(.{0, 0}, inv, i, .immutable, .immutable));
|
||||
i += 1;
|
||||
}
|
||||
columnList.finish(.center);
|
||||
rowList.add(columnList);
|
||||
}
|
||||
inv.fillAmountFromCreative(@intCast(recipe.sourceItems.len), recipe.resultItem.item, recipe.resultItem.amount);
|
||||
rowList.add(Icon.init(.{8, 0}, .{32, 32}, arrowTexture, false));
|
||||
const itemSlot = ItemSlot.init(.{8, 0}, inv, @intCast(recipe.sourceItems.len), .craftingResult, .takeOnly);
|
||||
rowList.add(itemSlot);
|
||||
@ -156,9 +154,6 @@ pub fn onClose() void {
|
||||
}
|
||||
availableItems.deinit();
|
||||
itemAmount.deinit();
|
||||
for(inventories.items) |inv| {
|
||||
inv.deinit(main.globalAllocator);
|
||||
}
|
||||
inventories.deinit();
|
||||
}
|
||||
|
||||
|
@ -1184,10 +1184,12 @@ pub const ItemStack = struct { // MARK: ItemStack
|
||||
}
|
||||
};
|
||||
|
||||
const Recipe = struct { // MARK: Recipe
|
||||
pub const Recipe = struct { // MARK: Recipe
|
||||
sourceItems: []*BaseItem,
|
||||
sourceAmounts: []u16,
|
||||
resultItem: ItemStack,
|
||||
resultItem: *BaseItem,
|
||||
resultAmount: u16,
|
||||
cachedInventory: ?Inventory = null,
|
||||
};
|
||||
|
||||
var arena: main.utils.NeverFailingArenaAllocator = undefined;
|
||||
@ -1244,7 +1246,8 @@ fn parseRecipe(zon: ZonElement) !Recipe {
|
||||
const recipe = Recipe {
|
||||
.sourceItems = arena.allocator().alloc(*BaseItem, inputs.len),
|
||||
.sourceAmounts = arena.allocator().alloc(u16, inputs.len),
|
||||
.resultItem = output,
|
||||
.resultItem = output.item.?.baseItem,
|
||||
.resultAmount = output.amount,
|
||||
};
|
||||
errdefer {
|
||||
arena.allocator().free(recipe.sourceAmounts);
|
||||
@ -1267,6 +1270,11 @@ pub fn registerRecipes(zon: ZonElement) void {
|
||||
|
||||
pub fn reset() void {
|
||||
reverseIndices.clearAndFree();
|
||||
for(recipeList.items) |recipe| {
|
||||
if(recipe.cachedInventory) |inv| {
|
||||
inv.deinit(main.globalAllocator);
|
||||
}
|
||||
}
|
||||
recipeList.clearAndFree();
|
||||
itemListSize = 0;
|
||||
_ = arena.reset(.free_all);
|
||||
@ -1274,6 +1282,11 @@ pub fn reset() void {
|
||||
|
||||
pub fn deinit() void {
|
||||
reverseIndices.clearAndFree();
|
||||
for(recipeList.items) |recipe| {
|
||||
if(recipe.cachedInventory) |inv| {
|
||||
inv.deinit(main.globalAllocator);
|
||||
}
|
||||
}
|
||||
recipeList.clearAndFree();
|
||||
arena.deinit();
|
||||
Inventory.Sync.ClientSide.deinit();
|
||||
|
Loading…
x
Reference in New Issue
Block a user