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 {
|
fn createInventory(user: *main.server.User, clientId: u32, len: usize, typ: Inventory.Type, source: Source) void {
|
||||||
main.utils.assertLocked(&mutex);
|
main.utils.assertLocked(&mutex);
|
||||||
switch(source) {
|
switch(source) {
|
||||||
.sharedTestingInventory => {
|
.sharedTestingInventory, .recipe => {
|
||||||
for(inventories.items) |*inv| {
|
for(inventories.items) |*inv| {
|
||||||
if(std.meta.eql(inv.source, source)) {
|
if(std.meta.eql(inv.source, source)) {
|
||||||
inv.addUser(user, clientId);
|
inv.addUser(user, clientId);
|
||||||
@ -332,6 +332,14 @@ pub const Sync = struct { // MARK: Sync
|
|||||||
|
|
||||||
inventory.inv.loadFromZon(inventoryZon);
|
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 => {},
|
.other => {},
|
||||||
.alreadyFreed => unreachable,
|
.alreadyFreed => unreachable,
|
||||||
}
|
}
|
||||||
@ -1063,10 +1071,17 @@ pub const Command = struct { // MARK: Command
|
|||||||
.playerInventory, .hand => |val| {
|
.playerInventory, .hand => |val| {
|
||||||
std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big);
|
std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big);
|
||||||
},
|
},
|
||||||
else => {}
|
.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);
|
||||||
}
|
}
|
||||||
switch(self.source) {
|
},
|
||||||
.playerInventory, .sharedTestingInventory, .hand, .other => {},
|
.sharedTestingInventory, .other => {},
|
||||||
.alreadyFreed => unreachable,
|
.alreadyFreed => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1082,6 +1097,32 @@ pub const Command = struct { // MARK: Command
|
|||||||
.playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)},
|
.playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)},
|
||||||
.sharedTestingInventory => .{.sharedTestingInventory = {}},
|
.sharedTestingInventory => .{.sharedTestingInventory = {}},
|
||||||
.hand => .{.hand = std.mem.readInt(u32, data[14..18], .big)},
|
.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 = {}},
|
.other => .{.other = {}},
|
||||||
.alreadyFreed => unreachable,
|
.alreadyFreed => unreachable,
|
||||||
};
|
};
|
||||||
@ -1655,6 +1696,7 @@ const SourceType = enum(u8) {
|
|||||||
playerInventory = 1,
|
playerInventory = 1,
|
||||||
sharedTestingInventory = 2,
|
sharedTestingInventory = 2,
|
||||||
hand = 3,
|
hand = 3,
|
||||||
|
recipe = 4,
|
||||||
other = 0xff, // TODO: List every type separately here.
|
other = 0xff, // TODO: List every type separately here.
|
||||||
};
|
};
|
||||||
const Source = union(SourceType) {
|
const Source = union(SourceType) {
|
||||||
@ -1662,6 +1704,7 @@ const Source = union(SourceType) {
|
|||||||
playerInventory: u32,
|
playerInventory: u32,
|
||||||
sharedTestingInventory: void,
|
sharedTestingInventory: void,
|
||||||
hand: u32,
|
hand: u32,
|
||||||
|
recipe: *const main.items.Recipe,
|
||||||
other: void,
|
other: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,9 +80,6 @@ fn findAvailableRecipes(list: *VerticalList) bool {
|
|||||||
_ = availableItems.swapRemove(i);
|
_ = availableItems.swapRemove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(inventories.items) |inv| {
|
|
||||||
inv.deinit(main.globalAllocator);
|
|
||||||
}
|
|
||||||
inventories.clearRetainingCapacity();
|
inventories.clearRetainingCapacity();
|
||||||
// Find all recipes the player can make:
|
// Find all recipes the player can make:
|
||||||
outer: for(items.recipes()) |*recipe| {
|
outer: for(items.recipes()) |*recipe| {
|
||||||
@ -95,7 +92,10 @@ fn findAvailableRecipes(list: *VerticalList) bool {
|
|||||||
continue :outer; // Ingredient not found.
|
continue :outer; // Ingredient not found.
|
||||||
}
|
}
|
||||||
// All ingredients found: Add it to the list.
|
// 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);
|
inventories.append(inv);
|
||||||
const rowList = HorizontalList.init();
|
const rowList = HorizontalList.init();
|
||||||
const maxColumns: u32 = 4;
|
const maxColumns: u32 = 4;
|
||||||
@ -107,14 +107,12 @@ fn findAvailableRecipes(list: *VerticalList) bool {
|
|||||||
if(col < remainder) itemsThisColumn += 1;
|
if(col < remainder) itemsThisColumn += 1;
|
||||||
const columnList = VerticalList.init(.{0, 0}, std.math.inf(f32), 0);
|
const columnList = VerticalList.init(.{0, 0}, std.math.inf(f32), 0);
|
||||||
for(0..itemsThisColumn) |_| {
|
for(0..itemsThisColumn) |_| {
|
||||||
inv.fillAmountFromCreative(i, .{.baseItem = recipe.sourceItems[i]}, recipe.sourceAmounts[i]);
|
|
||||||
columnList.add(ItemSlot.init(.{0, 0}, inv, i, .immutable, .immutable));
|
columnList.add(ItemSlot.init(.{0, 0}, inv, i, .immutable, .immutable));
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
columnList.finish(.center);
|
columnList.finish(.center);
|
||||||
rowList.add(columnList);
|
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));
|
rowList.add(Icon.init(.{8, 0}, .{32, 32}, arrowTexture, false));
|
||||||
const itemSlot = ItemSlot.init(.{8, 0}, inv, @intCast(recipe.sourceItems.len), .craftingResult, .takeOnly);
|
const itemSlot = ItemSlot.init(.{8, 0}, inv, @intCast(recipe.sourceItems.len), .craftingResult, .takeOnly);
|
||||||
rowList.add(itemSlot);
|
rowList.add(itemSlot);
|
||||||
@ -156,9 +154,6 @@ pub fn onClose() void {
|
|||||||
}
|
}
|
||||||
availableItems.deinit();
|
availableItems.deinit();
|
||||||
itemAmount.deinit();
|
itemAmount.deinit();
|
||||||
for(inventories.items) |inv| {
|
|
||||||
inv.deinit(main.globalAllocator);
|
|
||||||
}
|
|
||||||
inventories.deinit();
|
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,
|
sourceItems: []*BaseItem,
|
||||||
sourceAmounts: []u16,
|
sourceAmounts: []u16,
|
||||||
resultItem: ItemStack,
|
resultItem: *BaseItem,
|
||||||
|
resultAmount: u16,
|
||||||
|
cachedInventory: ?Inventory = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var arena: main.utils.NeverFailingArenaAllocator = undefined;
|
var arena: main.utils.NeverFailingArenaAllocator = undefined;
|
||||||
@ -1244,7 +1246,8 @@ fn parseRecipe(zon: ZonElement) !Recipe {
|
|||||||
const recipe = Recipe {
|
const recipe = Recipe {
|
||||||
.sourceItems = arena.allocator().alloc(*BaseItem, inputs.len),
|
.sourceItems = arena.allocator().alloc(*BaseItem, inputs.len),
|
||||||
.sourceAmounts = arena.allocator().alloc(u16, inputs.len),
|
.sourceAmounts = arena.allocator().alloc(u16, inputs.len),
|
||||||
.resultItem = output,
|
.resultItem = output.item.?.baseItem,
|
||||||
|
.resultAmount = output.amount,
|
||||||
};
|
};
|
||||||
errdefer {
|
errdefer {
|
||||||
arena.allocator().free(recipe.sourceAmounts);
|
arena.allocator().free(recipe.sourceAmounts);
|
||||||
@ -1267,6 +1270,11 @@ pub fn registerRecipes(zon: ZonElement) void {
|
|||||||
|
|
||||||
pub fn reset() void {
|
pub fn reset() void {
|
||||||
reverseIndices.clearAndFree();
|
reverseIndices.clearAndFree();
|
||||||
|
for(recipeList.items) |recipe| {
|
||||||
|
if(recipe.cachedInventory) |inv| {
|
||||||
|
inv.deinit(main.globalAllocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
recipeList.clearAndFree();
|
recipeList.clearAndFree();
|
||||||
itemListSize = 0;
|
itemListSize = 0;
|
||||||
_ = arena.reset(.free_all);
|
_ = arena.reset(.free_all);
|
||||||
@ -1274,6 +1282,11 @@ pub fn reset() void {
|
|||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
reverseIndices.clearAndFree();
|
reverseIndices.clearAndFree();
|
||||||
|
for(recipeList.items) |recipe| {
|
||||||
|
if(recipe.cachedInventory) |inv| {
|
||||||
|
inv.deinit(main.globalAllocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
recipeList.clearAndFree();
|
recipeList.clearAndFree();
|
||||||
arena.deinit();
|
arena.deinit();
|
||||||
Inventory.Sync.ClientSide.deinit();
|
Inventory.Sync.ClientSide.deinit();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user