mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Synchronize block breaking and placing throug the inventory synchronization system.
fixes #751 This allowed reenabling item consumption when placing blocks. Durability and item drops are still work in progress. Currently all game modes consume items. Temporarily reverts some changes from #739, until the game mode is synchronized to the server as well. progress towards #670
This commit is contained in:
parent
363edfb22d
commit
0c0ca2ed15
@ -2,12 +2,14 @@ const std = @import("std");
|
||||
|
||||
const main = @import("main.zig");
|
||||
const BaseItem = main.items.BaseItem;
|
||||
const Block = main.blocks.Block;
|
||||
const Item = main.items.Item;
|
||||
const ItemStack = main.items.ItemStack;
|
||||
const Tool = main.items.Tool;
|
||||
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
|
||||
const vec = main.vec;
|
||||
const Vec3f = vec.Vec3f;
|
||||
const Vec3i = vec.Vec3i;
|
||||
const ZonElement = main.ZonElement;
|
||||
|
||||
|
||||
@ -48,7 +50,7 @@ pub const Sync = struct { // MARK: Sync
|
||||
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
cmd.do(main.globalAllocator, .client, null);
|
||||
cmd.do(main.globalAllocator, .client, null) catch unreachable;
|
||||
const data = cmd.serializePayload(main.stackAllocator);
|
||||
defer main.stackAllocator.free(data);
|
||||
main.network.Protocols.inventory.sendCommand(main.game.world.?.conn, cmd.payload, data);
|
||||
@ -101,6 +103,27 @@ pub const Sync = struct { // MARK: Sync
|
||||
commands.dequeue().?.finalize(main.globalAllocator, .client, data);
|
||||
}
|
||||
|
||||
pub fn receiveFailure() void {
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
var tempData = main.List(Command).init(main.stackAllocator);
|
||||
defer tempData.deinit();
|
||||
while(commands.dequeue_front()) |_cmd| {
|
||||
var cmd = _cmd;
|
||||
cmd.undo();
|
||||
tempData.append(cmd);
|
||||
}
|
||||
if(tempData.popOrNull()) |_cmd| {
|
||||
var cmd = _cmd;
|
||||
cmd.finalize(main.globalAllocator, .client, &.{});
|
||||
}
|
||||
while(tempData.popOrNull()) |_cmd| {
|
||||
var cmd = _cmd;
|
||||
cmd.do(main.globalAllocator, .client, null) catch unreachable;
|
||||
commands.enqueue(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receiveSyncOperation(data: []const u8) !void {
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
@ -114,13 +137,13 @@ pub const Sync = struct { // MARK: Sync
|
||||
try Command.SyncOperation.executeFromData(data);
|
||||
while(tempData.popOrNull()) |_cmd| {
|
||||
var cmd = _cmd;
|
||||
cmd.do(main.globalAllocator, .client, null);
|
||||
cmd.do(main.globalAllocator, .client, null) catch unreachable;
|
||||
commands.enqueue(cmd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const ServerSide = struct {
|
||||
pub const ServerSide = struct { // MARK: ServerSide
|
||||
const ServerInventory = struct {
|
||||
inv: Inventory,
|
||||
users: main.ListUnmanaged(*main.server.User),
|
||||
@ -206,7 +229,10 @@ pub const Sync = struct { // MARK: Sync
|
||||
var command = Command {
|
||||
.payload = payload,
|
||||
};
|
||||
command.do(main.globalAllocator, .server, source);
|
||||
command.do(main.globalAllocator, .server, source) catch {
|
||||
main.network.Protocols.inventory.sendFailure(source.?.conn);
|
||||
return;
|
||||
};
|
||||
if(source != null) {
|
||||
const confirmationData = command.confirmationData(main.stackAllocator);
|
||||
defer main.stackAllocator.free(confirmationData);
|
||||
@ -338,6 +364,7 @@ pub const Command = struct { // MARK: Command
|
||||
fillFromCreative = 6,
|
||||
depositOrDrop = 7,
|
||||
clear = 8,
|
||||
updateBlock = 9,
|
||||
};
|
||||
pub const Payload = union(PayloadType) {
|
||||
open: Open,
|
||||
@ -349,6 +376,7 @@ pub const Command = struct { // MARK: Command
|
||||
fillFromCreative: FillFromCreative,
|
||||
depositOrDrop: DepositOrDrop,
|
||||
clear: Clear,
|
||||
updateBlock: UpdateBlock,
|
||||
};
|
||||
|
||||
const BaseOperationType = enum(u8) {
|
||||
@ -473,11 +501,11 @@ pub const Command = struct { // MARK: Command
|
||||
return list.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn do(self: *Command, allocator: NeverFailingAllocator, side: Side, user: ?*main.server.User) void {
|
||||
fn do(self: *Command, allocator: NeverFailingAllocator, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
std.debug.assert(self.baseOperations.items.len == 0); // do called twice without cleaning up
|
||||
switch(self.payload) {
|
||||
inline else => |payload| {
|
||||
payload.run(allocator, self, side, user);
|
||||
try payload.run(allocator, self, side, user);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -709,7 +737,7 @@ pub const Command = struct { // MARK: Command
|
||||
inv: Inventory,
|
||||
source: Source,
|
||||
|
||||
fn run(_: Open, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User) void {}
|
||||
fn run(_: Open, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User) error{serverFailure}!void {}
|
||||
|
||||
fn finalize(self: Open, side: Side, data: []const u8) void {
|
||||
if(side != .client) return;
|
||||
@ -761,7 +789,7 @@ pub const Command = struct { // MARK: Command
|
||||
inv: Inventory,
|
||||
allocator: NeverFailingAllocator,
|
||||
|
||||
fn run(_: Close, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User) void {}
|
||||
fn run(_: Close, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User) error{serverFailure}!void {}
|
||||
|
||||
fn finalize(self: Close, side: Side, data: []const u8) void {
|
||||
if(side != .client) return;
|
||||
@ -789,10 +817,10 @@ pub const Command = struct { // MARK: Command
|
||||
dest: InventoryAndSlot,
|
||||
source: InventoryAndSlot,
|
||||
|
||||
fn run(self: DepositOrSwap, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) void {
|
||||
fn run(self: DepositOrSwap, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
std.debug.assert(self.source.inv.type == .normal);
|
||||
if(self.dest.inv.type == .creative) {
|
||||
FillFromCreative.run(.{.dest = self.source, .item = self.dest.ref().item}, allocator, cmd, side, user);
|
||||
try FillFromCreative.run(.{.dest = self.source, .item = self.dest.ref().item}, allocator, cmd, side, user);
|
||||
return;
|
||||
}
|
||||
if(self.dest.inv.type == .crafting) {
|
||||
@ -852,7 +880,7 @@ pub const Command = struct { // MARK: Command
|
||||
source: InventoryAndSlot,
|
||||
amount: u16,
|
||||
|
||||
fn run(self: Deposit, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) void {
|
||||
fn run(self: Deposit, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) error{serverFailure}!void {
|
||||
std.debug.assert(self.source.inv.type == .normal);
|
||||
if(self.dest.inv.type == .creative) return;
|
||||
if(self.dest.inv.type == .crafting) return;
|
||||
@ -898,12 +926,12 @@ pub const Command = struct { // MARK: Command
|
||||
dest: InventoryAndSlot,
|
||||
source: InventoryAndSlot,
|
||||
|
||||
fn run(self: TakeHalf, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) void {
|
||||
fn run(self: TakeHalf, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
std.debug.assert(self.dest.inv.type == .normal);
|
||||
if(self.source.inv.type == .creative) {
|
||||
if(self.dest.ref().item == null) {
|
||||
const item = self.source.ref().item;
|
||||
FillFromCreative.run(.{.dest = self.dest, .item = item}, allocator, cmd, side, user);
|
||||
try FillFromCreative.run(.{.dest = self.dest, .item = item}, allocator, cmd, side, user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -961,7 +989,7 @@ pub const Command = struct { // MARK: Command
|
||||
source: InventoryAndSlot,
|
||||
desiredAmount: u16 = 0xffff,
|
||||
|
||||
fn run(self: Drop, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) void {
|
||||
fn run(self: Drop, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
if(self.source.inv.type == .creative) return;
|
||||
if(self.source.ref().item == null) return;
|
||||
if(self.source.inv.type == .crafting) {
|
||||
@ -1017,7 +1045,7 @@ pub const Command = struct { // MARK: Command
|
||||
item: ?Item,
|
||||
amount: u16 = 0,
|
||||
|
||||
fn run(self: FillFromCreative, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) void {
|
||||
fn run(self: FillFromCreative, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) error{serverFailure}!void {
|
||||
if(self.dest.inv.type == .workbench and self.dest.slot == 25) return;
|
||||
|
||||
if(!self.dest.ref().empty()) {
|
||||
@ -1069,7 +1097,7 @@ pub const Command = struct { // MARK: Command
|
||||
dest: Inventory,
|
||||
source: Inventory,
|
||||
|
||||
pub fn run(self: DepositOrDrop, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) void {
|
||||
pub fn run(self: DepositOrDrop, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
std.debug.assert(self.dest.type == .normal);
|
||||
if(self.source.type == .creative) return;
|
||||
if(self.source.type == .crafting) return;
|
||||
@ -1129,7 +1157,7 @@ pub const Command = struct { // MARK: Command
|
||||
const Clear = struct {
|
||||
inv: Inventory,
|
||||
|
||||
pub fn run(self: Clear, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) void {
|
||||
pub fn run(self: Clear, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User) error{serverFailure}!void {
|
||||
if(self.inv.type == .creative) return;
|
||||
if(self.inv.type == .crafting) return;
|
||||
var items = self.inv._items;
|
||||
@ -1156,6 +1184,89 @@ pub const Command = struct { // MARK: Command
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const UpdateBlock = struct {
|
||||
source: InventoryAndSlot,
|
||||
pos: Vec3i,
|
||||
oldBlock: Block,
|
||||
newBlock: Block,
|
||||
|
||||
fn run(self: UpdateBlock, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User) error{serverFailure}!void {
|
||||
if(self.source.inv.type != .normal) return;
|
||||
|
||||
const stack = self.source.ref();
|
||||
|
||||
const costOfChange = self.oldBlock.canBeChangedInto(self.newBlock, stack.*);
|
||||
|
||||
// Check if we can change it:
|
||||
if(!switch(costOfChange) {
|
||||
.no => false,
|
||||
.yes => true,
|
||||
.yes_costsDurability => |durability| stack.item != null and stack.item.? == .tool and stack.item.?.tool.durability >= durability,
|
||||
.yes_costsItems => |amount| stack.amount >= amount,
|
||||
.yes_dropsItems => true,
|
||||
}) {
|
||||
if(side == .server) {
|
||||
// Inform the client of the actual block:
|
||||
main.network.Protocols.blockUpdate.send(user.?.conn, self.pos[0], self.pos[1], self.pos[2], main.server.world.?.getBlock(self.pos[0], self.pos[1], self.pos[2]) orelse return);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(side == .server) {
|
||||
if(main.server.world.?.cmpxchgBlock(self.pos[0], self.pos[1], self.pos[2], self.oldBlock, self.newBlock)) |actualBlock| {
|
||||
// Inform the client of the actual block:
|
||||
main.network.Protocols.blockUpdate.send(user.?.conn, self.pos[0], self.pos[1], self.pos[2], actualBlock);
|
||||
return error.serverFailure;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply inventory changes:
|
||||
switch(costOfChange) {
|
||||
.no => unreachable,
|
||||
.yes => {},
|
||||
.yes_costsDurability => |durability| {
|
||||
// TODO: Add operations to track tool durability.
|
||||
_ = durability;
|
||||
},
|
||||
.yes_costsItems => |amount| {
|
||||
cmd.executeBaseOperation(allocator, .{.delete = .{
|
||||
.source = self.source,
|
||||
.amount = amount,
|
||||
}}, side);
|
||||
},
|
||||
.yes_dropsItems => |amount| {
|
||||
if(side == .server) {
|
||||
// TODO: Drop block items
|
||||
_ = amount;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(self: UpdateBlock, data: *main.List(u8)) void {
|
||||
self.source.write(data.addMany(8)[0..8]);
|
||||
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[0], .big);
|
||||
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[1], .big);
|
||||
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[2], .big);
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.oldBlock)), .big);
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.newBlock)), .big);
|
||||
}
|
||||
|
||||
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !UpdateBlock {
|
||||
if(data.len != 28) return error.Invalid;
|
||||
return .{
|
||||
.source = try InventoryAndSlot.read(data[0..8], side, user),
|
||||
.pos = .{
|
||||
std.mem.readInt(i32, data[8..12], .big),
|
||||
std.mem.readInt(i32, data[12..16], .big),
|
||||
std.mem.readInt(i32, data[16..20], .big),
|
||||
},
|
||||
.oldBlock = @bitCast(std.mem.readInt(u32, data[20..24], .big)),
|
||||
.newBlock = @bitCast(std.mem.readInt(u32, data[24..28], .big)),
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const SourceType = enum(u8) {
|
||||
@ -1281,12 +1392,12 @@ pub fn fillAmountFromCreative(dest: Inventory, destSlot: u32, item: ?Item, amoun
|
||||
Sync.ClientSide.executeCommand(.{.fillFromCreative = .{.dest = .{.inv = dest, .slot = destSlot}, .item = item, .amount = amount}});
|
||||
}
|
||||
|
||||
pub fn placeBlock(self: Inventory, slot: u32, unlimitedBlocks: bool) void {
|
||||
main.renderer.MeshSelection.placeBlock(&self._items[slot], unlimitedBlocks);
|
||||
pub fn placeBlock(self: Inventory, slot: u32) void {
|
||||
main.renderer.MeshSelection.placeBlock(self, slot);
|
||||
}
|
||||
|
||||
pub fn breakBlock(self: Inventory, slot: u32) void {
|
||||
main.renderer.MeshSelection.breakBlock(&self._items[slot]);
|
||||
main.renderer.MeshSelection.breakBlock(self, slot);
|
||||
}
|
||||
|
||||
pub fn size(self: Inventory) usize {
|
||||
|
@ -337,6 +337,10 @@ pub const Block = packed struct { // MARK: Block
|
||||
pub inline fn opaqueVariant(self: Block) u16 {
|
||||
return _opaqueVariant[self.typ];
|
||||
}
|
||||
|
||||
pub fn canBeChangedInto(self: Block, newBlock: Block, item: main.items.ItemStack) main.rotation.RotationMode.CanBeChangedInto {
|
||||
return newBlock.mode().canBeChangedInto(self, newBlock, item);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -439,7 +439,7 @@ pub const Player = struct { // MARK: Player
|
||||
}
|
||||
}
|
||||
|
||||
inventory.placeBlock(selectedSlot, isCreative());
|
||||
inventory.placeBlock(selectedSlot);
|
||||
}
|
||||
|
||||
pub fn breakBlock() void { // TODO: Breaking animation and tools
|
||||
|
@ -1029,6 +1029,7 @@ pub const Tool = struct { // MARK: Tool
|
||||
.stone => self.pickaxePower,
|
||||
.unbreakable => 0,
|
||||
.wood => self.axePower,
|
||||
.air => 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -905,7 +905,7 @@ pub const Protocols = struct {
|
||||
const z = std.mem.readInt(i32, data[8..12], .big);
|
||||
const newBlock = Block.fromInt(std.mem.readInt(u32, data[12..16], .big));
|
||||
if(conn.user != null) {
|
||||
main.server.world.?.updateBlock(x, y, z, newBlock);
|
||||
return error.InvalidPacket;
|
||||
} else {
|
||||
renderer.mesh_storage.updateBlock(x, y, z, newBlock);
|
||||
}
|
||||
@ -1162,6 +1162,8 @@ pub const Protocols = struct {
|
||||
} else {
|
||||
if(data[0] == 0xff) { // Confirmation
|
||||
items.Inventory.Sync.ClientSide.receiveConfirmation(data[1..]);
|
||||
} else if(data[0] == 0xfe) { // Failure
|
||||
items.Inventory.Sync.ClientSide.receiveFailure();
|
||||
} else {
|
||||
try items.Inventory.Sync.ClientSide.receiveSyncOperation(data[1..]);
|
||||
}
|
||||
@ -1184,6 +1186,10 @@ pub const Protocols = struct {
|
||||
@memcpy(data[1..], _data);
|
||||
conn.sendImportant(id, data);
|
||||
}
|
||||
pub fn sendFailure(conn: *Connection) void {
|
||||
std.debug.assert(conn.user != null);
|
||||
conn.sendImportant(id, &.{0xfe});
|
||||
}
|
||||
pub fn sendSyncOperation(conn: *Connection, _data: []const u8) void {
|
||||
std.debug.assert(conn.user != null);
|
||||
var data = main.stackAllocator.alloc(u8, _data.len + 1);
|
||||
|
@ -752,12 +752,11 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
return true; // TODO: Check other entities
|
||||
}
|
||||
|
||||
pub fn placeBlock(inventoryStack: *main.items.ItemStack, unlimitedBlocks: bool) void {
|
||||
const removeAmount: i32 = if(unlimitedBlocks) 0 else -1;
|
||||
_ = removeAmount; // TODO
|
||||
pub fn placeBlock(inventory: main.items.Inventory, slot: u32) void {
|
||||
if(selectedBlockPos) |selectedPos| {
|
||||
var block = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
if(inventoryStack.item) |item| {
|
||||
var oldBlock = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
var block = oldBlock;
|
||||
if(inventory.getItem(slot)) |item| {
|
||||
switch(item) {
|
||||
.baseItem => |baseItem| {
|
||||
if(baseItem.block) |itemBlock| {
|
||||
@ -768,8 +767,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selectedPos)));
|
||||
if(rotationMode.generateData(main.game.world.?, selectedPos, relPos, lastDir, neighborDir, &block, .{.typ = 0, .data = 0}, false)) {
|
||||
if(!canPlaceBlock(selectedPos, block)) return;
|
||||
updateBlockAndSendUpdate(selectedPos[0], selectedPos[1], selectedPos[2], block);
|
||||
// TODO: _ = inventoryStack.add(item, @as(i32, removeAmount));
|
||||
updateBlockAndSendUpdate(inventory, slot, selectedPos[0], selectedPos[1], selectedPos[2], oldBlock, block);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -778,12 +776,12 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
neighborDir = selectedPos - posBeforeBlock;
|
||||
const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos)));
|
||||
const neighborBlock = block;
|
||||
block = mesh_storage.getBlock(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return;
|
||||
oldBlock = mesh_storage.getBlock(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return;
|
||||
block = oldBlock;
|
||||
if(block.typ == itemBlock) {
|
||||
if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, neighborBlock, false)) {
|
||||
if(!canPlaceBlock(neighborPos, block)) return;
|
||||
updateBlockAndSendUpdate(neighborPos[0], neighborPos[1], neighborPos[2], block);
|
||||
// TODO: _ = inventoryStack.add(item, @as(i32, removeAmount));
|
||||
updateBlockAndSendUpdate(inventory, slot, neighborPos[0], neighborPos[1], neighborPos[2], oldBlock, block);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -792,8 +790,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
block.data = 0;
|
||||
if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, neighborBlock, true)) {
|
||||
if(!canPlaceBlock(neighborPos, block)) return;
|
||||
updateBlockAndSendUpdate(neighborPos[0], neighborPos[1], neighborPos[2], block);
|
||||
// TODO: _ = inventoryStack.add(item, @as(i32, removeAmount));
|
||||
updateBlockAndSendUpdate(inventory, slot, neighborPos[0], neighborPos[1], neighborPos[2], oldBlock, block);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -807,21 +804,21 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
}
|
||||
}
|
||||
|
||||
pub fn breakBlock(inventoryStack: *main.items.ItemStack) void {
|
||||
pub fn breakBlock(inventory: main.items.Inventory, slot: u32) void {
|
||||
if(selectedBlockPos) |selectedPos| {
|
||||
const block = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
var newBlock = block;
|
||||
// TODO: Breaking animation and tools.
|
||||
const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selectedPos)));
|
||||
block.mode().onBlockBreaking(inventoryStack.item, relPos, lastDir, &newBlock);
|
||||
block.mode().onBlockBreaking(inventory.getStack(slot).item, relPos, lastDir, &newBlock);
|
||||
if(!std.meta.eql(newBlock, block)) {
|
||||
updateBlockAndSendUpdate(selectedPos[0], selectedPos[1], selectedPos[2], newBlock);
|
||||
updateBlockAndSendUpdate(inventory, slot, selectedPos[0], selectedPos[1], selectedPos[2], block, newBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn updateBlockAndSendUpdate(x: i32, y: i32, z: i32, newBlock: blocks.Block) void {
|
||||
main.network.Protocols.blockUpdate.send(main.game.world.?.conn, x, y, z, newBlock);
|
||||
fn updateBlockAndSendUpdate(source: main.items.Inventory, slot: u32, x: i32, y: i32, z: i32, oldBlock: blocks.Block, newBlock: blocks.Block) void {
|
||||
main.items.Inventory.Sync.ClientSide.executeCommand(.{.updateBlock = .{.source = .{.inv = source, .slot = slot}, .pos = .{x, y, z}, .oldBlock = oldBlock, .newBlock = newBlock}});
|
||||
mesh_storage.updateBlock(x, y, z, newBlock);
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,39 @@ pub const RotationMode = struct { // MARK: RotationMode
|
||||
fn onBlockBreaking(_: ?main.items.Item, _: Vec3f, _: Vec3f, currentData: *Block) void {
|
||||
currentData.* = .{.typ = 0, .data = 0};
|
||||
}
|
||||
fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemStack) CanBeChangedInto {
|
||||
if(std.meta.eql(oldBlock, newBlock)) return .no;
|
||||
if(oldBlock.typ == newBlock.typ) return .yes;
|
||||
if(oldBlock.solid()) {
|
||||
var power: f32 = 0;
|
||||
const isTool = item.item != null and item.item.? == .tool;
|
||||
if(isTool) {
|
||||
power = item.item.?.tool.getPowerByBlockClass(oldBlock.blockClass());
|
||||
}
|
||||
if(power >= oldBlock.breakingPower()) {
|
||||
if(isTool) {
|
||||
return .{.yes_costsDurability = 1};
|
||||
} else return .yes;
|
||||
}
|
||||
} else {
|
||||
if(item.item) |_item| {
|
||||
if(_item == .baseItem) {
|
||||
if(_item.baseItem.block != null and _item.baseItem.block.? == newBlock.typ) {
|
||||
return .{.yes_costsItems = 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .no;
|
||||
}
|
||||
};
|
||||
|
||||
pub const CanBeChangedInto = union(enum) {
|
||||
no: void,
|
||||
yes: void,
|
||||
yes_costsDurability: u16,
|
||||
yes_costsItems: u16,
|
||||
yes_dropsItems: u16,
|
||||
};
|
||||
|
||||
/// if the block should be destroyed or changed when a certain neighbor is removed.
|
||||
@ -82,6 +115,8 @@ pub const RotationMode = struct { // MARK: RotationMode
|
||||
rayIntersection: *const fn(block: Block, item: ?main.items.Item, relativePlayerPos: Vec3f, playerDir: Vec3f) ?RayIntersectionResult = &DefaultFunctions.rayIntersection,
|
||||
|
||||
onBlockBreaking: *const fn(item: ?main.items.Item, relativePlayerPos: Vec3f, playerDir: Vec3f, currentData: *Block) void = &DefaultFunctions.onBlockBreaking,
|
||||
|
||||
canBeChangedInto: *const fn(oldBlock: Block, newBlock: Block, item: main.items.ItemStack) CanBeChangedInto = DefaultFunctions.canBeChangedInto,
|
||||
};
|
||||
|
||||
var rotationModes: std.StringHashMap(RotationMode) = undefined;
|
||||
@ -571,6 +606,15 @@ pub const RotationModes = struct {
|
||||
}
|
||||
return RotationMode.DefaultFunctions.onBlockBreaking(item, relativePlayerPos, playerDir, currentData);
|
||||
}
|
||||
|
||||
pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemStack) RotationMode.CanBeChangedInto {
|
||||
if(oldBlock.typ != newBlock.typ) return RotationMode.DefaultFunctions.canBeChangedInto(oldBlock, newBlock, item);
|
||||
if(oldBlock.data == newBlock.data) return .no;
|
||||
if(item.item != null and item.item.? == .baseItem and std.mem.eql(u8, item.item.?.baseItem.id, "cubyz:chisel")) {
|
||||
return .yes; // TODO: Durability change, after making the chisel a proper tool.
|
||||
}
|
||||
return .no;
|
||||
}
|
||||
};
|
||||
pub const Torch = struct { // MARK: Torch
|
||||
pub const id: []const u8 = "torch";
|
||||
@ -719,6 +763,21 @@ pub const RotationModes = struct {
|
||||
currentData.data &= ~bit;
|
||||
if(currentData.data == 0) currentData.typ = 0;
|
||||
}
|
||||
|
||||
pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemStack) RotationMode.CanBeChangedInto {
|
||||
switch(RotationMode.DefaultFunctions.canBeChangedInto(oldBlock, newBlock, item)) {
|
||||
.no, .yes_costsDurability, .yes_dropsItems => return .no,
|
||||
.yes, .yes_costsItems => {
|
||||
const torchAmountChange = @as(i32, @popCount(newBlock.data)) - if(oldBlock.typ == newBlock.typ) @as(i32, @popCount(oldBlock.data)) else 0;
|
||||
if(torchAmountChange <= 0) {
|
||||
return .{.yes_dropsItems = @intCast(-torchAmountChange)};
|
||||
} else {
|
||||
if(item.item == null or item.item.? != .baseItem or !std.meta.eql(item.item.?.baseItem.block, newBlock.typ)) return .no;
|
||||
return .{.yes_costsItems = @intCast(torchAmountChange)};
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
pub const Carpet = struct { // MARK: Carpet
|
||||
pub const id: []const u8 = "carpet";
|
||||
@ -886,6 +945,10 @@ pub const RotationModes = struct {
|
||||
currentData.data &= ~bit;
|
||||
if(currentData.data == 0) currentData.typ = 0;
|
||||
}
|
||||
|
||||
pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemStack) RotationMode.CanBeChangedInto {
|
||||
return Torch.canBeChangedInto(oldBlock, newBlock, item);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -895,12 +895,23 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
||||
return ch.getBlock(x - ch.super.pos.wx, y - ch.super.pos.wy, z - ch.super.pos.wz);
|
||||
}
|
||||
|
||||
pub fn updateBlock(_: *ServerWorld, wx: i32, wy: i32, wz: i32, _newBlock: Block) void {
|
||||
/// Returns the actual block on failure
|
||||
pub fn cmpxchgBlock(_: *ServerWorld, wx: i32, wy: i32, wz: i32, oldBlock: ?Block, _newBlock: Block) ?Block {
|
||||
const baseChunk = ChunkManager.getOrGenerateChunkAndIncreaseRefCount(.{.wx = wx & ~@as(i32, chunk.chunkMask), .wy = wy & ~@as(i32, chunk.chunkMask), .wz = wz & ~@as(i32, chunk.chunkMask), .voxelSize = 1});
|
||||
defer baseChunk.decreaseRefCount();
|
||||
const x: u5 = @intCast(wx & chunk.chunkMask);
|
||||
const y: u5 = @intCast(wy & chunk.chunkMask);
|
||||
const z: u5 = @intCast(wz & chunk.chunkMask);
|
||||
baseChunk.mutex.lock();
|
||||
const currentBlock = baseChunk.getBlock(x, y, z);
|
||||
if(oldBlock != null) {
|
||||
if(!std.meta.eql(oldBlock.?, currentBlock)) {
|
||||
baseChunk.mutex.unlock();
|
||||
return currentBlock;
|
||||
}
|
||||
baseChunk.updateBlockAndSetChanged(x, y, z, _newBlock);
|
||||
}
|
||||
baseChunk.mutex.unlock();
|
||||
var newBlock = _newBlock;
|
||||
for(chunk.Neighbor.iterable) |neighbor| {
|
||||
const nx = x + neighbor.relX();
|
||||
@ -938,6 +949,11 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
||||
for(main.server.users.items) |user| {
|
||||
main.network.Protocols.blockUpdate.send(user.conn, wx, wy, wz, _newBlock);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn updateBlock(self: *ServerWorld, wx: i32, wy: i32, wz: i32, newBlock: Block) void {
|
||||
_ = self.cmpxchgBlock(wx, wy, wz, null, newBlock);
|
||||
}
|
||||
|
||||
pub fn queueChunkUpdateAndDecreaseRefCount(self: *ServerWorld, ch: *ServerChunk) void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user