diff --git a/src/main/java/net/nuggetmc/ai/PlayerAI.java b/src/main/java/net/nuggetmc/ai/PlayerAI.java index 5849ae5..1315884 100644 --- a/src/main/java/net/nuggetmc/ai/PlayerAI.java +++ b/src/main/java/net/nuggetmc/ai/PlayerAI.java @@ -30,8 +30,8 @@ public class PlayerAI extends JavaPlugin { instance = this; // Create Instances - this.handler = new CommandHandler(this); this.manager = new BotManager(this); + this.handler = new CommandHandler(this); // Register all the things this.registerEvents(); diff --git a/src/main/java/net/nuggetmc/ai/bot/Bot.java b/src/main/java/net/nuggetmc/ai/bot/Bot.java index 5d0a228..1eaeb24 100644 --- a/src/main/java/net/nuggetmc/ai/bot/Bot.java +++ b/src/main/java/net/nuggetmc/ai/bot/Bot.java @@ -4,12 +4,12 @@ import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import net.minecraft.server.v1_16_R3.*; import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.utils.MathUtils; import net.nuggetmc.ai.utils.MojangAPI; import net.nuggetmc.ai.utils.SteveUUID; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.attribute.Attribute; import org.bukkit.craftbukkit.v1_16_R3.CraftServer; import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity; @@ -35,7 +35,7 @@ public class Bot extends EntityPlayer { kbTicks = 0; } - public static Bot createBot(String name, Location loc, String skin) { + public static Bot createBot(Location loc, String name, String skin) { MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer(); WorldServer nmsWorld = ((CraftWorld) loc.getWorld()).getHandle(); @@ -107,11 +107,11 @@ public class Bot extends EntityPlayer { if (noDamageTicks > 0) --noDamageTicks; if (kbTicks > 0) --kbTicks; - Player playerNPC = this.getBukkitEntity(); - if (playerNPC.isDead()) return; + Player botPlayer = this.getBukkitEntity(); + if (botPlayer.isDead()) return; - double health = playerNPC.getHealth(); - double maxHealth = playerNPC.getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue(); + double health = botPlayer.getHealth(); + double maxHealth = botPlayer.getHealthScale(); double amount; if (health < maxHealth - regenAmount) { @@ -120,7 +120,7 @@ public class Bot extends EntityPlayer { amount = maxHealth; } - playerNPC.setHealth(amount); + botPlayer.setHealth(amount); updateLocation(); } @@ -184,6 +184,11 @@ public class Bot extends EntityPlayer { } public void despawn() { + for (Player player : Bukkit.getOnlinePlayers()) { + PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; + connection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, this)); + } + getBukkitEntity().remove(); } @@ -232,13 +237,13 @@ public class Bot extends EntityPlayer { Location loc1 = player.getLocation(); Location loc2 = entity.getLocation(); - kb(player, loc1, loc2); + kb(loc1, loc2); } return damaged; } - private void kb(Player playerBot, Location loc1, Location loc2) { + private void kb(Location loc1, Location loc2) { Vector diff = loc1.toVector().subtract(loc2.toVector()).normalize(); diff.multiply(0.25); diff.setY(0.5); @@ -252,17 +257,17 @@ public class Bot extends EntityPlayer { } public void faceLocation(Location loc) { - try { - CraftPlayer playerBot = this.getBukkitEntity(); - Vector dir = loc.toVector().subtract(playerBot.getLocation().toVector()).normalize(); - Location facing = playerBot.getLocation().setDirection(dir); - playerBot.teleport(facing); + CraftPlayer botPlayer = getBukkitEntity(); + Vector dir = loc.toVector().subtract(botPlayer.getLocation().toVector()); - for (Player player : Bukkit.getOnlinePlayers()) { - PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; - connection.sendPacket(new PacketPlayOutEntityHeadRotation(playerBot.getHandle(), (byte) (facing.getYaw() * 256 / 360))); - } - } catch (IllegalArgumentException ignored) { } + float[] vals = MathUtils.fetchYawPitch(dir); + + setYawPitch(vals[0], vals[1]); + + for (Player player : Bukkit.getOnlinePlayers()) { + PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; + connection.sendPacket(new PacketPlayOutEntityHeadRotation(botPlayer.getHandle(), (byte) (vals[0] * 256 / 360f))); + } } @Override diff --git a/src/main/java/net/nuggetmc/ai/bot/BotManager.java b/src/main/java/net/nuggetmc/ai/bot/BotManager.java index 6687077..586f204 100644 --- a/src/main/java/net/nuggetmc/ai/bot/BotManager.java +++ b/src/main/java/net/nuggetmc/ai/bot/BotManager.java @@ -2,10 +2,16 @@ package net.nuggetmc.ai.bot; import net.minecraft.server.v1_16_R3.PlayerConnection; import net.nuggetmc.ai.PlayerAI; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.World; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.util.Vector; import java.util.HashSet; import java.util.Set; @@ -28,6 +34,35 @@ public class BotManager implements Listener { this.plugin = plugin; } + public void createBots(Player sender, String name, String skin, int n) { + long timestamp = System.currentTimeMillis(); + + if (n < 1) n = 1; + + World world = sender.getWorld(); + Location loc = sender.getLocation(); + + if (name.length() > 16) name = name.substring(0, 16); + if (skin != null && skin.length() > 16) skin = skin.substring(0, 16); + + sender.sendMessage("Creating " + (n == 1 ? "new bot" : ChatColor.RED + String.valueOf(n) + ChatColor.RESET + " new bots") + + " with name " + ChatColor.GREEN + name + + (skin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skin) + ChatColor.RESET + "..."); + + skin = skin == null ? name : skin; + + for (int i = 0; i < n; i++) { + Bot bot = Bot.createBot(loc, name, skin); + if (i > 0) bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(0.4)); + } + + world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5); + + double time = (System.currentTimeMillis() - timestamp) / 1000D; + + sender.sendMessage("Process completed (" + ChatColor.RED + time + "s" + ChatColor.RESET + ")."); + } + public void reset() { for (Bot bot : bots) { bot.despawn(); diff --git a/src/main/java/net/nuggetmc/ai/commands/commands/PlayerAICommand.java b/src/main/java/net/nuggetmc/ai/commands/commands/PlayerAICommand.java index 92f0c1a..450fa6f 100644 --- a/src/main/java/net/nuggetmc/ai/commands/commands/PlayerAICommand.java +++ b/src/main/java/net/nuggetmc/ai/commands/commands/PlayerAICommand.java @@ -11,15 +11,24 @@ import net.nuggetmc.ai.commands.CommandHandler; import net.nuggetmc.ai.commands.CommandInstance; import net.nuggetmc.ai.utils.ChatUtils; import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.util.Vector; import java.text.NumberFormat; import java.util.Locale; public class PlayerAICommand extends CommandInstance { + private BotManager manager; + public PlayerAICommand(CommandHandler commandHandler) { super(commandHandler); + + this.manager = PlayerAI.getInstance().getManager(); } @Command(name = "", desc = "The PlayerAI main command.") @@ -38,25 +47,36 @@ public class PlayerAICommand extends CommandInstance { @Command(name = "create", desc = "Create bots.", usage = " [skin]") @Require("playerai.manage") public void createBotCommand(@Sender Player sender, String name, @OptArg String skin) { - // make 16 character limit on name - Bot.createBot(name, sender.getLocation(), skin == null ? name : skin); + manager.createBots(sender, name, skin, 1); + } + + @Command(name = "multi", desc = "Create multiple bots at once.", usage = " [skin]") + @Require("playerai.manage") + public void multiBotCommand(@Sender Player sender, int n, String name, @OptArg String skin) { + manager.createBots(sender, name, skin, n); } @Command(name = "debug", desc = "Debug bot stats.") @Require("playerai.manage") public void debugCommand(@Sender Player sender) { // This will be used for miscellaneous code for testing as the plugin is worked on + + Location loc = sender.getLocation(); + + for (Bot bot : PlayerAI.getInstance().getManager().fetch()) { + bot.faceLocation(loc); + } } @Command(name = "info", desc = "Information about loaded bots.") @Require("playerai.manage") - public void infoCommand(@Sender Player sender) { + public void infoCommand(@Sender Player player) { // This will be the future GUI where players can view information about every loaded bot } @Command(name = "reset", desc = "Remove all loaded bots.") @Require("playerai.manage") - public void resetCommand(@Sender Player sender) { + public void resetCommand(@Sender CommandSender sender) { sender.sendMessage("Removing every bot..."); BotManager manager = PlayerAI.getInstance().getManager(); diff --git a/src/main/java/net/nuggetmc/ai/utils/MathUtils.java b/src/main/java/net/nuggetmc/ai/utils/MathUtils.java new file mode 100644 index 0000000..01ab353 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/utils/MathUtils.java @@ -0,0 +1,30 @@ +package net.nuggetmc.ai.utils; + +import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; + +public class MathUtils { + + public static float[] fetchYawPitch(Vector dir) { + double x = dir.getX(); + double z = dir.getZ(); + + float[] out = new float[2]; + + if (x == 0.0D && z == 0.0D) { + out[1] = (float) (dir.getY() > 0.0D ? -90 : 90); + } + + else { + double theta = Math.atan2(-x, z); + out[0] = (float) Math.toDegrees((theta + 6.283185307179586D) % 6.283185307179586D); + + double x2 = NumberConversions.square(x); + double z2 = NumberConversions.square(z); + double xz = Math.sqrt(x2 + z2); + out[1] = (float) Math.toDegrees(Math.atan(-dir.getY() / xz)); + } + + return out; + } +} diff --git a/src/main/java/net/nuggetmc/ai/utils/MojangAPI.java b/src/main/java/net/nuggetmc/ai/utils/MojangAPI.java index 8368226..b09460c 100644 --- a/src/main/java/net/nuggetmc/ai/utils/MojangAPI.java +++ b/src/main/java/net/nuggetmc/ai/utils/MojangAPI.java @@ -6,12 +6,25 @@ import com.google.gson.JsonParser; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; +import java.util.HashMap; +import java.util.Map; public class MojangAPI { - // Eventually create some sort of cache that stores the skin data so it doesn't have to keep pulling from the API - // CATCHING NULLPOINTEREXCEPTION BAD!!!! eventually fix from the getAsJsonObject thingy + private static final Map CACHE = new HashMap<>(); + public static String[] getSkin(String name) { + if (CACHE.containsKey(name)) { + return CACHE.get(name); + } + + String[] values = pullFromAPI(name); + CACHE.put(name, values); + return values; + } + + // CATCHING NULLPOINTEREXCEPTION BAD!!!! eventually fix from the getAsJsonObject thingy + public static String[] pullFromAPI(String name) { try { String uuid = new JsonParser().parse(new InputStreamReader(new URL("https://api.mojang.com/users/profiles/minecraft/" + name) .openStream())).getAsJsonObject().get("id").getAsString(); @@ -19,7 +32,7 @@ public class MojangAPI { .parse(new InputStreamReader(new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false") .openStream())).getAsJsonObject().get("properties").getAsJsonArray().get(0).getAsJsonObject(); return new String[] {property.get("value").getAsString(), property.get("signature").getAsString()}; - } catch (IOException | NullPointerException e) { + } catch (IOException | IllegalStateException e) { return null; } }