Track durability changes in inventory operations when breaking blocks.

fixes #847
This commit is contained in:
IntegratedQuantum 2025-01-04 20:25:22 +01:00
parent 72796f9905
commit a85237ba2e
2 changed files with 72 additions and 16 deletions

View File

@ -274,6 +274,7 @@ pub const Sync = struct { // MARK: Sync
.inv = .{.inv = command.payload.open.inv, .slot = @intCast(slot)}, .inv = .{.inv = command.payload.open.inv, .slot = @intCast(slot)},
.amount = stack.amount, .amount = stack.amount,
.item = stack.item, .item = stack.item,
.durability = 0,
}; };
const syncData = syncOp.serialize(main.stackAllocator); const syncData = syncOp.serialize(main.stackAllocator);
defer main.stackAllocator.free(syncData); defer main.stackAllocator.free(syncData);
@ -453,6 +454,7 @@ pub const Command = struct { // MARK: Command
swap = 1, swap = 1,
delete = 2, delete = 2,
create = 3, create = 3,
useDurability = 4,
}; };
const InventoryAndSlot = struct { const InventoryAndSlot = struct {
@ -497,23 +499,31 @@ pub const Command = struct { // MARK: Command
item: ?Item, item: ?Item,
amount: u16, amount: u16,
}, },
useDurability: struct {
source: InventoryAndSlot,
item: main.items.Item = undefined,
durability: u31,
previousDurability: u32 = undefined,
},
}; };
const SyncOperation = struct { // MARK: SyncOperation const SyncOperation = struct { // MARK: SyncOperation
// Since the client doesn't know about all inventories, we can only use create(+amount)/delete(-amount) operations to apply the server side updates. // Since the client doesn't know about all inventories, we can only use create(+amount)/delete(-amount) and use durability operations to apply the server side updates.
inv: InventoryAndSlot, inv: InventoryAndSlot,
amount: i32, amount: i32,
item: ?Item, item: ?Item,
durability: i32,
pub fn executeFromData(data: []const u8) !void { pub fn executeFromData(data: []const u8) !void {
std.debug.assert(data.len >= 12); std.debug.assert(data.len >= 16);
var self = SyncOperation { var self = SyncOperation {
.inv = try InventoryAndSlot.read(data[0..8], .client, null), .inv = try InventoryAndSlot.read(data[0..8], .client, null),
.amount = std.mem.readInt(i32, data[8..12], .big), .amount = std.mem.readInt(i32, data[8..12], .big),
.durability = std.mem.readInt(i32, data[12..16], .big),
.item = null, .item = null,
}; };
if(data.len > 12) { if(data.len > 16) {
const zon = ZonElement.parseFromString(main.stackAllocator, data[12..]); const zon = ZonElement.parseFromString(main.stackAllocator, data[16..]);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
self.item = try Item.init(zon); self.item = try Item.init(zon);
} }
@ -527,7 +537,7 @@ pub const Command = struct { // MARK: Command
return error.Invalid; return error.Invalid;
} }
self.inv.ref().amount += @intCast(self.amount); self.inv.ref().amount += @intCast(self.amount);
} else { // Delete } else if(self.amount < 0) { // Delete
if(self.inv.ref().amount < -self.amount) { if(self.inv.ref().amount < -self.amount) {
return error.Invalid; return error.Invalid;
} }
@ -537,13 +547,22 @@ pub const Command = struct { // MARK: Command
} }
} }
if(self.durability < 0) { // useDurability
self.inv.ref().item.?.tool.durability -|= @intCast(-self.durability);
if(self.inv.ref().item.?.tool.durability == 0) {
self.inv.ref().item = null;
self.inv.ref().amount = 0;
}
}
self.inv.inv.update(); self.inv.inv.update();
} }
pub fn serialize(self: SyncOperation, allocator: NeverFailingAllocator) []const u8 { pub fn serialize(self: SyncOperation, allocator: NeverFailingAllocator) []const u8 {
var data = main.List(u8).initCapacity(allocator, 12); var data = main.List(u8).initCapacity(allocator, 16);
self.inv.write(data.addMany(8)[0..8]); self.inv.write(data.addMany(8)[0..8]);
std.mem.writeInt(i32, data.addMany(4)[0..4], self.amount, .big); std.mem.writeInt(i32, data.addMany(4)[0..4], self.amount, .big);
std.mem.writeInt(i32, data.addMany(4)[0..4], self.durability, .big);
if(self.item) |item| { if(self.item) |item| {
const zon = ZonElement.initObject(main.stackAllocator); const zon = ZonElement.initObject(main.stackAllocator);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
@ -617,6 +636,12 @@ pub const Command = struct { // MARK: Command
} }
info.dest.inv.update(); info.dest.inv.update();
}, },
.useDurability => |info| {
std.debug.assert(info.source.ref().item == null or std.meta.eql(info.source.ref().item, info.item));
info.source.ref().item = info.item;
info.item.tool.durability = info.previousDurability;
info.source.inv.update();
}
} }
} }
} }
@ -628,6 +653,11 @@ pub const Command = struct { // MARK: Command
.delete => |info| { .delete => |info| {
info.item.?.deinit(); info.item.?.deinit();
}, },
.useDurability => |info| {
if(info.previousDurability <= info.durability) {
info.item.deinit();
}
}
} }
} }
self.baseOperations.deinit(allocator); self.baseOperations.deinit(allocator);
@ -665,6 +695,7 @@ pub const Command = struct { // MARK: Command
.inv = inv, .inv = inv,
.amount = amount, .amount = amount,
.item = if(inv.ref().amount == 0) item else null, .item = if(inv.ref().amount == 0) item else null,
.durability = 0,
}); });
} }
std.debug.assert(inv.ref().item == null or std.meta.eql(inv.ref().item.?, item.?)); std.debug.assert(inv.ref().item == null or std.meta.eql(inv.ref().item.?, item.?));
@ -680,6 +711,7 @@ pub const Command = struct { // MARK: Command
.inv = inv, .inv = inv,
.amount = -@as(i32, amount), .amount = -@as(i32, amount),
.item = null, .item = null,
.durability = 0,
}); });
} }
inv.ref().amount -= amount; inv.ref().amount -= amount;
@ -688,6 +720,23 @@ pub const Command = struct { // MARK: Command
} }
} }
fn executeDurabilityUseOperation(self: *Command, allocator: NeverFailingAllocator, side: Side, inv: InventoryAndSlot, durability: u31) void {
if(durability == 0) return;
if(side == .server) {
self.syncOperations.append(allocator, .{
.inv = inv,
.amount = 0,
.item = null,
.durability = -@as(i32, durability),
});
}
inv.ref().item.?.tool.durability -|= durability;
if(inv.ref().item.?.tool.durability == 0) {
inv.ref().item = null;
inv.ref().amount = 0;
}
}
fn executeBaseOperation(self: *Command, allocator: NeverFailingAllocator, _op: BaseOperation, side: Side) void { // MARK: executeBaseOperation() fn executeBaseOperation(self: *Command, allocator: NeverFailingAllocator, _op: BaseOperation, side: Side) void { // MARK: executeBaseOperation()
var op = _op; var op = _op;
switch(op) { switch(op) {
@ -716,6 +765,12 @@ pub const Command = struct { // MARK: Command
self.executeAddOperation(allocator, side, info.dest, info.amount, info.item); self.executeAddOperation(allocator, side, info.dest, info.amount, info.item);
info.dest.inv.update(); info.dest.inv.update();
}, },
.useDurability => |*info| {
info.item = info.source.ref().item.?;
info.previousDurability = info.item.tool.durability;
self.executeDurabilityUseOperation(allocator, side, info.source, info.durability);
info.source.inv.update();
},
} }
self.baseOperations.append(allocator, op); self.baseOperations.append(allocator, op);
} }
@ -1295,8 +1350,10 @@ pub const Command = struct { // MARK: Command
.no => unreachable, .no => unreachable,
.yes => {}, .yes => {},
.yes_costsDurability => |durability| { .yes_costsDurability => |durability| {
// TODO: Add operations to track tool durability. cmd.executeBaseOperation(allocator, .{.useDurability = .{
_ = durability; .source = self.source,
.durability = durability,
}}, side);
}, },
.yes_costsItems => |amount| { .yes_costsItems => |amount| {
cmd.executeBaseOperation(allocator, .{.delete = .{ cmd.executeBaseOperation(allocator, .{.delete = .{

View File

@ -865,7 +865,7 @@ const ToolPhysics = struct { // MARK: ToolPhysics
pub const Tool = struct { // MARK: Tool pub const Tool = struct { // MARK: Tool
craftingGrid: [25]?*const BaseItem, craftingGrid: [25]?*const BaseItem,
materialGrid: [16][16]?*const BaseItem, materialGrid: [16][16]?*const BaseItem,
tooltip: ?[]const u8, tooltip: main.List(u8),
image: graphics.Image, image: graphics.Image,
texture: ?graphics.Texture, texture: ?graphics.Texture,
seed: u32, seed: u32,
@ -901,7 +901,7 @@ pub const Tool = struct { // MARK: Tool
const self = main.globalAllocator.create(Tool); const self = main.globalAllocator.create(Tool);
self.image = graphics.Image.init(main.globalAllocator, 16, 16); self.image = graphics.Image.init(main.globalAllocator, 16, 16);
self.texture = null; self.texture = null;
self.tooltip = null; self.tooltip = .init(main.globalAllocator);
return self; return self;
} }
@ -910,7 +910,7 @@ pub const Tool = struct { // MARK: Tool
texture.deinit(); texture.deinit();
} }
self.image.deinit(main.globalAllocator); self.image.deinit(main.globalAllocator);
if(self.tooltip) |tooltip| main.globalAllocator.free(tooltip); self.tooltip.deinit();
main.globalAllocator.destroy(self); main.globalAllocator.destroy(self);
} }
@ -919,7 +919,7 @@ pub const Tool = struct { // MARK: Tool
result.* = .{ result.* = .{
.craftingGrid = self.craftingGrid, .craftingGrid = self.craftingGrid,
.materialGrid = self.materialGrid, .materialGrid = self.materialGrid,
.tooltip = null, .tooltip = .init(main.globalAllocator),
.image = graphics.Image.init(main.globalAllocator, self.image.width, self.image.height), .image = graphics.Image.init(main.globalAllocator, self.image.width, self.image.height),
.texture = null, .texture = null,
.seed = self.seed, .seed = self.seed,
@ -1001,9 +1001,8 @@ pub const Tool = struct { // MARK: Tool
} }
fn getTooltip(self: *Tool) []const u8 { fn getTooltip(self: *Tool) []const u8 {
if(self.tooltip) |tooltip| return tooltip; self.tooltip.clearRetainingCapacity();
self.tooltip = std.fmt.allocPrint( self.tooltip.writer().print(
main.globalAllocator.allocator,
\\Time to swing: {d:.2} s \\Time to swing: {d:.2} s
\\Pickaxe power: {} % \\Pickaxe power: {} %
\\Axe power: {} % \\Axe power: {} %
@ -1018,7 +1017,7 @@ pub const Tool = struct { // MARK: Tool
self.durability, self.maxDurability, self.durability, self.maxDurability,
} }
) catch unreachable; ) catch unreachable;
return self.tooltip.?; return self.tooltip.items;
} }
pub fn getPowerByBlockClass(self: *Tool, blockClass: blocks.BlockClass) f32 { pub fn getPowerByBlockClass(self: *Tool, blockClass: blocks.BlockClass) f32 {