From 74f421d368fff2902129a1d5129b5ea4fe742971 Mon Sep 17 00:00:00 2001 From: MrJeremyFisher <63616270+MrJeremyFisher@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:28:48 -0500 Subject: [PATCH] Make combat loggers real entities --- .../java/net/minelink/ctplus/NpcManager.java | 15 ++---- .../minelink/ctplus/listener/NpcListener.java | 2 + .../net/minelink/ctplus/nms/NpcPlayer.java | 47 ++++++++++++++--- .../ctplus/nms/NpcPlayerHelperImpl.java | 51 ++++++++++++------- 4 files changed, 81 insertions(+), 34 deletions(-) diff --git a/paper/src/main/java/net/minelink/ctplus/NpcManager.java b/paper/src/main/java/net/minelink/ctplus/NpcManager.java index efaa342..65e417c 100644 --- a/paper/src/main/java/net/minelink/ctplus/NpcManager.java +++ b/paper/src/main/java/net/minelink/ctplus/NpcManager.java @@ -1,8 +1,5 @@ package net.minelink.ctplus; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import net.minelink.ctplus.event.NpcDespawnEvent; import net.minelink.ctplus.event.NpcDespawnReason; import net.minelink.ctplus.task.NpcDespawnTask; @@ -11,9 +8,12 @@ import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.metadata.FixedMetadataValue; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + public final class NpcManager { private final CombatTagPlus plugin; @@ -53,13 +53,6 @@ public Npc spawn(Player player) { entity.getInventory().setArmorContents(player.getInventory().getArmorContents()); entity.addPotionEffects(player.getActivePotionEffects()); - // Should fix some visual glitches, such as health bars displaying zero - // TODO: Find another solution. This one causes the player to be added to the NMS PlayerList, that's not ideal. - entity.teleport(player, PlayerTeleportEvent.TeleportCause.PLUGIN); - - // Send equipment packets to nearby players - plugin.getNpcPlayerHelper().updateEquipment(entity); - entity.setMetadata("NPC", new FixedMetadataValue(plugin, true)); // Play a nice little effect indicating the NPC was spawned diff --git a/paper/src/main/java/net/minelink/ctplus/listener/NpcListener.java b/paper/src/main/java/net/minelink/ctplus/listener/NpcListener.java index 0f700d7..eb92832 100644 --- a/paper/src/main/java/net/minelink/ctplus/listener/NpcListener.java +++ b/paper/src/main/java/net/minelink/ctplus/listener/NpcListener.java @@ -46,6 +46,8 @@ public void despawnNpc(PlayerJoinEvent event) { // Attempt to despawn NPC Npc npc = plugin.getNpcManager().getSpawnedNpc(event.getPlayer().getUniqueId()); if (npc != null) { + event.getPlayer().teleport(npc.getEntity().getLocation()); + plugin.getNpcManager().despawn(npc); } } diff --git a/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayer.java b/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayer.java index 813afe4..5fdfec8 100644 --- a/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayer.java +++ b/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayer.java @@ -1,18 +1,32 @@ package net.minelink.ctplus.nms; +import com.destroystokyo.paper.profile.ProfileProperty; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; -import java.util.Map; -import java.util.UUID; +import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket; +import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import net.minelink.ctplus.CombatTagPlus; import net.minelink.ctplus.compat.base.NpcIdentity; import net.minelink.ctplus.compat.base.NpcNameGeneratorFactory; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_18_R2.CraftServer; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.entity.Arrow; import org.bukkit.entity.Player; +import java.util.Map; + public class NpcPlayer extends ServerPlayer { private NpcIdentity identity; @@ -29,15 +43,36 @@ public static NpcPlayer valueOf(Player player) { ServerLevel worldServer = ((CraftWorld) player.getWorld()).getHandle(); GameProfile gameProfile = new GameProfile(player.getUniqueId(), NpcNameGeneratorFactory.getNameGenerator().generate(player)); - for (Map.Entry entry: ((CraftPlayer) player).getProfile().getProperties().entries()) { - gameProfile.getProperties().put(entry.getKey(), entry.getValue()); - } - NpcPlayer npcPlayer = new NpcPlayer(minecraftServer, worldServer, gameProfile); npcPlayer.identity = new NpcIdentity(player); new NpcPlayerConnection(npcPlayer); + ProfileProperty property = player.getPlayerProfile().getProperties().iterator().next(); + String texture = property.getValue(); + String signature = property.getSignature(); + + npcPlayer.getGameProfile().getProperties().put("textures", new Property("textures", texture, signature)); + return npcPlayer; } + + @Override + public void tick(){ + super.tick(); + doTick(); + } + + @Override + public boolean hurt(DamageSource damageSource, float f) { + boolean damaged = super.hurt(damageSource, f); + if (damaged) { + if (this.hurtMarked) { + this.hurtMarked = false; + Bukkit.getScheduler().runTask(CombatTagPlus.getPlugin(CombatTagPlus.class), () -> NpcPlayer.this.hurtMarked = true); + } + } + return damaged; + } } + diff --git a/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayerHelperImpl.java b/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayerHelperImpl.java index 5873d30..8433f5f 100644 --- a/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayerHelperImpl.java +++ b/paper/src/main/java/net/minelink/ctplus/nms/NpcPlayerHelperImpl.java @@ -2,20 +2,18 @@ import com.google.common.collect.Lists; import com.mojang.datafixers.util.Pair; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.List; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtIo; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket; +import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.food.FoodData; @@ -29,31 +27,50 @@ import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; import org.bukkit.entity.Player; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.List; + public class NpcPlayerHelperImpl implements NpcPlayerHelper { @Override public Player spawn(Player player) { - NpcPlayer npcPlayer = NpcPlayer.valueOf(player); ServerLevel worldServer = ((CraftWorld) player.getWorld()).getHandle(); - Location l = player.getLocation(); - npcPlayer.spawnIn(worldServer); - npcPlayer.forceSetPositionRotation(l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch()); - npcPlayer.gameMode.setLevel(worldServer); - npcPlayer.spawnInvulnerableTime = 0; + NpcPlayer npcPlayer = NpcPlayer.valueOf(player); - for (ServerPlayer serverPlayer : MinecraftServer.getServer().getPlayerList().getPlayers()) { - if (serverPlayer instanceof NpcPlayer) continue; + Location location = player.getLocation(); - ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, npcPlayer); - serverPlayer.connection.send(packet); - } + npcPlayer.setPos(location.getX(), location.getY(), location.getZ()); + npcPlayer.setXRot(location.getYaw()); + npcPlayer.setYRot(location.getPitch()); + + npcPlayer.getBukkitEntity().setNoDamageTicks(0); worldServer.entityManager.getEntityGetter().get(player.getUniqueId()).remove(Entity.RemovalReason.DISCARDED); - worldServer.entityManager.addNewEntity(npcPlayer); + worldServer.addNewPlayer(npcPlayer); + showAll(npcPlayer, location); return npcPlayer.getBukkitEntity(); } + public static void showAll(ServerPlayer entityPlayer, Location location) { + ClientboundPlayerInfoPacket playerInfoAdd = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER); + ClientboundAddPlayerPacket namedEntitySpawn = new ClientboundAddPlayerPacket(entityPlayer); + ClientboundRotateHeadPacket headRotation = new ClientboundRotateHeadPacket(entityPlayer, (byte) ((location.getYaw() * 256f) / 360f)); + ClientboundPlayerInfoPacket playerInfoRemove = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER); + for (Player player : Bukkit.getOnlinePlayers()) { + ServerGamePacketListenerImpl connection = ((CraftPlayer) player).getHandle().connection; + connection.send(playerInfoAdd); + connection.send(namedEntitySpawn); + connection.send(headRotation); + connection.send(playerInfoRemove); + } + entityPlayer.getEntityData(). + set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) 0xFF); + } + @Override public void despawn(Player player) { ServerPlayer entity = ((CraftPlayer) player).getHandle();