Skip to content

Commit

Permalink
Added fall damage and /kill (#959)
Browse files Browse the repository at this point in the history
* add player health and fall damage and /kill

* less latency for taking damage

* clamp health

* changes

* final changes maybe

* return serverFailure if target not found

* fix error

* remove /damage
  • Loading branch information
OneAvargeCoder193 authored Jan 25, 2025
1 parent 967e24a commit 818f736
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 10 deletions.
150 changes: 147 additions & 3 deletions src/Inventory.zig
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ pub const Sync = struct { // MARK: Sync
defer main.stackAllocator.free(users);

for (users) |user| {
if (user == source) continue;
if (user == source and op.ignoreSource()) continue;
main.network.Protocols.inventory.sendSyncOperation(user.conn, syncData);
}
}
Expand Down Expand Up @@ -410,6 +410,14 @@ pub const Sync = struct { // MARK: Sync
}
};

pub fn addHealth(health: f32, cause: main.game.DamageType, side: Side, id: u32) void {
if (side == .client) {
Sync.ClientSide.executeCommand(.{.addHealth = .{.target = id, .health = health, .cause = cause}});
} else {
Sync.ServerSide.executeCommand(.{.addHealth = .{.target = id, .health = health, .cause = cause}}, null);
}
}

pub fn getInventory(id: u32, side: Side, user: ?*main.server.User) ?Inventory {
return switch(side) {
.client => ClientSide.getInventory(id),
Expand Down Expand Up @@ -438,6 +446,7 @@ pub const Command = struct { // MARK: Command
depositOrDrop = 7,
clear = 8,
updateBlock = 9,
addHealth = 10,
};
pub const Payload = union(PayloadType) {
open: Open,
Expand All @@ -450,6 +459,7 @@ pub const Command = struct { // MARK: Command
depositOrDrop: DepositOrDrop,
clear: Clear,
updateBlock: UpdateBlock,
addHealth: AddHealth,
};

const BaseOperationType = enum(u8) {
Expand All @@ -458,6 +468,7 @@ pub const Command = struct { // MARK: Command
delete = 2,
create = 3,
useDurability = 4,
addHealth = 5,
};

const InventoryAndSlot = struct {
Expand Down Expand Up @@ -508,12 +519,20 @@ pub const Command = struct { // MARK: Command
durability: u31,
previousDurability: u32 = undefined,
},
addHealth: struct {
target: ?*main.server.User,
health: f32,
cause: main.game.DamageType,
previous: f32
}
};

const SyncOperationType = enum(u8) {
create = 0,
delete = 1,
useDurability = 2,
health = 3,
kill = 4,
};

const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation
Expand All @@ -531,6 +550,13 @@ pub const Command = struct { // MARK: Command
inv: InventoryAndSlot,
durability: u32
},
health: struct {
target: ?*main.server.User,
health: f32
},
kill: struct {
target: ?*main.server.User
},

pub fn executeFromData(data: []const u8) !void {
std.debug.assert(data.len >= 1);
Expand Down Expand Up @@ -569,6 +595,12 @@ pub const Command = struct { // MARK: Command
}

durability.inv.inv.update();
},
.health => |health| {
main.game.Player.super.health = std.math.clamp(main.game.Player.super.health + health.health, 0, main.game.Player.super.maxHealth);
},
.kill => {
main.game.Player.kill();
}
}
}
Expand All @@ -577,10 +609,22 @@ pub const Command = struct { // MARK: Command
switch (self) {
inline .create, .delete, .useDurability => |data| {
return allocator.dupe(*main.server.User, Sync.ServerSide.inventories.items[data.inv.inv.id].users.items);
},
inline .health, .kill => |data| {
const out = allocator.alloc(*main.server.User, 1);
out[0] = data.target.?;
return out;
}
}
}

pub fn ignoreSource(self: SyncOperation) bool {
return switch (self) {
.create, .delete, .useDurability, .health => true,
.kill => false
};
}

fn deserialize(fullData: []const u8) !SyncOperation {
if (fullData.len == 0) {
return error.Invalid;
Expand Down Expand Up @@ -630,6 +674,25 @@ pub const Command = struct { // MARK: Command
}};

return out;
},
.health => {
if (data.len != 4) {
return error.Invalid;
}

return .{.health = .{
.target = null,
.health = @bitCast(std.mem.readInt(u32, data[0..4], .big))
}};
},
.kill => {
if (data.len != 0) {
return error.Invalid;
}

return .{.kill = .{
.target = null,
}};
}
}
}
Expand Down Expand Up @@ -657,7 +720,11 @@ pub const Command = struct { // MARK: Command
.useDurability => |durability| {
durability.inv.write(data.addMany(8)[0..8]);
std.mem.writeInt(u32, data.addMany(4)[0..4], durability.durability, .big);
}
},
.health => |health| {
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(health.health), .big);
},
.kill => {},
}
return data.toOwnedSlice();
}
Expand Down Expand Up @@ -729,6 +796,9 @@ pub const Command = struct { // MARK: Command
info.source.ref().item = info.item;
info.item.tool.durability = info.previousDurability;
info.source.inv.update();
},
.addHealth => |info| {
main.game.Player.super.health = info.previous;
}
}
}
Expand All @@ -737,7 +807,7 @@ pub const Command = struct { // MARK: Command
fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, data: []const u8) void {
for(self.baseOperations.items) |step| {
switch(step) {
.move, .swap, .create => {},
.move, .swap, .create, .addHealth => {},
.delete => |info| {
info.item.?.deinit();
},
Expand Down Expand Up @@ -854,6 +924,30 @@ pub const Command = struct { // MARK: Command
self.executeDurabilityUseOperation(allocator, side, info.source, info.durability);
info.source.inv.update();
},
.addHealth => |*info| {
if (side == .server) {
info.previous = info.target.?.player.health;

info.target.?.player.health = std.math.clamp(info.target.?.player.health + info.health, 0, info.target.?.player.maxHealth);

if (info.target.?.player.health <= 0) {
info.target.?.player.health = info.target.?.player.maxHealth;
info.cause.sendMessage(info.target.?.name);

self.syncOperations.append(allocator, .{.kill = .{
.target = info.target.?
}});
} else {
self.syncOperations.append(allocator, .{.health = .{
.target = info.target.?,
.health = info.health
}});
}
} else {
info.previous = main.game.Player.super.health;
main.game.Player.super.health = std.math.clamp(main.game.Player.super.health + info.health, 0, main.game.Player.super.maxHealth);
}
}
}
self.baseOperations.append(allocator, op);
}
Expand Down Expand Up @@ -1497,6 +1591,56 @@ pub const Command = struct { // MARK: Command
};
}
};

const AddHealth = struct { // MARK: AddHealth
target: u32,
health: f32,
cause: main.game.DamageType,

pub fn run(self: AddHealth, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {
var target: ?*main.server.User = null;

if (side == .server) {
const userList = main.server.getUserListAndIncreaseRefCount(main.stackAllocator);
defer main.server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for (userList) |user| {
if (user.id == self.target) {
target = user;
break;
}
}

if (target == null) return error.serverFailure;

if (target.?.gamemode.raw == .creative) return;
} else {
if (main.game.Player.gamemode.raw == .creative) return;
}

cmd.executeBaseOperation(allocator, .{.addHealth = .{
.target = target,
.health = self.health,
.cause = self.cause,
.previous = if (side == .server) target.?.player.health else main.game.Player.super.health
}}, side);
}

fn serialize(self: AddHealth, data: *main.List(u8)) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.target, .big);
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(self.health), .big);
data.append(@intFromEnum(self.cause));
}

fn deserialize(data: []const u8, _: Side, _: ?*main.server.User) !AddHealth {
if(data.len != 9) return error.Invalid;

return .{
.target = std.mem.readInt(u32, data[0..4], .big),
.health = @bitCast(std.mem.readInt(u32, data[4..8], .big)),
.cause = @enumFromInt(data[8]),
};
}
};
};

const SourceType = enum(u8) {
Expand Down
34 changes: 31 additions & 3 deletions src/game.zig
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,20 @@ pub const collision = struct {

pub const Gamemode = enum(u8) { survival = 0, creative = 1 };

pub const DamageType = enum(u8) {
heal = 0, // For when you are adding health
kill = 1,
fall = 2,

pub fn sendMessage(self: DamageType, name: []const u8) void {
switch (self) {
.heal => main.server.sendMessage("{s}§#ffffff was healed", .{name}),
.kill => main.server.sendMessage("{s}§#ffffff was killed", .{name}),
.fall => main.server.sendMessage("{s}§#ffffff died of fall damage", .{name}),
}
}
};

pub const Player = struct { // MARK: Player
pub var super: main.server.Entity = .{};
pub var eyePos: Vec3d = .{0, 0, 0};
Expand All @@ -341,9 +355,6 @@ pub const Player = struct { // MARK: Player
pub var inventory: Inventory = undefined;
pub var selectedSlot: u32 = 0;

pub var maxHealth: f32 = 8;
pub var health: f32 = 4.5;

pub var onGround: bool = false;
pub var jumpCooldown: f64 = 0;
const jumpCooldownConstant = 0.3;
Expand Down Expand Up @@ -442,6 +453,17 @@ pub const Player = struct { // MARK: Player
inventory.placeBlock(selectedSlot);
}

pub fn kill() void {
Player.super.pos = world.?.spawn;
Player.super.vel = .{0, 0, 0};

Player.super.health = Player.super.maxHealth;

Player.eyeVel = .{0, 0, 0};
Player.eyeCoyote = 0;
Player.eyeStep = .{false, false, false};
}

pub fn breakBlock(deltaTime: f64) void {
if(!main.Window.grabbed) return;
inventory.breakBlock(selectedSlot, deltaTime);
Expand Down Expand Up @@ -955,6 +977,12 @@ pub fn update(deltaTime: f64) void { // MARK: update()
} else {
Player.super.pos[2] = box.min[2] - hitBox.max[2];
}

const damage: f32 = @floatCast(@round(@max((Player.super.vel[2] * Player.super.vel[2]) / (2 * gravity) - 3, 0)) / 2);
if (damage > 0.01) {
Inventory.Sync.addHealth(-damage, .fall, .client, Player.id);
}

Player.super.vel[2] = 0;

// Always unstuck upwards for now
Expand Down
6 changes: 3 additions & 3 deletions src/gui/windows/healthbar.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ pub fn render() void {
var y: f32 = 0;
var x: f32 = 0;
var health: f32 = 0;
while(health < main.game.Player.maxHealth) : (health += 1) {
while(health < main.game.Player.super.maxHealth) : (health += 1) {
if(x >= window.contentSize[0]) {
x = 0;
y += 20;
}
if(health + 1 <= main.game.Player.health) {
if(health + 1 <= main.game.Player.super.health) {
heartTexture.bindTo(0);
} else if(health + 0.5 <= main.game.Player.health) {
} else if(health + 0.5 <= main.game.Player.super.health) {
halfHeartTexture.bindTo(0);
} else {
deadHeartTexture.bindTo(0);
Expand Down
7 changes: 6 additions & 1 deletion src/server/Entity.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ const NeverFailingAllocator = main.utils.NeverFailingAllocator;
pos: Vec3d = .{0, 0, 0},
vel: Vec3d = .{0, 0, 0},
rot: Vec3f = .{0, 0, 0},
// TODO: Health and hunger

health: f32 = 8,
maxHealth: f32 = 8,
// TODO: Hunger
// TODO: Name

pub fn loadFrom(self: *@This(), zon: ZonElement) void {
self.pos = zon.get(Vec3d, "position", .{0, 0, 0});
self.vel = zon.get(Vec3d, "velocity", .{0, 0, 0});
self.rot = zon.get(Vec3f, "rotation", .{0, 0, 0});
self.health = zon.get(f32, "health", self.maxHealth);
}

pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement {
const zon = ZonElement.initObject(allocator);
zon.put("position", self.pos);
zon.put("velocity", self.vel);
zon.put("rotation", self.rot);
zon.put("health", self.health);
return zon;
}
1 change: 1 addition & 0 deletions src/server/command/_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pub const clear = @import("clear.zig");
pub const gamemode = @import("gamemode.zig");
pub const help = @import("help.zig");
pub const invite = @import("invite.zig");
pub const kill = @import("kill.zig");
pub const time = @import("time.zig");
pub const tp = @import("tp.zig");
15 changes: 15 additions & 0 deletions src/server/command/kill.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const std = @import("std");

const main = @import("root");
const User = main.server.User;

pub const description = "Kills the player";
pub const usage = "/kill";

pub fn execute(args: []const u8, source: *User) void {
if(args.len != 0) {
source.sendMessage("#ff0000Too many arguments for command /kill. Expected no arguments.", .{});
return;
}
main.items.Inventory.Sync.addHealth(-std.math.floatMax(f32), .kill, .server, source.id);
}

0 comments on commit 818f736

Please sign in to comment.