feat: allow FakePlayer to be attackted

This commit is contained in:
Simon Giesel 2020-05-06 16:50:08 +02:00
parent d4c6af4633
commit 44ef00d397
7 changed files with 265 additions and 67 deletions

View file

@ -4,15 +4,18 @@
"java.jdt.ls.vmargs": "-Dfile.encoding=UTF-8",
"java.configuration.updateBuildConfiguration": "automatic",
"cSpell.words": [
"Gamemode",
"Gamerules",
"Minecraft",
"Mojang",
"cliffbreak",
"mkdir",
"npcs",
"teamcolors",
"testremove",
"unban",
"uninject",
"varo"
],
"java.format.settings.url": "eclipse-formatter.xml",
"java.format.settings.url": "eclipse-formatter.xml"
}

View file

@ -17,7 +17,7 @@ import de.cliffbreak.varo.commands.VaroCommand;
import de.cliffbreak.varo.listeners.BannedItemListener;
import de.cliffbreak.varo.listeners.ChatListener;
import de.cliffbreak.varo.listeners.CreatureSpawnListener;
import de.cliffbreak.varo.listeners.EntityDamageByEntityListener;
import de.cliffbreak.varo.listeners.PlayerInteractNPCListener;
import de.cliffbreak.varo.listeners.EntityRegainHealthListener;
import de.cliffbreak.varo.listeners.PlayerClientOptionsChangeListener;
import de.cliffbreak.varo.listeners.PlayerDeathListener;
@ -41,6 +41,7 @@ public class Varo extends JavaPlugin {
this.playerCache = new PlayerCache(this);
// this.config.addDefault("Varo.Start", "TODO: StartDate");
this.config.addDefault("Varo.Debug", false);
this.config.addDefault("Varo.Bans", new ArrayList<String>());
this.config.options().copyDefaults(true);
this.saveConfiguration();
@ -53,13 +54,16 @@ public class Varo extends JavaPlugin {
getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this);
getServer().getPluginManager().registerEvents(new BannedItemListener(), this);
getServer().getPluginManager().registerEvents(new CreatureSpawnListener(this), this);
getServer().getPluginManager().registerEvents(new PlayerInteractNPCListener(this), this);
getCommand("varo").setExecutor(new VaroCommand(this));
for (World world : getServer().getWorlds()) {
if (world.getEnvironment() == Environment.NORMAL) {
world.setDifficulty(Difficulty.HARD);
// world.setDifficulty(Difficulty.HARD);
world.setDifficulty(Difficulty.PEACEFUL); // TODO: REMOVE AFTER DEBUG!!
world.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false);
world.setGameRule(GameRule.REDUCED_DEBUG_INFO, true);
}
}
getServer().removeRecipe(NamespacedKey.minecraft("fishing_rod"));
@ -68,7 +72,6 @@ public class Varo extends JavaPlugin {
@Override
public void onDisable() {
getLogger().info("Stopping CliffbreakVaro!");
saveConfiguration();
this.playerCache.shutdown();
}

View file

@ -0,0 +1,61 @@
package de.cliffbreak.varo.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class PlayerInteractNPCEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Type type;
private final int id;
private final double damage;
private final boolean isCritical;
public PlayerInteractNPCEvent(Type type, int id, double damage, boolean isCritical) {
super(true);
this.type = type;
this.id = id;
this.damage = damage;
this.isCritical = isCritical;
}
/**
* @return InteractionType of the Event
*/
public Type getType() {
return type;
}
/**
* @return EntityId of the Event
*/
public int getId() {
return id;
}
/**
* @return Damage from Player to Entity (only use if Type=ATTACK)
*/
public double getDamage() {
return damage;
}
/**
* @return Return if Player made a critical attack
*/
public boolean getIsCritical() {
return isCritical;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public enum Type {
ATTACK, INTERACT,
}
}

View file

@ -0,0 +1,26 @@
package de.cliffbreak.varo.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import de.cliffbreak.varo.Varo;
import de.cliffbreak.varo.events.PlayerInteractNPCEvent;
import de.cliffbreak.varo.events.PlayerInteractNPCEvent.Type;
public class PlayerInteractNPCListener implements Listener {
private Varo plugin;
public PlayerInteractNPCListener(Varo plugin) {
this.plugin = plugin;
}
@EventHandler
public void onEntityDamageByEntity(PlayerInteractNPCEvent e) {
// plugin.getLogger().info(e.getType().toString() + " : " + e.getId() + " : " + e.getDamage());
if (e.getType().equals(Type.ATTACK)) {
plugin.npcManager.addDamage(e.getId(), e.getDamage(), e.getIsCritical());
}
}
}

View file

@ -1,6 +1,5 @@
package de.cliffbreak.varo.listeners;
import java.io.IOException;
import java.util.Stack;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
@ -15,6 +14,7 @@ import org.bukkit.scheduler.BukkitRunnable;
import de.cliffbreak.varo.Varo;
import de.cliffbreak.varo.uitls.MessageUtils;
import de.cliffbreak.varo.uitls.PacketReader;
public class PlayerJoinQuitListener implements Listener {
@ -24,7 +24,7 @@ public class PlayerJoinQuitListener implements Listener {
this.plugin = plugin;
}
@EventHandler()
@EventHandler
public void onPlayerQuit(final PlayerQuitEvent e) {
e.setQuitMessage(null);
plugin.npcManager.createClone(e.getPlayer(), true);
@ -32,22 +32,32 @@ public class PlayerJoinQuitListener implements Listener {
MessageUtils.getRichTextComponent(e.getPlayer().getName(), "§f hat den Server verlassen.", true));
}
@EventHandler()
@EventHandler
public void onPlayerJoin(final PlayerJoinEvent e) {
e.setJoinMessage(null);
plugin.npcManager.removeClone(e.getPlayer());
plugin.npcManager.syncClones(e.getPlayer());
// Inject PacketReader
PacketReader packetReader = new PacketReader(plugin, e.getPlayer());
packetReader.inject();
Bukkit.broadcast(
MessageUtils.getRichTextComponent(e.getPlayer().getName(), "§f hat den Server betreten.", true));
if (plugin.config.getBoolean("Varo.Debug")) {
e.getPlayer().sendMessage("\n§7§l#### §9Cliffbreak.de - §lVaro §r§9Changelog §7§l####\n \n"
+ " §cWarning: §r§c Plugin is running in DEBUG mode!\n ");
final Stack<String> changes = new Stack<String>();
changes.push(" §7• §r§lADD: §rFakePlayer is no attackable (can also be killed)");
changes.push(" §7• §r§lADD: §rAdd a FakePlayer if Player is logged out");
changes.push(" §7• §r§lADD: §rAdd /varo test && /varo testremove");
changes.push(" §7• §r§lFIX: §r/varo Command is only usable by Server Operators now");
changes.push(" §7• §r§lADD: §rDisallow special Items (Enchanted Golden Apple, Fishing Rod, Totem of Undying)");
changes.push(
" §7• §r§lADD: §rDisallow special Items (Enchanted Golden Apple, Fishing Rod, Totem of Undying)");
changes.push(" §7• §r§lADD: §rUse Vanilla Hearts");
changes.push(" §7• §r§lADD: §rDisable Fishing Rod Crafting Recipe");
changes.push(" §7• §r§lFIX: §rCheck for Ban in AsyncPreLoginEvent");
@ -75,8 +85,9 @@ public class PlayerJoinQuitListener implements Listener {
}
}.runTaskTimer(plugin, 20, 20);
}
}
@EventHandler()
@EventHandler
public void onServerListPing(final ServerListPingEvent event) {
if (event instanceof PaperServerListPingEvent) {
handlePaperServerListPing((PaperServerListPingEvent) event);

View file

@ -9,6 +9,8 @@ import com.mojang.authlib.properties.Property;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
@ -18,7 +20,6 @@ import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
@ -31,17 +32,18 @@ import net.minecraft.server.v1_15_R1.MinecraftServer;
import net.minecraft.server.v1_15_R1.PacketPlayOutEntityDestroy;
import net.minecraft.server.v1_15_R1.PacketPlayOutEntityHeadRotation;
import net.minecraft.server.v1_15_R1.PacketPlayOutEntityMetadata;
import net.minecraft.server.v1_15_R1.PacketPlayOutEntityStatus;
import net.minecraft.server.v1_15_R1.PacketPlayOutNamedEntitySpawn;
import net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo;
import net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import net.minecraft.server.v1_15_R1.PlayerConnection;
import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.WorldServer;
import net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
public class NPCManager {
private final Varo plugin;
private final ArrayList<EntityPlayer> players = new ArrayList<EntityPlayer>();
private final ArrayList<EntityPlayer> npcs = new ArrayList<EntityPlayer>();
private Scoreboard scoreboard;
private Team afkTeam;
@ -74,16 +76,18 @@ public class NPCManager {
} catch (IOException | ParseException e) {
plugin.getLogger().info(e.getMessage());
}
final EntityPlayer npc = new EntityPlayer(nmsServer, nmsWorld, gameProfile,
new PlayerInteractManager(nmsWorld));
final PlayerInteractManager playerInteractManager = new PlayerInteractManager(nmsWorld);
final EntityPlayer npc = new EntityPlayer(nmsServer, nmsWorld, gameProfile, playerInteractManager);
npc.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
npc.setHealth((float) player.getHealth());
if (shouldHaveAFKPrefix) {
this.afkTeam.addEntry(npc.getName());
}
players.add(npc);
npcs.add(npc);
for (Player connectionPlayer : Bukkit.getOnlinePlayers()) {
final PlayerConnection connection = ((CraftPlayer) connectionPlayer).getHandle().playerConnection;
@ -105,7 +109,7 @@ public class NPCManager {
}
public void syncClones(Player player) {
for (EntityPlayer npc : this.players) {
for (EntityPlayer npc : this.npcs) {
final PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, npc));
connection.sendPacket(new PacketPlayOutNamedEntitySpawn(npc));
@ -118,10 +122,50 @@ public class NPCManager {
}
}
public boolean isNPC(int id) {
for (EntityPlayer npc : this.npcs) {
if (npc.getId() == id)
return true;
}
return false;
}
public void addDamage(int id, double damage, boolean isCritical) {
for (EntityPlayer npc : this.npcs) {
if (npc.getId() == id) {
npc.setHealth((float) (npc.getHealth() - damage));
World world = npc.getWorld().getWorld();
Location loc = new Location(world, npc.locX(), npc.locY(), npc.locZ());
if (npc.getHealth() > 0.0F) {
for (Player connectionPlayer : Bukkit.getOnlinePlayers()) {
final PlayerConnection connection = ((CraftPlayer) connectionPlayer)
.getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutEntityStatus(npc, (byte) 2)); // Send HURT (2) Animation
}
world.playSound(loc, Sound.ENTITY_PLAYER_HURT, 1.0F, 1.0F);
world.playSound(loc, Sound.ENTITY_PLAYER_ATTACK_STRONG, 1.0F, 1.0F);
if (isCritical)
world.playSound(loc, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0F, 1.0F);
} else {
for (Player connectionPlayer : Bukkit.getOnlinePlayers()) {
final PlayerConnection connection = ((CraftPlayer) connectionPlayer)
.getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutEntityMetadata(npc.getId(), npc.getDataWatcher(), true));
}
if (isCritical)
world.playSound(loc, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0F, 1.0F);
world.playSound(loc, Sound.ENTITY_PLAYER_DEATH, 1.0F, 1.0F);
}
}
}
}
public void removeClone(Player player) {
this.afkTeam.removeEntry(player.getName());
for (int i = 0; i < this.players.size(); i++) {
EntityPlayer npc = this.players.get(i);
for (int i = 0; i < this.npcs.size(); i++) {
EntityPlayer npc = this.npcs.get(i);
if (npc.getUniqueID().equals(player.getUniqueId())) {
final WorldServer nmsWorld = ((CraftWorld) player.getWorld()).getHandle();
nmsWorld.removeEntity(npc);
@ -130,7 +174,7 @@ public class NPCManager {
final PlayerConnection connection = ((CraftPlayer) connectionPlayer).getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutEntityDestroy(npc.getId()));
}
this.players.remove(npc);
this.npcs.remove(npc);
}
}
}

View file

@ -1,23 +1,37 @@
package de.cliffbreak.varo.uitls;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import net.minecraft.server.v1_15_R1.Packet;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import de.cliffbreak.varo.Varo;
import de.cliffbreak.varo.events.PlayerInteractNPCEvent;
import de.cliffbreak.varo.events.PlayerInteractNPCEvent.Type;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import net.minecraft.server.v1_15_R1.AttributeModifier;
import net.minecraft.server.v1_15_R1.EnumItemSlot;
import net.minecraft.server.v1_15_R1.GenericAttributes;
import net.minecraft.server.v1_15_R1.ItemStack;
import net.minecraft.server.v1_15_R1.Packet;
public class PacketReader {
Player player;
Channel channel;
private Varo plugin;
private Player player;
private Channel channel;
public PacketReader(final Player player) {
public PacketReader(final Varo plugin, final Player player) {
this.plugin = plugin;
this.player = player;
}
@ -40,21 +54,57 @@ public class PacketReader {
}
}
// https://wiki.vg/Protocol#Interact_Entity
public void readPacket(final Packet<?> packet) {
if (packet.getClass().getSimpleName().equalsIgnoreCase("PacketPlayInUseEntity")) {
final int id = (Integer) getValue(packet, "a");
System.out.println(getValue(packet, "action").toString());
System.out.println(getValue(packet, "a").toString());
// if (Main.npc.getEntityID() == id) {
// if (getValue(packet, "action").toString().equalsIgnoreCase("ATTACK")) {
// Main.npc.animation(1);
// } else if (getValue(packet, "action").toString().equalsIgnoreCase("INTERACT")) {
// }
// }
int id = (int) getValue(packet, "a");
if (!plugin.npcManager.isNPC(id)) {
return;
}
try {
Type type = Type.valueOf(getValue(packet, "action").toString());
PlayerInteractNPCEvent event = new PlayerInteractNPCEvent(type, id, calculateDamage(this.player),
isCriticalHit(this.player));
Bukkit.getServer().getPluginManager().callEvent(event);
} catch (Exception e) {
// noop()
}
}
}
private double calculateDamage(Player player) {
double damage = 1.0; // Default 1.0 Damage (by hand)
ItemStack nmsItemStack = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand());
Collection<AttributeModifier> attributes = nmsItemStack.getItem().a(EnumItemSlot.MAINHAND)
.get(GenericAttributes.ATTACK_DAMAGE.getName());
for (AttributeModifier am : attributes) {
damage += am.getAmount();
}
if (isCriticalHit(player)) {
damage = damage * 1.5;
}
damage = damage * player.getAttackCooldown();
Map<Enchantment, Integer> enchantments = player.getInventory().getItemInMainHand().getEnchantments();
for (Map.Entry<Enchantment, Integer> enchantment : enchantments.entrySet()) {
// TODO: Add more enchantments?
if (enchantment.getKey().equals(Enchantment.DAMAGE_ALL))
damage += 0.5 * (enchantment.getValue() + 1);
}
return damage;
}
private boolean isCriticalHit(Player player) {
return player.getFallDistance() > 0.0F && !player.isOnGround() && !player.getLocation().getBlock().isLiquid()
&& player.getPotionEffect(PotionEffectType.BLINDNESS) == null && player.getVehicle() == null
&& !player.isSprinting() && player.getAttackCooldown() > 0.848F;
}
public void setValue(final Object obj, final String name, final Object value) {