Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
121 changes: 120 additions & 1 deletion src/Inventory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ pub const Command = struct { // MARK: Command
depositOrDrop = 7,
clear = 8,
updateBlock = 9,
addHealth = 10,
damageBlock = 10,
addHealth = 11,
};
pub const Payload = union(PayloadType) {
open: Open,
Expand All @@ -469,6 +470,7 @@ pub const Command = struct { // MARK: Command
depositOrDrop: DepositOrDrop,
clear: Clear,
updateBlock: UpdateBlock,
damageBlock: DamageBlock,
addHealth: AddHealth,
};

Expand Down Expand Up @@ -1778,6 +1780,123 @@ pub const Command = struct { // MARK: Command
};
}
};

const DamageBlock = struct {
source: InventoryAndSlot,
pos: Vec3i,
dropLocation: UpdateBlock.BlockDropLocation,
oldBlock: Block,
newBlock: Block,

fn run(self: DamageBlock, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User, gamemode: Gamemode) error{serverFailure}!void {
if(self.source.inv.type != .normal) return;
if(gamemode == .creative) return;

const stack = self.source.ref();
const isTool = stack.item != null and stack.item.? == .tool;

var shouldDropSourceBlockOnSuccess: bool = true;
const costOfChange = self.oldBlock.canBeChangedInto(self.newBlock, stack.*, &shouldDropSourceBlockOnSuccess);

// Check if we can change it:
if(!switch(costOfChange) {
.no => false,
.yes => true,
.yes_costsDurability => isTool,
.yes_costsItems => false,
.yes_dropsItems => false,
}) {
if(side == .server) {
// Inform the client of the actual block:
const actualBlock = main.server.world.?.getBlock(self.pos[0], self.pos[1], self.pos[2]) orelse return;
main.network.Protocols.blockUpdate.send(user.?.conn, &.{.init(self.pos, actualBlock)});
}
return;
}

const damageDelta: f32 = if(isTool) stack.item.?.tool.getBlockDamage(self.oldBlock) - self.oldBlock.blockResistance() else 1;
if(damageDelta <= 0) return;

switch(costOfChange) {
.yes => {},
.yes_costsDurability => {
cmd.executeBaseOperation(allocator, .{.useDurability = .{
.source = self.source,
.durability = 1,
}}, side);
},
else => unreachable,
}

const maxBlockHealth: f32 = self.oldBlock.blockHealth();
var remainingHealth: f32 = 0.0;

if(side == .server) {
main.server.world.?.blockDamage.mutex.lock();
const currentRemainingHealth = main.server.world.?.blockDamage.get(self.pos) orelse maxBlockHealth;
std.debug.assert(currentRemainingHealth > 0.0);
std.debug.assert(currentRemainingHealth <= maxBlockHealth);

remainingHealth = @min(maxBlockHealth, @max(0.0, currentRemainingHealth - damageDelta));

main.server.world.?.blockDamage.set(self.pos, remainingHealth);
} else if(side == .client) {
main.renderer.mesh_storage.blockDamage.mutex.lock();
const currentRemainingHealth = main.renderer.mesh_storage.blockDamage.get(self.pos) orelse maxBlockHealth;
std.debug.assert(currentRemainingHealth > 0.0);
std.debug.assert(currentRemainingHealth <= maxBlockHealth);

remainingHealth = @min(maxBlockHealth, @max(0.0, currentRemainingHealth - damageDelta));

main.renderer.mesh_storage.blockDamage.set(self.pos, remainingHealth);
}

if(side == .server) {
if(remainingHealth <= 0) {
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, &.{.init(self.pos, actualBlock)});
return error.serverFailure;
}

if(self.oldBlock.typ != self.newBlock.typ and shouldDropSourceBlockOnSuccess) {
for(self.oldBlock.blockDrops()) |drop| {
if(drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) {
self.dropLocation.drop(self.pos, self.newBlock, drop);
}
}
}
}
main.server.world.?.blockDamage.mutex.unlock();
} else {
main.renderer.mesh_storage.blockDamage.mutex.unlock();
}
}

fn serialize(self: DamageBlock, writer: *utils.BinaryWriter) void {
self.source.write(writer);
writer.writeVec(Vec3i, self.pos);
writer.writeEnum(Neighbor, self.dropLocation.dir);
writer.writeVec(Vec3f, self.dropLocation.min);
writer.writeVec(Vec3f, self.dropLocation.max);
writer.writeInt(u32, @as(u32, @bitCast(self.oldBlock)));
writer.writeInt(u32, @as(u32, @bitCast(self.newBlock)));
}

fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DamageBlock {
return .{
.source = try InventoryAndSlot.read(reader, side, user),
.pos = try reader.readVec(Vec3i),
.dropLocation = .{
.dir = try reader.readEnum(Neighbor),
.min = try reader.readVec(Vec3f),
.max = try reader.readVec(Vec3f),
},
.oldBlock = @bitCast(try reader.readInt(u32)),
.newBlock = @bitCast(try reader.readInt(u32)),
};
}
};
};

const SourceType = enum(u8) {
Expand Down
1 change: 1 addition & 0 deletions src/blocks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ pub const Block = packed struct { // MARK: Block
typ: u16,
data: u16,

pub const healingRatio: f32 = 0.01;
pub const air = Block{.typ = 0, .data = 0};

pub fn toInt(self: Block) u32 {
Expand Down
2 changes: 2 additions & 0 deletions src/game.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1212,5 +1212,7 @@ pub fn update(deltaTime: f64) void { // MARK: update()
fog.fogLower = (biome.fogLower - fog.fogLower)*t + fog.fogLower;
fog.fogHigher = (biome.fogHigher - fog.fogHigher)*t + fog.fogHigher;

main.renderer.mesh_storage.blockDamage.update(deltaTime);

world.?.update();
}
41 changes: 41 additions & 0 deletions src/network.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,7 @@ pub const Protocols = struct {
worldEditPos = 2,
time = 3,
biome = 4,
damageBlock = 5,
};

const WorldEditPosition = enum(u2) {
Expand All @@ -1029,6 +1030,11 @@ pub const Protocols = struct {
clear = 2,
};

const BlockDamage = enum(u8) {
sync = 0,
update = 1,
};

fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
switch(try reader.readEnum(UpdateType)) {
.gamemode => {
Expand Down Expand Up @@ -1064,6 +1070,21 @@ pub const Protocols = struct {
}
}
},
.damageBlock => {
const typ = try reader.readEnum(BlockDamage);
switch(typ) {
.sync => {
try renderer.mesh_storage.blockDamage.loadSerialized(reader);
},
.update => {
const pos = try reader.readVec(Vec3i);
const remainingHealth = try reader.readFloat(f32);
renderer.mesh_storage.blockDamage.mutex.lock();
renderer.mesh_storage.blockDamage.set(pos, remainingHealth);
renderer.mesh_storage.blockDamage.mutex.unlock();
},
}
},
.time => {
if(conn.manager.world) |world| {
const expectedTime = try reader.readInt(i64);
Expand Down Expand Up @@ -1142,6 +1163,26 @@ pub const Protocols = struct {

conn.send(.fast, id, writer.data.items);
}

pub fn sendDamageBlock(conn: *Connection, typ: BlockDamage, _pos: ?Vec3i, remainingHealth: ?f32) void {
var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 18);
defer writer.deinit();

writer.writeEnum(UpdateType, .damageBlock);
writer.writeEnum(BlockDamage, typ);

switch(typ) {
.sync => {
main.server.world.?.blockDamage.serialize(&writer);
},
.update => {
writer.writeVec(Vec3i, _pos.?);
writer.writeFloat(f32, remainingHealth.?);
},
}

conn.send(.fast, id, writer.data.items);
}
};
pub const chat = struct {
pub const id: u8 = 10;
Expand Down
32 changes: 18 additions & 14 deletions src/renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection
}

if(@reduce(.Or, lastSelectedBlockPos != selectedPos)) {
mesh_storage.removeBreakingAnimation(lastSelectedBlockPos);
lastSelectedBlockPos = selectedPos;
currentBlockProgress = 0;
currentSwingProgress = 0;
Expand All @@ -1052,24 +1051,29 @@ pub const MeshSelection = struct { // MARK: MeshSelection
currentSwingProgress = 0;
currentSwingTime = swingTime;
}
var newBlock = block;
block.mode().onBlockBreaking(inventory.getStack(slot).item, relPos, lastDir, &newBlock);
currentSwingProgress += @floatCast(deltaTime);
while(currentSwingProgress > currentSwingTime) {
currentSwingProgress -= currentSwingTime;
currentBlockProgress += damage/block.blockHealth();
if(currentBlockProgress > 1) break;
}
if(currentBlockProgress < 1) {
mesh_storage.removeBreakingAnimation(lastSelectedBlockPos);
if(currentBlockProgress != 0) {
mesh_storage.addBreakingAnimation(lastSelectedBlockPos, currentBlockProgress);
}
main.items.Inventory.Sync.ClientSide.mutex.unlock();

return;
} else {
mesh_storage.removeBreakingAnimation(lastSelectedBlockPos);
currentBlockProgress = 0;
main.items.Inventory.Sync.ClientSide.executeCommand(.{
.damageBlock = .{
.source = .{.inv = inventory, .slot = slot},
.pos = selectedPos,
.dropLocation = .{
.dir = selectionFace,
.min = selectionMin,
.max = selectionMax,
},
.oldBlock = block,
.newBlock = newBlock,
},
});
main.items.Inventory.Sync.ClientSide.mutex.lock();
}
main.items.Inventory.Sync.ClientSide.mutex.unlock();
return;
} else {
main.items.Inventory.Sync.ClientSide.mutex.unlock();
return;
Expand Down
Loading