From 780e7dc597866eba8735ca62496feb98687776af Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 4 May 2020 22:39:01 +0200 Subject: [PATCH] feat: add FakePlayer on Disconnect --- .../main/java/de/cliffbreak/varo/Varo.java | 7 +- .../cliffbreak/varo/commands/VaroCommand.java | 42 +++--- .../listeners/PlayerJoinQuitListener.java | 10 ++ .../cliffbreak/varo/managers/NPCManager.java | 133 ++++++++++++++++++ 4 files changed, 173 insertions(+), 19 deletions(-) create mode 100644 varo/src/main/java/de/cliffbreak/varo/managers/NPCManager.java diff --git a/varo/src/main/java/de/cliffbreak/varo/Varo.java b/varo/src/main/java/de/cliffbreak/varo/Varo.java index 3341ebc..fa24302 100644 --- a/varo/src/main/java/de/cliffbreak/varo/Varo.java +++ b/varo/src/main/java/de/cliffbreak/varo/Varo.java @@ -21,18 +21,23 @@ import de.cliffbreak.varo.listeners.PlayerClientOptionsChangeListener; import de.cliffbreak.varo.listeners.PlayerDeathListener; import de.cliffbreak.varo.listeners.PlayerJoinQuitListener; import de.cliffbreak.varo.listeners.PlayerPreLoginListener; +import de.cliffbreak.varo.managers.NPCManager; public class Varo extends JavaPlugin { public File configurationFile = new File("plugins/CliffbreakVaro", "config.yml"); public FileConfiguration config = YamlConfiguration.loadConfiguration(this.configurationFile); - public PlayerCache playerCache = new PlayerCache(this); + public NPCManager npcManager; + public PlayerCache playerCache; @Override public void onEnable() { getLogger().info("CliffbreakVaro is starting..."); + this.npcManager = new NPCManager(this); + this.playerCache = new PlayerCache(this); + // this.config.addDefault("Varo.Start", "TODO: StartDate"); this.config.addDefault("Varo.Bans", new ArrayList()); this.config.options().copyDefaults(true); diff --git a/varo/src/main/java/de/cliffbreak/varo/commands/VaroCommand.java b/varo/src/main/java/de/cliffbreak/varo/commands/VaroCommand.java index 30b9b0a..3b40793 100644 --- a/varo/src/main/java/de/cliffbreak/varo/commands/VaroCommand.java +++ b/varo/src/main/java/de/cliffbreak/varo/commands/VaroCommand.java @@ -1,10 +1,6 @@ package de.cliffbreak.varo.commands; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import com.destroystokyo.paper.Title; @@ -23,6 +19,7 @@ import org.json.simple.parser.ParseException; import de.cliffbreak.varo.Varo; import de.cliffbreak.varo.uitls.MessageUtils; +import de.cliffbreak.varo.uitls.WebUtils; public class VaroCommand implements CommandExecutor { @@ -41,23 +38,13 @@ public class VaroCommand implements CommandExecutor { if (args.length == 2 && args[0].equals("unban")) { @SuppressWarnings("unchecked") final ArrayList bans = (ArrayList) this.plugin.config.get("Varo.Bans"); - String url = "https://api.mojang.com/users/profiles/minecraft/" + args[1]; try { - HttpURLConnection connection = ((HttpURLConnection) (new URL(url)).openConnection()); - connection.setRequestMethod("GET"); - BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String inputLine; - StringBuffer content = new StringBuffer(); - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - in.close(); - connection.disconnect(); - if (content.toString().isEmpty()) { + String result = WebUtils.GET("https://api.mojang.com/users/profiles/minecraft/" + args[1]); + if (result.isEmpty()) { sender.sendMessage("§c§lFehler: §r§cDer Spieler wurde nicht gefunden."); return true; } - JSONObject uuidObject = (JSONObject) JSONValue.parseWithException(content.toString()); + JSONObject uuidObject = (JSONObject) JSONValue.parseWithException(result); if (!bans.contains(uuidObject.get("id").toString())) { sender.sendMessage("§c§lFehler: §r§cDer Spieler ist nicht gebannt."); return true; @@ -66,7 +53,7 @@ public class VaroCommand implements CommandExecutor { this.plugin.config.set("Varo.Bans", bans); this.plugin.saveConfiguration(); sender.sendMessage("§a§lErfolg: §r§aDer Spieler ist nun nicht mehr gebannt."); - } catch (ParseException | IOException e) { + } catch (IOException | ParseException e) { sender.sendMessage( "§c§lFehler:§r§c beim Verbinden mit der Mojang-API ist ein Fehler aufgetreten. Eventuell sind die Mojang Server down."); e.printStackTrace(); @@ -94,11 +81,30 @@ public class VaroCommand implements CommandExecutor { sender.sendMessage(MessageUtils.getRichTextComponent("§9" + "Team 08", "")); sender.sendMessage(MessageUtils.getRichTextComponent("§d" + "Team 09", "")); sender.sendMessage(MessageUtils.getRichTextComponent("§5" + "Team 10", "")); + } else if (args.length == 1 && args[0].equals("test")) { + if (sender instanceof Player) { + try { + plugin.npcManager.createClone((Player) sender, false); + } catch (IOException e) { + sender.sendMessage( + "§c§lFehler:§r§c beim Verbinden mit der Mojang-API ist ein Fehler aufgetreten. Eventuell sind die Mojang Server down."); + } + } else { + sender.sendMessage("§c§lFehler:§r§c Dieses Kommando kann nur durch einen Spieler ausgeführt werden."); + } + } else if (args.length == 1 && args[0].equals("testremove")) { + if (sender instanceof Player) { + plugin.npcManager.removeClone(((Player) sender)); + } else { + sender.sendMessage("§c§lFehler:§r§c Dieses Kommando kann nur durch einen Spieler ausgeführt werden."); + } } else { sender.sendMessage("\n§6========== §b§lCliffbreak.de VARO §r§6============\n "); sender.sendMessage("§a/varo start: §rStarte das Varo Projekt!"); sender.sendMessage("§a/varo unban [Spieler]: §rSpieler wieder auf den Server lassen"); sender.sendMessage("§a/varo teamcolors: §rZeige verfügbare Team-Farben an"); + sender.sendMessage("§a/varo test: §rSpawne einen Klon"); + sender.sendMessage("§a/varo testremove: §rLösche den Klon"); sender.sendMessage("\n§6=========================================="); } return true; diff --git a/varo/src/main/java/de/cliffbreak/varo/listeners/PlayerJoinQuitListener.java b/varo/src/main/java/de/cliffbreak/varo/listeners/PlayerJoinQuitListener.java index ec563c2..967378e 100644 --- a/varo/src/main/java/de/cliffbreak/varo/listeners/PlayerJoinQuitListener.java +++ b/varo/src/main/java/de/cliffbreak/varo/listeners/PlayerJoinQuitListener.java @@ -1,5 +1,6 @@ package de.cliffbreak.varo.listeners; +import java.io.IOException; import java.util.Stack; import com.destroystokyo.paper.event.server.PaperServerListPingEvent; @@ -26,6 +27,11 @@ public class PlayerJoinQuitListener implements Listener { @EventHandler() public void onPlayerQuit(final PlayerQuitEvent e) { e.setQuitMessage(null); + try { + plugin.npcManager.createClone(e.getPlayer(), true); + } catch (IOException ex) { + ex.printStackTrace(); + } Bukkit.broadcast( MessageUtils.getRichTextComponent(e.getPlayer().getName(), "§f hat den Server verlassen.", true)); } @@ -33,6 +39,8 @@ public class PlayerJoinQuitListener implements Listener { @EventHandler() public void onPlayerJoin(final PlayerJoinEvent e) { e.setJoinMessage(null); + plugin.npcManager.removeClone(e.getPlayer()); + plugin.npcManager.syncClones(e.getPlayer()); Bukkit.broadcast( MessageUtils.getRichTextComponent(e.getPlayer().getName(), "§f hat den Server betreten.", true)); e.getPlayer().sendMessage("\n§7§l#### §9Cliffbreak.de - §lVaro §r§9Changelog §7§l####\n \n" @@ -40,6 +48,8 @@ public class PlayerJoinQuitListener implements Listener { final Stack changes = new Stack(); + 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: §rUse Vanilla Hearts"); diff --git a/varo/src/main/java/de/cliffbreak/varo/managers/NPCManager.java b/varo/src/main/java/de/cliffbreak/varo/managers/NPCManager.java new file mode 100644 index 0000000..547712d --- /dev/null +++ b/varo/src/main/java/de/cliffbreak/varo/managers/NPCManager.java @@ -0,0 +1,133 @@ +package de.cliffbreak.varo.managers; + +import java.io.IOException; +import java.util.ArrayList; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +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; +import org.bukkit.entity.Player; +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; + +import de.cliffbreak.varo.Varo; +import de.cliffbreak.varo.uitls.WebUtils; +import net.minecraft.server.v1_15_R1.DataWatcherObject; +import net.minecraft.server.v1_15_R1.DataWatcherRegistry; +import net.minecraft.server.v1_15_R1.EntityPlayer; +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.PacketPlayOutNamedEntitySpawn; +import net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo; +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 players = new ArrayList(); + private Scoreboard scoreboard; + private Team afkTeam; + + public NPCManager(final Varo plugin) { + this.plugin = plugin; + this.scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + if (scoreboard.getTeam("afk") == null) { + + this.afkTeam = scoreboard.registerNewTeam("afk"); + } else { + this.afkTeam = scoreboard.getTeam("afk"); + } + this.afkTeam.setPrefix("§8§l[AFK] "); + this.afkTeam.setColor(ChatColor.GRAY); + } + + public void createClone(final Player player, final boolean shouldHaveAFKPrefix) throws IOException { + final Location location = player.getLocation(); + final MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer(); + final WorldServer nmsWorld = ((CraftWorld) player.getWorld()).getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), player.getName()); + try { + final String result = WebUtils.GET("https://sessionserver.mojang.com/session/minecraft/profile/" + + player.getUniqueId().toString().replace("-", "") + "?unsigned=false"); + final JSONObject resultObject = (JSONObject) JSONValue.parseWithException(result); + final JSONObject properties = (JSONObject) ((JSONArray) resultObject.get("properties")).get(0); + + gameProfile.getProperties().put("textures", new Property("textures", properties.get("value").toString(), + properties.get("signature").toString())); + } catch (IOException | ParseException e) { + plugin.getLogger().info(e.getMessage()); + throw new IOException(); + } + final EntityPlayer npc = new EntityPlayer(nmsServer, nmsWorld, gameProfile, + new PlayerInteractManager(nmsWorld)); + + npc.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + if (shouldHaveAFKPrefix) { + this.afkTeam.addEntry(npc.getName()); + } + + players.add(npc); + + for (Player connectionPlayer : Bukkit.getOnlinePlayers()) { + final PlayerConnection connection = ((CraftPlayer) connectionPlayer).getHandle().playerConnection; + connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, npc)); + connection.sendPacket(new PacketPlayOutNamedEntitySpawn(npc)); + connection.sendPacket( + new PacketPlayOutEntityHeadRotation(npc, (byte) ((location.getYaw() * 256.0F) / 360.0F))); + npc.getDataWatcher().set(new DataWatcherObject<>(16, DataWatcherRegistry.a), + (byte) plugin.playerCache.getSkinParts(player.getUniqueId().toString().replace("-", ""))); + npc.getDataWatcher().set(new DataWatcherObject<>(17, DataWatcherRegistry.a), + plugin.playerCache.getMainHandAsByte(player.getUniqueId().toString().replace("-", ""))); + connection.sendPacket(new PacketPlayOutEntityMetadata(npc.getId(), npc.getDataWatcher(), true)); + } + } + + public void syncClones(Player player) { + for (EntityPlayer npc : this.players) { + final PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; + connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, npc)); + connection.sendPacket(new PacketPlayOutNamedEntitySpawn(npc)); + connection.sendPacket(new PacketPlayOutEntityHeadRotation(npc, (byte) ((npc.yaw * 256.0F) / 360.0F))); + npc.getDataWatcher().set(new DataWatcherObject<>(16, DataWatcherRegistry.a), + (byte) plugin.playerCache.getSkinParts(player.getUniqueId().toString().replace("-", ""))); + npc.getDataWatcher().set(new DataWatcherObject<>(17, DataWatcherRegistry.a), + plugin.playerCache.getMainHandAsByte(player.getUniqueId().toString().replace("-", ""))); + connection.sendPacket(new PacketPlayOutEntityMetadata(npc.getId(), npc.getDataWatcher(), true)); + } + } + + 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); + if (npc.getUniqueID().equals(player.getUniqueId())) { + final WorldServer nmsWorld = ((CraftWorld) player.getWorld()).getHandle(); + nmsWorld.removeEntity(npc); + + for (Player connectionPlayer : Bukkit.getOnlinePlayers()) { + final PlayerConnection connection = ((CraftPlayer) connectionPlayer).getHandle().playerConnection; + connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, npc)); + connection.sendPacket(new PacketPlayOutEntityDestroy(npc.getId())); + } + this.players.remove(npc); + } + } + } +} \ No newline at end of file