From 7e3bc22bc4055e48689f76107e12a578077f076d Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Mon, 27 Apr 2020 21:12:42 +0200 Subject: [PATCH] Chat messages when player dies and cleaner damage types --- .../net/minestom/server/entity/Player.java | 41 ++++++++++ .../server/entity/damage/DamageType.java | 31 +++++-- .../server/entity/damage/EntityDamage.java | 20 +++++ .../entity/damage/EntityProjectileDamage.java | 26 ++++++ .../packet/server/ServerPacketIdentifier.java | 2 +- .../packet/server/play/CombatEventPacket.java | 80 +++++++++++++++++++ 6 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/minestom/server/entity/damage/EntityDamage.java create mode 100644 src/main/java/net/minestom/server/entity/damage/EntityProjectileDamage.java create mode 100644 src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index c7d92d16c..d6ad73afb 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1,5 +1,6 @@ package net.minestom.server.entity; +import club.thectm.minecraft.text.TextBuilder; import club.thectm.minecraft.text.TextObject; import com.google.gson.JsonObject; import net.minestom.server.MinecraftServer; @@ -76,6 +77,11 @@ public class Player extends LivingEntity { private Team team; private BelowNameScoreboard belowNameScoreboard; + /** + * Last damage source to hit this player, used to display the death message. + */ + private DamageType lastDamageSource; + // Abilities private boolean invulnerable; private boolean flying; @@ -109,6 +115,14 @@ public class Player extends LivingEntity { setCanPickupItem(true); // By default } + @Override + public void damage(DamageType type, float value) { + if(!isImmune(type)) { + lastDamageSource = type; + } + super.damage(type, value); + } + @Override public void update() { @@ -229,6 +243,33 @@ public class Player extends LivingEntity { } + @Override + public void kill() { + if(!isDead()) { + // send death message to player + TextObject deathMessage; + if(lastDamageSource != null) { + deathMessage = lastDamageSource.buildDeathMessage(); + } else { // may happen if killed by the server without applying damage + deathMessage = TextBuilder.of("Killed by poor programming.").build(); + } + CombatEventPacket deathPacket = CombatEventPacket.death(this, Optional.empty(), deathMessage); + playerConnection.sendPacket(deathPacket); + + // send death message to chat + TextObject chatMessage; + if(lastDamageSource != null) { + chatMessage = lastDamageSource.buildChatMessage(this); + } else { // may happen if killed by the server without applying damage + chatMessage = TextBuilder.of(getUsername()+" was killed by poor programming.").build(); + } + MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(player -> { + player.sendMessage(chatMessage); + }); + } + super.kill(); + } + @Override public void spawn() { diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index 645346282..9b0342600 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -1,5 +1,8 @@ package net.minestom.server.entity.damage; +import club.thectm.minecraft.text.TextBuilder; +import club.thectm.minecraft.text.TextObject; +import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; /** @@ -7,13 +10,31 @@ import net.minestom.server.entity.Player; */ public class DamageType { - // TODO + public static final DamageType VOID = new DamageType("void"); + public static final DamageType GRAVITY = new DamageType("gravity"); + private final String identifier; - public static final DamageType VOID = new DamageType(); - public static final DamageType PLAYER = new DamageType(); + public DamageType(String identifier) { + this.identifier = identifier; + } + + public String getIdentifier() { + return identifier; + } + + public TextObject buildChatMessage(Player killed) { + return TextBuilder.of(killed.getUsername()+" was killed by damage of type "+identifier).build(); + } + + public TextObject buildDeathMessage() { + return TextBuilder.of("Killed by damage of type "+identifier).build(); + } public static DamageType fromPlayer(Player player) { - // TODO - return PLAYER; + return new EntityDamage(player); + } + + public static DamageType fromProjectile(Entity shooter, Entity projectile) { + return new EntityProjectileDamage(shooter, projectile); } } diff --git a/src/main/java/net/minestom/server/entity/damage/EntityDamage.java b/src/main/java/net/minestom/server/entity/damage/EntityDamage.java new file mode 100644 index 000000000..76e033630 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/damage/EntityDamage.java @@ -0,0 +1,20 @@ +package net.minestom.server.entity.damage; + +import net.minestom.server.entity.Entity; + +/** + * Represents damage inflicted by an entity + */ +public class EntityDamage extends DamageType { + + private final Entity source; + + public EntityDamage(Entity source) { + super("entity_source"); + this.source = source; + } + + public Entity getSource() { + return source; + } +} diff --git a/src/main/java/net/minestom/server/entity/damage/EntityProjectileDamage.java b/src/main/java/net/minestom/server/entity/damage/EntityProjectileDamage.java new file mode 100644 index 000000000..1d9c2adb7 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/damage/EntityProjectileDamage.java @@ -0,0 +1,26 @@ +package net.minestom.server.entity.damage; + +import net.minestom.server.entity.Entity; + +/** + * Represents damage inflicted by an entity, via a projectile + */ +public class EntityProjectileDamage extends DamageType { + + private Entity shooter; + private final Entity projectile; + + public EntityProjectileDamage(Entity shooter, Entity projectile) { + super("projectile_source"); + this.shooter = shooter; + this.projectile = projectile; + } + + public Entity getProjectile() { + return projectile; + } + + public Entity getShooter() { + return shooter; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java index cd954437b..5861115cf 100644 --- a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java +++ b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java @@ -53,7 +53,7 @@ public class ServerPacketIdentifier { public static final int OPEN_SIGN_EDITOR = 0x30; public static final int CRAFT_RECIPE_RESPONSE = 0x31; public static final int PLAYER_ABILITIES = 0x32; - public static final int COMBAT_EVENT = 0x33; // Do not seem to be used by the client + public static final int COMBAT_EVENT = 0x33; public static final int PLAYER_INFO = 0x34; public static final int FACE_PLAYER = 0x35; public static final int PLAYER_POSITION_AND_LOOK = 0x36; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java new file mode 100644 index 000000000..9deac0451 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java @@ -0,0 +1,80 @@ +package net.minestom.server.network.packet.server.play; + +import club.thectm.minecraft.text.TextObject; +import net.minestom.server.chat.Chat; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.PacketWriter; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; + +import java.util.Optional; + +/** + * Packet sent during combat to a player. + * Only death is supported for the moment (other events are ignored anyway as of 1.15.2) + */ +public class CombatEventPacket implements ServerPacket { + + private EventType type; + private int duration; + private int opponent; + private int playerID; + private TextObject deathMessage; + + private CombatEventPacket() {} + + public static CombatEventPacket enter() { + CombatEventPacket packet = new CombatEventPacket(); + packet.type = EventType.ENTER_COMBAT; + return packet; + } + + public static CombatEventPacket end(int durationInTicks, Optional opponent) { + CombatEventPacket packet = new CombatEventPacket(); + packet.type = EventType.END_COMBAT; + packet.duration = durationInTicks; + packet.opponent = opponent.map(Entity::getEntityId).orElse(-1); + return packet; + } + + public static CombatEventPacket death(Player player, Optional killer, TextObject message) { + CombatEventPacket packet = new CombatEventPacket(); + packet.type = EventType.DEATH; + packet.playerID = player.getEntityId(); + packet.opponent = killer.map(Entity::getEntityId).orElse(-1); + packet.deathMessage = message; + return packet; + } + + @Override + public void write(PacketWriter writer) { + writer.writeVarInt(type.ordinal()); + switch (type) { + case ENTER_COMBAT: + // nothing to add + break; + + case END_COMBAT: + writer.writeVarInt(duration); + writer.writeInt(opponent); + break; + + case DEATH: + writer.writeVarInt(playerID); + writer.writeInt(opponent); + writer.writeSizedString(deathMessage.toJson().toString()); + break; + } + } + + @Override + public int getId() { + return ServerPacketIdentifier.COMBAT_EVENT; + } + + public enum EventType { + ENTER_COMBAT, END_COMBAT, // both ignored by Notchian client + DEATH, + } +}