mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-10 04:41:32 -04:00
Added fall damage and /kill (#959)
* 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
This commit is contained in:
parent
967e24a1c4
commit
818f736311
@ -267,7 +267,7 @@ pub const Sync = struct { // MARK: Sync
|
|||||||
defer main.stackAllocator.free(users);
|
defer main.stackAllocator.free(users);
|
||||||
|
|
||||||
for (users) |user| {
|
for (users) |user| {
|
||||||
if (user == source) continue;
|
if (user == source and op.ignoreSource()) continue;
|
||||||
main.network.Protocols.inventory.sendSyncOperation(user.conn, syncData);
|
main.network.Protocols.inventory.sendSyncOperation(user.conn, syncData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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 {
|
pub fn getInventory(id: u32, side: Side, user: ?*main.server.User) ?Inventory {
|
||||||
return switch(side) {
|
return switch(side) {
|
||||||
.client => ClientSide.getInventory(id),
|
.client => ClientSide.getInventory(id),
|
||||||
@ -438,6 +446,7 @@ pub const Command = struct { // MARK: Command
|
|||||||
depositOrDrop = 7,
|
depositOrDrop = 7,
|
||||||
clear = 8,
|
clear = 8,
|
||||||
updateBlock = 9,
|
updateBlock = 9,
|
||||||
|
addHealth = 10,
|
||||||
};
|
};
|
||||||
pub const Payload = union(PayloadType) {
|
pub const Payload = union(PayloadType) {
|
||||||
open: Open,
|
open: Open,
|
||||||
@ -450,6 +459,7 @@ pub const Command = struct { // MARK: Command
|
|||||||
depositOrDrop: DepositOrDrop,
|
depositOrDrop: DepositOrDrop,
|
||||||
clear: Clear,
|
clear: Clear,
|
||||||
updateBlock: UpdateBlock,
|
updateBlock: UpdateBlock,
|
||||||
|
addHealth: AddHealth,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BaseOperationType = enum(u8) {
|
const BaseOperationType = enum(u8) {
|
||||||
@ -458,6 +468,7 @@ pub const Command = struct { // MARK: Command
|
|||||||
delete = 2,
|
delete = 2,
|
||||||
create = 3,
|
create = 3,
|
||||||
useDurability = 4,
|
useDurability = 4,
|
||||||
|
addHealth = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
const InventoryAndSlot = struct {
|
const InventoryAndSlot = struct {
|
||||||
@ -508,12 +519,20 @@ pub const Command = struct { // MARK: Command
|
|||||||
durability: u31,
|
durability: u31,
|
||||||
previousDurability: u32 = undefined,
|
previousDurability: u32 = undefined,
|
||||||
},
|
},
|
||||||
|
addHealth: struct {
|
||||||
|
target: ?*main.server.User,
|
||||||
|
health: f32,
|
||||||
|
cause: main.game.DamageType,
|
||||||
|
previous: f32
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const SyncOperationType = enum(u8) {
|
const SyncOperationType = enum(u8) {
|
||||||
create = 0,
|
create = 0,
|
||||||
delete = 1,
|
delete = 1,
|
||||||
useDurability = 2,
|
useDurability = 2,
|
||||||
|
health = 3,
|
||||||
|
kill = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation
|
const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation
|
||||||
@ -531,6 +550,13 @@ pub const Command = struct { // MARK: Command
|
|||||||
inv: InventoryAndSlot,
|
inv: InventoryAndSlot,
|
||||||
durability: u32
|
durability: u32
|
||||||
},
|
},
|
||||||
|
health: struct {
|
||||||
|
target: ?*main.server.User,
|
||||||
|
health: f32
|
||||||
|
},
|
||||||
|
kill: struct {
|
||||||
|
target: ?*main.server.User
|
||||||
|
},
|
||||||
|
|
||||||
pub fn executeFromData(data: []const u8) !void {
|
pub fn executeFromData(data: []const u8) !void {
|
||||||
std.debug.assert(data.len >= 1);
|
std.debug.assert(data.len >= 1);
|
||||||
@ -569,6 +595,12 @@ pub const Command = struct { // MARK: Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
durability.inv.inv.update();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,10 +609,22 @@ pub const Command = struct { // MARK: Command
|
|||||||
switch (self) {
|
switch (self) {
|
||||||
inline .create, .delete, .useDurability => |data| {
|
inline .create, .delete, .useDurability => |data| {
|
||||||
return allocator.dupe(*main.server.User, Sync.ServerSide.inventories.items[data.inv.inv.id].users.items);
|
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 {
|
fn deserialize(fullData: []const u8) !SyncOperation {
|
||||||
if (fullData.len == 0) {
|
if (fullData.len == 0) {
|
||||||
return error.Invalid;
|
return error.Invalid;
|
||||||
@ -630,6 +674,25 @@ pub const Command = struct { // MARK: Command
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
return out;
|
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,
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,7 +720,11 @@ pub const Command = struct { // MARK: Command
|
|||||||
.useDurability => |durability| {
|
.useDurability => |durability| {
|
||||||
durability.inv.write(data.addMany(8)[0..8]);
|
durability.inv.write(data.addMany(8)[0..8]);
|
||||||
std.mem.writeInt(u32, data.addMany(4)[0..4], durability.durability, .big);
|
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();
|
return data.toOwnedSlice();
|
||||||
}
|
}
|
||||||
@ -729,6 +796,9 @@ pub const Command = struct { // MARK: Command
|
|||||||
info.source.ref().item = info.item;
|
info.source.ref().item = info.item;
|
||||||
info.item.tool.durability = info.previousDurability;
|
info.item.tool.durability = info.previousDurability;
|
||||||
info.source.inv.update();
|
info.source.inv.update();
|
||||||
|
},
|
||||||
|
.addHealth => |info| {
|
||||||
|
main.game.Player.super.health = info.previous;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -737,7 +807,7 @@ pub const Command = struct { // MARK: Command
|
|||||||
fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, data: []const u8) void {
|
fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, data: []const u8) void {
|
||||||
for(self.baseOperations.items) |step| {
|
for(self.baseOperations.items) |step| {
|
||||||
switch(step) {
|
switch(step) {
|
||||||
.move, .swap, .create => {},
|
.move, .swap, .create, .addHealth => {},
|
||||||
.delete => |info| {
|
.delete => |info| {
|
||||||
info.item.?.deinit();
|
info.item.?.deinit();
|
||||||
},
|
},
|
||||||
@ -854,6 +924,30 @@ pub const Command = struct { // MARK: Command
|
|||||||
self.executeDurabilityUseOperation(allocator, side, info.source, info.durability);
|
self.executeDurabilityUseOperation(allocator, side, info.source, info.durability);
|
||||||
info.source.inv.update();
|
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);
|
self.baseOperations.append(allocator, op);
|
||||||
}
|
}
|
||||||
@ -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) {
|
const SourceType = enum(u8) {
|
||||||
|
34
src/game.zig
34
src/game.zig
@ -326,6 +326,20 @@ pub const collision = struct {
|
|||||||
|
|
||||||
pub const Gamemode = enum(u8) { survival = 0, creative = 1 };
|
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 const Player = struct { // MARK: Player
|
||||||
pub var super: main.server.Entity = .{};
|
pub var super: main.server.Entity = .{};
|
||||||
pub var eyePos: Vec3d = .{0, 0, 0};
|
pub var eyePos: Vec3d = .{0, 0, 0};
|
||||||
@ -341,9 +355,6 @@ pub const Player = struct { // MARK: Player
|
|||||||
pub var inventory: Inventory = undefined;
|
pub var inventory: Inventory = undefined;
|
||||||
pub var selectedSlot: u32 = 0;
|
pub var selectedSlot: u32 = 0;
|
||||||
|
|
||||||
pub var maxHealth: f32 = 8;
|
|
||||||
pub var health: f32 = 4.5;
|
|
||||||
|
|
||||||
pub var onGround: bool = false;
|
pub var onGround: bool = false;
|
||||||
pub var jumpCooldown: f64 = 0;
|
pub var jumpCooldown: f64 = 0;
|
||||||
const jumpCooldownConstant = 0.3;
|
const jumpCooldownConstant = 0.3;
|
||||||
@ -442,6 +453,17 @@ pub const Player = struct { // MARK: Player
|
|||||||
inventory.placeBlock(selectedSlot);
|
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 {
|
pub fn breakBlock(deltaTime: f64) void {
|
||||||
if(!main.Window.grabbed) return;
|
if(!main.Window.grabbed) return;
|
||||||
inventory.breakBlock(selectedSlot, deltaTime);
|
inventory.breakBlock(selectedSlot, deltaTime);
|
||||||
@ -955,6 +977,12 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
|||||||
} else {
|
} else {
|
||||||
Player.super.pos[2] = box.min[2] - hitBox.max[2];
|
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;
|
Player.super.vel[2] = 0;
|
||||||
|
|
||||||
// Always unstuck upwards for now
|
// Always unstuck upwards for now
|
||||||
|
@ -47,14 +47,14 @@ pub fn render() void {
|
|||||||
var y: f32 = 0;
|
var y: f32 = 0;
|
||||||
var x: f32 = 0;
|
var x: f32 = 0;
|
||||||
var health: 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]) {
|
if(x >= window.contentSize[0]) {
|
||||||
x = 0;
|
x = 0;
|
||||||
y += 20;
|
y += 20;
|
||||||
}
|
}
|
||||||
if(health + 1 <= main.game.Player.health) {
|
if(health + 1 <= main.game.Player.super.health) {
|
||||||
heartTexture.bindTo(0);
|
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);
|
halfHeartTexture.bindTo(0);
|
||||||
} else {
|
} else {
|
||||||
deadHeartTexture.bindTo(0);
|
deadHeartTexture.bindTo(0);
|
||||||
|
@ -10,13 +10,17 @@ const NeverFailingAllocator = main.utils.NeverFailingAllocator;
|
|||||||
pos: Vec3d = .{0, 0, 0},
|
pos: Vec3d = .{0, 0, 0},
|
||||||
vel: Vec3d = .{0, 0, 0},
|
vel: Vec3d = .{0, 0, 0},
|
||||||
rot: Vec3f = .{0, 0, 0},
|
rot: Vec3f = .{0, 0, 0},
|
||||||
// TODO: Health and hunger
|
|
||||||
|
health: f32 = 8,
|
||||||
|
maxHealth: f32 = 8,
|
||||||
|
// TODO: Hunger
|
||||||
// TODO: Name
|
// TODO: Name
|
||||||
|
|
||||||
pub fn loadFrom(self: *@This(), zon: ZonElement) void {
|
pub fn loadFrom(self: *@This(), zon: ZonElement) void {
|
||||||
self.pos = zon.get(Vec3d, "position", .{0, 0, 0});
|
self.pos = zon.get(Vec3d, "position", .{0, 0, 0});
|
||||||
self.vel = zon.get(Vec3d, "velocity", .{0, 0, 0});
|
self.vel = zon.get(Vec3d, "velocity", .{0, 0, 0});
|
||||||
self.rot = zon.get(Vec3f, "rotation", .{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 {
|
pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement {
|
||||||
@ -24,5 +28,6 @@ pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement {
|
|||||||
zon.put("position", self.pos);
|
zon.put("position", self.pos);
|
||||||
zon.put("velocity", self.vel);
|
zon.put("velocity", self.vel);
|
||||||
zon.put("rotation", self.rot);
|
zon.put("rotation", self.rot);
|
||||||
|
zon.put("health", self.health);
|
||||||
return zon;
|
return zon;
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,6 @@ pub const clear = @import("clear.zig");
|
|||||||
pub const gamemode = @import("gamemode.zig");
|
pub const gamemode = @import("gamemode.zig");
|
||||||
pub const help = @import("help.zig");
|
pub const help = @import("help.zig");
|
||||||
pub const invite = @import("invite.zig");
|
pub const invite = @import("invite.zig");
|
||||||
|
pub const kill = @import("kill.zig");
|
||||||
pub const time = @import("time.zig");
|
pub const time = @import("time.zig");
|
||||||
pub const tp = @import("tp.zig");
|
pub const tp = @import("tp.zig");
|
15
src/server/command/kill.zig
Normal file
15
src/server/command/kill.zig
Normal file
@ -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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user