From 2a8059f8386c9d46372c5100b5a6cebcfff06ecf Mon Sep 17 00:00:00 2001 From: batchprogrammer314 Date: Wed, 21 Jul 2021 13:52:21 -0500 Subject: [PATCH] project rebrand --- README.md | 6 +- PlayerAI.iml => TerminatorPlus.iml | 0 pom.xml | 4 +- .../ai/{PlayerAI.java => TerminatorPlus.java} | 8 +- src/main/java/net/nuggetmc/ai/bot/Bot.java | 43 ++++- .../java/net/nuggetmc/ai/bot/BotManager.java | 13 +- .../java/net/nuggetmc/ai/bot/agent/Agent.java | 7 +- .../bot/agent/legacyagent/EnumTargetGoal.java | 16 +- .../ai/bot/agent/legacyagent/LegacyAgent.java | 12 +- .../agent/legacyagent/LegacyBlockCheck.java | 6 +- .../ai/bot/agent/legacyagent/ai/BotData.java | 47 ++++++ .../agent/legacyagent/ai/NeuralNetwork.java | 20 +++ .../agent/legacyagent/ai/NodeConnections.java | 47 ++++++ .../nuggetmc/ai/command/CommandHandler.java | 32 +++- .../ai/command/commands/AICommand.java | 40 +++++ .../ai/command/commands/BotCommand.java | 145 ++++++++++++++++ .../ai/command/commands/MainCommand.java | 159 +++++------------- .../java/net/nuggetmc/ai/utils/Debugger.java | 93 ++++++++-- .../java/net/nuggetmc/ai/utils/MathUtils.java | 4 + .../net/nuggetmc/ai/utils/PlayerUtils.java | 43 +++++ .../net/nuggetmc/ai/utils/StringUtils.java | 7 + src/main/resources/plugin.yml | 14 +- 22 files changed, 575 insertions(+), 191 deletions(-) rename PlayerAI.iml => TerminatorPlus.iml (100%) rename src/main/java/net/nuggetmc/ai/{PlayerAI.java => TerminatorPlus.java} (83%) create mode 100644 src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/BotData.java create mode 100644 src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NeuralNetwork.java create mode 100644 src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NodeConnections.java create mode 100644 src/main/java/net/nuggetmc/ai/command/commands/AICommand.java create mode 100644 src/main/java/net/nuggetmc/ai/command/commands/BotCommand.java create mode 100644 src/main/java/net/nuggetmc/ai/utils/StringUtils.java diff --git a/README.md b/README.md index 4e327d4..1c3ca40 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# PlayerAI [v3.0] +# TerminatorPlus [v3.0-BETA] -welcome, young one. yes, this is what will become the official release of the long awaited HorseNuggets 1,000 terminator manhunt plugin. +ah welcome. yes, this is what will become the official release of the long awaited HorseNuggets 1,000 terminator manhunt plugin. ## Some basic stuff -This plugin requires [Spigot 1.16.5](https://www.spigotmc.org/wiki/buildtools/#1-16-5). NMS 1.17 kind of screwed up a lot of the EntityPlayer variable names so I don't really wanna have to deal with that yet. +This plugin requires [Spigot 1.16.5](https://www.spigotmc.org/wiki/buildtools/#1-16-5). NMS 1.17 kind of screwed up a lot of the EntityPlayer variable names (obfuscation yay!!!) so I don't really wanna have to deal with that yet. ## Things that are left to do diff --git a/PlayerAI.iml b/TerminatorPlus.iml similarity index 100% rename from PlayerAI.iml rename to TerminatorPlus.iml diff --git a/pom.xml b/pom.xml index 1458da8..65e790c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,9 +5,9 @@ 4.0.0 net.nuggetmc - PlayerAI + TerminatorPlus 1.0-SNAPSHOT - PlayerAI + TerminatorPlus UTF-8 diff --git a/src/main/java/net/nuggetmc/ai/PlayerAI.java b/src/main/java/net/nuggetmc/ai/TerminatorPlus.java similarity index 83% rename from src/main/java/net/nuggetmc/ai/PlayerAI.java rename to src/main/java/net/nuggetmc/ai/TerminatorPlus.java index 1489c08..08451d8 100644 --- a/src/main/java/net/nuggetmc/ai/PlayerAI.java +++ b/src/main/java/net/nuggetmc/ai/TerminatorPlus.java @@ -7,14 +7,14 @@ import org.bukkit.plugin.java.JavaPlugin; import java.util.Arrays; -public class PlayerAI extends JavaPlugin { +public class TerminatorPlus extends JavaPlugin { - private static PlayerAI instance; + private static TerminatorPlus instance; private static String version; private BotManager manager; - public static PlayerAI getInstance() { + public static TerminatorPlus getInstance() { return instance; } @@ -29,7 +29,7 @@ public class PlayerAI extends JavaPlugin { @Override public void onEnable() { instance = this; - version = this.getDescription().getVersion(); + version = getDescription().getVersion(); // Create Instances this.manager = new BotManager(); diff --git a/src/main/java/net/nuggetmc/ai/bot/Bot.java b/src/main/java/net/nuggetmc/ai/bot/Bot.java index 05843e3..e999dae 100644 --- a/src/main/java/net/nuggetmc/ai/bot/Bot.java +++ b/src/main/java/net/nuggetmc/ai/bot/Bot.java @@ -2,11 +2,14 @@ package net.nuggetmc.ai.bot; import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Pair; +import net.minecraft.server.v1_16_R3.Chunk; import net.minecraft.server.v1_16_R3.*; -import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.bot.event.BotFallDamageEvent; import net.nuggetmc.ai.utils.BotUtils; import net.nuggetmc.ai.utils.MathUtils; +import net.nuggetmc.ai.utils.MojangAPI; +import net.nuggetmc.ai.utils.StringUtils; import org.bukkit.Material; import org.bukkit.SoundCategory; import org.bukkit.World; @@ -29,6 +32,8 @@ import java.util.UUID; public class Bot extends EntityPlayer { + public boolean item; // eventually make this not garbage lol + public Vector velocity; private Vector oldVelocity; @@ -55,13 +60,17 @@ public class Bot extends EntityPlayer { datawatcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF); } + public static Bot createBot(Location loc, String name) { + return createBot(loc, name, MojangAPI.getSkin(name), true); + } + public static Bot createBot(Location loc, String name, String[] skin, boolean removeOnDeath) { MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer(); WorldServer nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle(); UUID uuid = BotUtils.randomSteveUUID(); - CustomGameProfile profile = new CustomGameProfile(uuid, name, skin); + CustomGameProfile profile = new CustomGameProfile(uuid, StringUtils.trim16(name), skin); PlayerInteractManager interactManager = new PlayerInteractManager(nmsWorld); Bot bot = new Bot(nmsServer, nmsWorld, profile, interactManager); @@ -74,7 +83,7 @@ public class Bot extends EntityPlayer { bot.renderAll(); - PlayerAI.getInstance().getManager().add(bot); + TerminatorPlus.getInstance().getManager().add(bot); return bot; } @@ -90,7 +99,7 @@ public class Bot extends EntityPlayer { connection.sendPacket(packets[2]); if (login) { - Bukkit.getScheduler().runTaskLater(PlayerAI.getInstance(), () -> connection.sendPacket(packets[3]), 10); + Bukkit.getScheduler().runTaskLater(TerminatorPlus.getInstance(), () -> connection.sendPacket(packets[3]), 10); } else { connection.sendPacket(packets[3]); } @@ -140,6 +149,8 @@ public class Bot extends EntityPlayer { @Override public void tick() { + loadChunks(); + super.tick(); if (!isAlive()) return; @@ -162,7 +173,7 @@ public class Bot extends EntityPlayer { float health = getHealth(); float maxHealth = getMaxHealth(); - float regenAmount = 0.05f; + float regenAmount = 0.025f; float amount; if (health < maxHealth - regenAmount) { @@ -179,6 +190,20 @@ public class Bot extends EntityPlayer { oldVelocity = velocity.clone(); } + private void loadChunks() { + net.minecraft.server.v1_16_R3.World world = getWorld(); + + for (int i = chunkX - 1; i <= chunkX + 1; i++) { + for (int j = chunkZ - 1; j <= chunkZ + 1; j++) { + Chunk chunk = world.getChunkAt(i, j); + + if (!chunk.loaded) { + chunk.setLoaded(true); + } + } + } + } + private void fireDamageCheck() { if (!isAlive()) { return; // maybe also have packet reset thing @@ -231,7 +256,7 @@ public class Bot extends EntityPlayer { if (groundTicks != 0 && noFallTicks == 0 && !(oldVelocity.getY() >= -0.8) && !BotUtils.NO_FALL.contains(getLocation().getBlock().getType())) { BotFallDamageEvent event = new BotFallDamageEvent(this); - PlayerAI.getInstance().getManager().getAgent().onFallDamage(event); + TerminatorPlus.getInstance().getManager().getAgent().onFallDamage(event); if (!event.isCancelled()) { damageEntity(DamageSource.FALL, (float) Math.pow(3.6, -oldVelocity.getY())); @@ -301,7 +326,7 @@ public class Bot extends EntityPlayer { punch(); if (entity instanceof Damageable) { - ((Damageable) entity).damage(2, getBukkitEntity()); // fist damage is 0.25 + ((Damageable) entity).damage(item ? 6 : 0.25, getBukkitEntity()); // fist damage is 0.25 } } @@ -381,7 +406,7 @@ public class Bot extends EntityPlayer { private void dieCheck() { if (removeOnDeath) { - PlayerAI plugin = PlayerAI.getInstance(); + TerminatorPlus plugin = TerminatorPlus.getInstance(); plugin.getManager().remove(this); this.removeTab(); Bukkit.getScheduler().runTaskLater(plugin, this::setDead, 30); @@ -512,7 +537,7 @@ public class Bot extends EntityPlayer { } public void setItem(org.bukkit.inventory.ItemStack item) { - if (item == null) item = new org.bukkit.inventory.ItemStack(Material.AIR); + if (item == null) item = new org.bukkit.inventory.ItemStack(this.item ? Material.IRON_SWORD : Material.AIR); getBukkitEntity().getInventory().setItemInMainHand(item); diff --git a/src/main/java/net/nuggetmc/ai/bot/BotManager.java b/src/main/java/net/nuggetmc/ai/bot/BotManager.java index d061a17..2787d94 100644 --- a/src/main/java/net/nuggetmc/ai/bot/BotManager.java +++ b/src/main/java/net/nuggetmc/ai/bot/BotManager.java @@ -4,10 +4,7 @@ import net.minecraft.server.v1_16_R3.PlayerConnection; import net.nuggetmc.ai.bot.agent.Agent; import net.nuggetmc.ai.bot.agent.legacyagent.LegacyAgent; import net.nuggetmc.ai.utils.MojangAPI; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.Particle; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -26,6 +23,7 @@ public class BotManager implements Listener { private final Set bots; private final NumberFormat numberFormat; + public boolean joinMessages = false; public boolean removeOnDeath = true; public BotManager() { @@ -39,6 +37,10 @@ public class BotManager implements Listener { } public void add(Bot bot) { + if (joinMessages) { + Bukkit.broadcastMessage(ChatColor.YELLOW + bot.getName() + " joined the game"); + } + bots.add(bot); } @@ -64,9 +66,6 @@ public class BotManager implements Listener { World world = sender.getWorld(); Location loc = sender.getLocation(); - if (name.length() > 16) name = name.substring(0, 16); - if (skinName != null && skinName.length() > 16) skinName = skinName.substring(0, 16); - sender.sendMessage("Creating " + (n == 1 ? "new bot" : ChatColor.RED + numberFormat.format(n) + ChatColor.RESET + " new bots") + " with name " + ChatColor.GREEN + name + (skinName == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skinName) diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/Agent.java b/src/main/java/net/nuggetmc/ai/bot/agent/Agent.java index 6968b1e..dfb5dfa 100644 --- a/src/main/java/net/nuggetmc/ai/bot/agent/Agent.java +++ b/src/main/java/net/nuggetmc/ai/bot/agent/Agent.java @@ -1,7 +1,6 @@ package net.nuggetmc.ai.bot.agent; -import net.nuggetmc.ai.PlayerAI; -import net.nuggetmc.ai.bot.Bot; +import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.bot.BotManager; import net.nuggetmc.ai.bot.event.BotFallDamageEvent; import org.bukkit.Bukkit; @@ -14,7 +13,7 @@ import java.util.Set; public abstract class Agent { - protected final PlayerAI plugin; + protected final TerminatorPlus plugin; protected final BotManager manager; protected final BukkitScheduler scheduler; protected final Set taskList; @@ -24,7 +23,7 @@ public abstract class Agent { protected int taskID; public Agent(BotManager manager) { - this.plugin = PlayerAI.getInstance(); + this.plugin = TerminatorPlus.getInstance(); this.manager = manager; this.scheduler = Bukkit.getScheduler(); this.taskList = new HashSet<>(); diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/EnumTargetGoal.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/EnumTargetGoal.java index e0567dc..8990733 100644 --- a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/EnumTargetGoal.java +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/EnumTargetGoal.java @@ -1,10 +1,10 @@ package net.nuggetmc.ai.bot.agent.legacyagent; public enum EnumTargetGoal { - CLOSEST_REAL_VULNERABLE_PLAYER, - CLOSEST_REAL_PLAYER, - CLOSEST_BOT_DIFFER, - CLOSEST_BOT, + NEAREST_REAL_VULNERABLE_PLAYER, + NEAREST_REAL_PLAYER, + NEAREST_BOT_DIFFER, + NEAREST_BOT, NONE; public static EnumTargetGoal of(int n) { @@ -13,16 +13,16 @@ public enum EnumTargetGoal { return NONE; case 1: - return CLOSEST_REAL_VULNERABLE_PLAYER; + return NEAREST_REAL_VULNERABLE_PLAYER; case 2: - return CLOSEST_REAL_PLAYER; + return NEAREST_REAL_PLAYER; case 3: - return CLOSEST_BOT_DIFFER; + return NEAREST_BOT_DIFFER; case 4: - return CLOSEST_BOT; + return NEAREST_BOT; } } } diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyAgent.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyAgent.java index f3f6003..4e9249e 100644 --- a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyAgent.java +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyAgent.java @@ -26,10 +26,12 @@ public class LegacyAgent extends Agent { private final LegacyBlockCheck blockCheck; + private boolean useAIManipulators; + public LegacyAgent(BotManager manager) { super(manager); - this.goal = EnumTargetGoal.CLOSEST_REAL_VULNERABLE_PLAYER; + this.goal = EnumTargetGoal.NEAREST_REAL_VULNERABLE_PLAYER; this.blockCheck = new LegacyBlockCheck(this); } @@ -1038,16 +1040,16 @@ public class LegacyAgent extends Agent { private Player nearestPlayer(Bot bot, Location loc) { switch (goal) { - case CLOSEST_REAL_VULNERABLE_PLAYER: + case NEAREST_REAL_VULNERABLE_PLAYER: return nearestRealVulnerablePlayer(loc); - case CLOSEST_REAL_PLAYER: + case NEAREST_REAL_PLAYER: return nearestRealPlayer(loc); - case CLOSEST_BOT_DIFFER: + case NEAREST_BOT_DIFFER: return nearestBotDiffer(bot, loc); - case CLOSEST_BOT: + case NEAREST_BOT: return nearestBot(bot, loc); default: diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyBlockCheck.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyBlockCheck.java index 16b8c4a..df0554a 100644 --- a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyBlockCheck.java +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/LegacyBlockCheck.java @@ -1,6 +1,6 @@ package net.nuggetmc.ai.bot.agent.legacyagent; -import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.bot.Bot; import org.bukkit.*; import org.bukkit.block.Block; @@ -14,11 +14,11 @@ import java.util.Set; public class LegacyBlockCheck { - private final PlayerAI plugin; + private final TerminatorPlus plugin; private final LegacyAgent agent; public LegacyBlockCheck(LegacyAgent agent) { - this.plugin = PlayerAI.getInstance(); + this.plugin = TerminatorPlus.getInstance(); this.agent = agent; } diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/BotData.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/BotData.java new file mode 100644 index 0000000..51c20c6 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/BotData.java @@ -0,0 +1,47 @@ +package net.nuggetmc.ai.bot.agent.legacyagent.ai; + +import net.nuggetmc.ai.bot.Bot; +import net.nuggetmc.ai.utils.MathUtils; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +// If this is laggy, try only instantiating this once and update it instead of creating a new instance every tick +public class BotData { + + private final float health; + + private final double distXZ; + private final double distY; + + private final boolean enemyBlocking; + + private BotData(Bot bot, Player target) { + Location a = bot.getLocation(); + Location b = target.getLocation(); + + this.health = bot.getHealth(); + this.distXZ = Math.sqrt(MathUtils.square(a.getX() - b.getX()) + MathUtils.square(a.getZ() - b.getZ())); + this.distY = b.getY() - a.getY(); + this.enemyBlocking = target.isBlocking(); + } + + public static BotData generate(Bot bot, Player target) { + return new BotData(bot, target); + } + + public float getHealth() { + return health; + } + + public double getDistXZ() { + return distXZ; + } + + public double getDistY() { + return distY; + } + + public boolean getEnemyBlocking() { + return enemyBlocking; + } +} diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NeuralNetwork.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NeuralNetwork.java new file mode 100644 index 0000000..f9de0e8 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NeuralNetwork.java @@ -0,0 +1,20 @@ +package net.nuggetmc.ai.bot.agent.legacyagent.ai; + +public class NeuralNetwork { + + private final NodeConnections nodeL; // left strafe + private final NodeConnections nodeR; // right strafe (if L and R are opposite, move forward) + private final NodeConnections nodeB; // block + private final NodeConnections nodeJ; // jump + + public NeuralNetwork() { + this.nodeL = new NodeConnections(); + this.nodeR = new NodeConnections(); + this.nodeB = new NodeConnections(); + this.nodeJ = new NodeConnections(); + } + + public void feed(BotData data) { + + } +} diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NodeConnections.java b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NodeConnections.java new file mode 100644 index 0000000..b111846 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/bot/agent/legacyagent/ai/NodeConnections.java @@ -0,0 +1,47 @@ +package net.nuggetmc.ai.bot.agent.legacyagent.ai; + +public class NodeConnections { + + private final double connectionX; // horizontal distance + private final double connectionY; // vertical distance + private final double connectionB; // enemy blocking + private final double connectionH; // health + + public NodeConnections() { + this.connectionX = generate(); + this.connectionY = generate(); + this.connectionB = generate(); + this.connectionH = generate(); + } + + public NodeConnections(double y, double b, double t, double h) { + this.connectionX = t; + this.connectionY = y; + this.connectionB = b; + this.connectionH = h; + } + + private double generate() { + return Math.random() * 20 - 10; + } + + public double getX() { + return connectionX; + } + + public double getY() { + return connectionY; + } + + public double getB() { + return connectionB; + } + + public double getH() { + return connectionH; + } + + public boolean test(double y, double b, double t, double h) { + return Math.tanh(y * connectionX + b * connectionY + t * connectionB + h * connectionH) >= 0.5; + } +} diff --git a/src/main/java/net/nuggetmc/ai/command/CommandHandler.java b/src/main/java/net/nuggetmc/ai/command/CommandHandler.java index 1109ed6..4088562 100644 --- a/src/main/java/net/nuggetmc/ai/command/CommandHandler.java +++ b/src/main/java/net/nuggetmc/ai/command/CommandHandler.java @@ -4,10 +4,14 @@ import com.jonahseguin.drink.Drink; import com.jonahseguin.drink.annotation.Command; import com.jonahseguin.drink.command.DrinkCommandService; import com.jonahseguin.drink.utils.ChatUtils; +import net.nuggetmc.ai.command.commands.AICommand; +import net.nuggetmc.ai.command.commands.BotCommand; import net.nuggetmc.ai.command.commands.MainCommand; import org.bukkit.ChatColor; import org.bukkit.plugin.java.JavaPlugin; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; @@ -17,21 +21,31 @@ import java.util.stream.Collectors; public class CommandHandler { + private static final String MANAGE_PERMISSION = "terminatorplus.manage"; + private final DrinkCommandService drink; private final Map, List> help; public CommandHandler(JavaPlugin plugin) { - drink = (DrinkCommandService) Drink.get(plugin); - drink.register(new MainCommand(this), "playerai.manage", "bot", "playerai", "pai", "ai", "npc"); - drink.registerCommands(); - - help = new HashMap<>(); - setHelps(MainCommand.class); + this.drink = (DrinkCommandService) Drink.get(plugin); + this.help = new HashMap<>(); + this.registerCommands(); + this.drink.registerCommands(); } - @SafeVarargs - private final void setHelps(Class... cls) { - Arrays.stream(cls).forEach(c -> help.put(c, getUsage(c))); + private void registerCommands() { + registerCommand(new MainCommand(this, drink), "terminatorplus", "terminator"); + registerCommand(new BotCommand(this), "bot", "npc"); + registerCommand(new AICommand(this), "ai"); + } + + private void registerCommand(@Nonnull CommandInstance handler, @Nonnull String name, @Nullable String... aliases) { + drink.register(handler, MANAGE_PERMISSION, name, aliases); + setHelp(handler.getClass()); + } + + private void setHelp(Class cls) { + help.put(cls, getUsage(cls)); } public List getHelp(Class cls) { diff --git a/src/main/java/net/nuggetmc/ai/command/commands/AICommand.java b/src/main/java/net/nuggetmc/ai/command/commands/AICommand.java new file mode 100644 index 0000000..23946bd --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/command/commands/AICommand.java @@ -0,0 +1,40 @@ +package net.nuggetmc.ai.command.commands; + +import com.jonahseguin.drink.annotation.Command; +import com.jonahseguin.drink.annotation.OptArg; +import com.jonahseguin.drink.annotation.Sender; +import com.jonahseguin.drink.utils.ChatUtils; +import net.nuggetmc.ai.TerminatorPlus; +import net.nuggetmc.ai.bot.BotManager; +import net.nuggetmc.ai.command.CommandHandler; +import net.nuggetmc.ai.command.CommandInstance; +import org.bukkit.entity.Player; + +public class AICommand extends CommandInstance { + + private final BotManager manager; + + public AICommand(CommandHandler commandHandler) { + super(commandHandler); + + this.manager = TerminatorPlus.getInstance().getManager(); + } + + @Command( + desc = "The root command for bot AI training." + ) + public void root(@Sender Player sender) { + sender.sendMessage(ChatUtils.LINE); + commandHandler.getHelp(getClass()).forEach(sender::sendMessage); + sender.sendMessage(ChatUtils.LINE); + } + + @Command( + name = "random", + desc = "Create bots with random neural networks, collecting feed data.", + usage = " [skin]" + ) + public void create(@Sender Player sender, String name, @OptArg String skin) { + manager.createBots(sender, name, skin, 1); + } +} diff --git a/src/main/java/net/nuggetmc/ai/command/commands/BotCommand.java b/src/main/java/net/nuggetmc/ai/command/commands/BotCommand.java new file mode 100644 index 0000000..7c98b4f --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/command/commands/BotCommand.java @@ -0,0 +1,145 @@ +package net.nuggetmc.ai.command.commands; + +import com.jonahseguin.drink.annotation.Command; +import com.jonahseguin.drink.annotation.OptArg; +import com.jonahseguin.drink.annotation.Sender; +import com.jonahseguin.drink.annotation.Text; +import com.jonahseguin.drink.utils.ChatUtils; +import net.nuggetmc.ai.TerminatorPlus; +import net.nuggetmc.ai.bot.Bot; +import net.nuggetmc.ai.bot.BotManager; +import net.nuggetmc.ai.command.CommandHandler; +import net.nuggetmc.ai.command.CommandInstance; +import net.nuggetmc.ai.utils.Debugger; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +public class BotCommand extends CommandInstance { + + private final TerminatorPlus plugin; + private final BotManager manager; + private final BukkitScheduler scheduler; + private final DecimalFormat formatter; + + public BotCommand(CommandHandler commandHandler) { + super(commandHandler); + + this.plugin = TerminatorPlus.getInstance(); + this.manager = plugin.getManager(); + this.scheduler = Bukkit.getScheduler(); + this.formatter = new DecimalFormat("0.##"); + } + + @Command( + desc = "The root command for bot management." + ) + public void root(@Sender Player sender) { + sender.sendMessage(ChatUtils.LINE); + commandHandler.getHelp(getClass()).forEach(sender::sendMessage); + sender.sendMessage(ChatUtils.LINE); + } + + @Command( + name = "create", + desc = "Create a bot.", + usage = " [skin]" + ) + public void create(@Sender Player sender, String name, @OptArg String skin) { + manager.createBots(sender, name, skin, 1); + } + + @Command( + name = "multi", + desc = "Create multiple bots at once.", + usage = " [skin]" + ) + public void multi(@Sender Player sender, int n, String name, @OptArg String skin) { + manager.createBots(sender, name, skin, n); + } + + @Command( + name = "debug", + desc = "Debug plugin code.", + usage = "" + ) + public void debug(@Sender CommandSender sender, @Text String cmd) { + new Debugger(sender).execute(cmd); + } + + @Command( + name = "info", + desc = "Information about loaded bots.", + usage = "[name]" + ) + public void info(@Sender CommandSender sender, @OptArg String name) { + if (name == null) { + sender.sendMessage(ChatColor.YELLOW + "Bot GUI coming soon!"); + return; + } + + sender.sendMessage("Processing request..."); + + scheduler.runTaskAsynchronously(plugin, () -> { + try { + Bot bot = manager.getFirst(name); + + if (bot == null) { + sender.sendMessage("Could not find bot " + ChatColor.GREEN + name + ChatColor.RESET + "!"); + return; + } + + /* + * time created + * current life (how long it has lived for) + * health + * inventory + * current target + * current kills + * skin + * neural network values + */ + + String botName = bot.getName(); + String world = ChatColor.YELLOW + bot.getBukkitEntity().getWorld().getName(); + Location loc = bot.getLocation(); + String strLoc = ChatColor.YELLOW + formatter.format(loc.getBlockX()) + ", " + formatter.format(loc.getBlockY()) + ", " + formatter.format(loc.getBlockZ()); + Vector vel = bot.getVelocity(); + String strVel = ChatColor.AQUA + formatter.format(vel.getX()) + ", " + formatter.format(vel.getY()) + ", " + formatter.format(vel.getZ()); + + sender.sendMessage(ChatUtils.LINE); + sender.sendMessage(ChatColor.GREEN + botName); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + "World: " + world); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Position: " + strLoc); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Velocity: " + strVel); + sender.sendMessage(ChatUtils.LINE); + } + + catch (Exception e) { + sender.sendMessage(ChatColor.RED + "An exception has occured. Please try again."); + } + }); + } + + @Command( + name = "reset", + desc = "Remove all loaded bots." + ) + public void reset(@Sender CommandSender sender) { + sender.sendMessage("Removing every bot..."); + + int size = manager.fetch().size(); + manager.reset(); + + String formatted = NumberFormat.getNumberInstance(Locale.US).format(size); + sender.sendMessage("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + "."); + } +} diff --git a/src/main/java/net/nuggetmc/ai/command/commands/MainCommand.java b/src/main/java/net/nuggetmc/ai/command/commands/MainCommand.java index 8d74080..b488861 100644 --- a/src/main/java/net/nuggetmc/ai/command/commands/MainCommand.java +++ b/src/main/java/net/nuggetmc/ai/command/commands/MainCommand.java @@ -1,148 +1,73 @@ package net.nuggetmc.ai.command.commands; import com.jonahseguin.drink.annotation.Command; -import com.jonahseguin.drink.annotation.OptArg; import com.jonahseguin.drink.annotation.Sender; -import com.jonahseguin.drink.annotation.Text; +import com.jonahseguin.drink.command.DrinkCommandService; import com.jonahseguin.drink.utils.ChatUtils; -import net.nuggetmc.ai.PlayerAI; -import net.nuggetmc.ai.bot.Bot; -import net.nuggetmc.ai.bot.BotManager; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.hover.content.Text; +import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.command.CommandHandler; import net.nuggetmc.ai.command.CommandInstance; -import net.nuggetmc.ai.utils.Debugger; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.util.Vector; - -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Locale; public class MainCommand extends CommandInstance { - private final PlayerAI plugin; - private final BotManager manager; - private final BukkitScheduler scheduler; - private final DecimalFormat formatter; + private final DrinkCommandService drink; - public MainCommand(CommandHandler commandHandler) { + private BaseComponent[] rootInfo; + + public MainCommand(CommandHandler commandHandler, DrinkCommandService drink) { super(commandHandler); - this.plugin = PlayerAI.getInstance(); - this.manager = plugin.getManager(); - this.scheduler = Bukkit.getScheduler(); - this.formatter = new DecimalFormat("0.##"); + this.drink = drink; + + Bukkit.getScheduler().runTask(TerminatorPlus.getInstance(), this::rootInfoSetup); } @Command( - desc = "The PlayerAI main command." + desc = "The TerminatorPlus main command." ) public void root(@Sender Player sender) { - sender.sendMessage(ChatUtils.LINE); - sender.sendMessage(ChatColor.GOLD + "PlayerAI" + ChatColor.GRAY + " [" + ChatColor.RED + "v" + PlayerAI.getVersion() + ChatColor.GRAY + "]"); - commandHandler.getHelp(getClass()).forEach(sender::sendMessage); - sender.sendMessage(ChatUtils.LINE); + sender.spigot().sendMessage(rootInfo); } - @Command( - name = "create", - desc = "Create bots.", - usage = " [skin]" - ) - public void create(@Sender Player sender, String name, @OptArg String skin) { - manager.createBots(sender, name, skin, 1); - } + private void rootInfoSetup() { + ComponentBuilder message = new ComponentBuilder(); + String pluginName = TerminatorPlus.getInstance().getName(); - @Command( - name = "multi", - desc = "Create multiple bots at once.", - usage = " [skin]" - ) - public void multi(@Sender Player sender, int n, String name, @OptArg String skin) { - manager.createBots(sender, name, skin, n); - } + message.append(ChatUtils.LINE + "\n"); + message.append(ChatColor.GOLD + pluginName + ChatColor.GRAY + " [v" + TerminatorPlus.getVersion() + "]\n"); + message.append("\nPlugin Information:\n"); + message.append(ChatUtils.BULLET_FORMATTED + "Author" + ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "HorseNuggets\n"); + message.append(ChatUtils.BULLET_FORMATTED + "Links" + ChatUtils.BULLET_FORMATTED); + message.append(ChatColor.RED + "YouTube"); + message.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.youtube.com/horsenuggets")); + message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to visit HorseNuggets' " + ChatColor.RED + "YouTube" + ChatColor.RESET + "!"))); + message.append(", "); + message.event((ClickEvent) null); + message.event((HoverEvent) null); + message.append(ChatColor.BLUE + "Discord"); + message.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "lol okay this isn't actually ready yet")); + message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to visit HorseNuggets' " + ChatColor.BLUE + "Discord" + ChatColor.RESET + "!"))); + message.append("\n"); + message.event((ClickEvent) null); + message.event((HoverEvent) null); + message.append("\nPlugin Commands:\n"); - @Command( - name = "debug", - desc = "Debug plugin code.", - usage = "" - ) - public void debug(@Sender CommandSender sender, @Text String cmd) { - new Debugger(sender).execute(cmd); - } - - @Command( - name = "info", - desc = "Information about loaded bots.", - usage = "[name]" - ) - public void info(@Sender CommandSender sender, @OptArg String name) { - if (name == null) { - sender.sendMessage(ChatColor.YELLOW + "Bot GUI coming soon!"); - return; - } - - sender.sendMessage("Processing request..."); - - scheduler.runTaskAsynchronously(plugin, () -> { - try { - Bot bot = manager.getFirst(name); - - if (bot == null) { - sender.sendMessage("Could not find bot " + ChatColor.GREEN + name + ChatColor.RESET + "!"); - return; - } - - /* - * health - * inventory - * current target - * current kills - * skin - * neural network values - */ - - sender.sendMessage(ChatUtils.LINE); - String botName = bot.getName(); - sender.sendMessage(ChatColor.GREEN + botName); - //String created = ChatColor.YELLOW + ""; - //sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Created: " + created); - String world = ChatColor.YELLOW + bot.getBukkitEntity().getWorld().getName(); - sender.sendMessage(ChatUtils.BULLET_FORMATTED + "World: " + world); - Location loc = bot.getLocation(); - String strLoc = ChatColor.YELLOW + formatter.format(loc.getBlockX()) + ", " + formatter.format(loc.getBlockY()) + ", " + formatter.format(loc.getBlockZ()); - sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Position: " + strLoc); - Vector vel = bot.getVelocity(); - sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Velocity: " + vel); - String strVel = ChatColor.AQUA + formatter.format(vel.getX()) + ", " + formatter.format(vel.getY()) + ", " + formatter.format(vel.getZ()); - sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Velocity: " + strVel); - - sender.sendMessage(ChatUtils.LINE); - } - - catch (Exception e) { - sender.sendMessage(ChatColor.RED + "An exception has occured. Please try again."); + drink.getCommands().forEach((name, command) -> { + if (!name.equalsIgnoreCase(pluginName)) { + message.append(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "/" + name + ChatUtils.BULLET_FORMATTED + command.getDescription() + "\n"); } }); - } - @Command( - name = "reset", - desc = "Remove all loaded bots." - ) - public void reset(@Sender CommandSender sender) { - sender.sendMessage("Removing every bot..."); + message.append(ChatUtils.LINE); - int size = manager.fetch().size(); - manager.reset(); - - String formatted = NumberFormat.getNumberInstance(Locale.US).format(size); - sender.sendMessage("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + "."); + this.rootInfo = message.create(); } } diff --git a/src/main/java/net/nuggetmc/ai/utils/Debugger.java b/src/main/java/net/nuggetmc/ai/utils/Debugger.java index cf2a291..9dc9ee1 100644 --- a/src/main/java/net/nuggetmc/ai/utils/Debugger.java +++ b/src/main/java/net/nuggetmc/ai/utils/Debugger.java @@ -1,19 +1,17 @@ package net.nuggetmc.ai.utils; -import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.bot.Bot; import net.nuggetmc.ai.bot.agent.Agent; import net.nuggetmc.ai.bot.agent.legacyagent.EnumTargetGoal; import net.nuggetmc.ai.bot.agent.legacyagent.LegacyAgent; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.permissions.ServerOperator; +import org.bukkit.util.Vector; import java.beans.Statement; import java.util.ArrayList; @@ -98,8 +96,77 @@ public class Debugger { * DEBUGGER METHODS */ + public void confuse(int n) { + if (!(sender instanceof Player)) return; + + Player player = (Player) sender; + Location loc = player.getLocation(); + + double f = n < 100 ? .004 * n : .4; + + for (int i = 0; i < n; i++) { + Player target = Bukkit.getOnlinePlayers().stream().skip((int) (Bukkit.getOnlinePlayers().size() * Math.random())).findFirst().orElse(null); + String name = target == null ? "Steve" : target.getName(); + Bot bot = Bot.createBot(loc, name); + bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f)); + bot.faceLocation(bot.getLocation().add(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)); + } + + player.getWorld().spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5); + } + + public void dreamsmp() { + if (!(sender instanceof Player)) return; + + Player player = (Player) sender; + Location loc = player.getLocation(); + + String[] players = new String[] { + "Dream", "GeorgeNotFound", "Callahan", "Sapnap", "awesamdude", "Ponk", "BadBoyHalo", "TommyInnit", "Tubbo_", "ItsFundy", "Punz", + "Purpled", "WilburSoot", "Jschlatt", "Skeppy", "The_Eret", "JackManifoldTV", "Nihachu", "Quackity", "KarlJacobs", "HBomb94", + "Technoblade", "Antfrost", "Ph1LzA", "ConnorEatsPants", "CaptainPuffy", "Vikkstar123", "LazarCodeLazar", "Ranboo", "FoolishG", + "hannahxxrose", "Slimecicle", "Michaelmcchill" + }; + + double f = .004 * players.length; + + for (String name : players) { + Bot bot = Bot.createBot(loc, name); + bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f)); + bot.faceLocation(bot.getLocation().add(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)); + } + + player.getWorld().spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5); + } + + public void item() { + TerminatorPlus.getInstance().getManager().fetch().forEach(b -> b.item = true); + } + + public void j(boolean b) { + TerminatorPlus.getInstance().getManager().joinMessages = b; + } + + public void epic(int n) { + if (!(sender instanceof Player)) return; + + Player player = (Player) sender; + Location loc = player.getLocation(); + + double f = n < 100 ? .004 * n : .4; + + for (int i = 0; i < n; i++) { + String name = PlayerUtils.randomName(); + Bot bot = Bot.createBot(loc, name); + bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f)); + bot.faceLocation(bot.getLocation().add(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)); + } + + player.getWorld().spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5); + } + public void tp() { - Bot bot = MathUtils.getRandomSetElement(PlayerAI.getInstance().getManager().fetch()); + Bot bot = MathUtils.getRandomSetElement(TerminatorPlus.getInstance().getManager().fetch()); if (bot == null) { print("Failed to locate a bot."); @@ -115,7 +182,7 @@ public class Debugger { } public void setTarget(int n) { - Agent agent = PlayerAI.getInstance().getManager().getAgent(); + Agent agent = TerminatorPlus.getInstance().getManager().getAgent(); if (!(agent instanceof LegacyAgent)) { print("This method currently only supports " + ChatColor.AQUA + "LegacyAgent" + ChatColor.RESET + "."); return; @@ -130,7 +197,7 @@ public class Debugger { } public void fire(boolean b) { - PlayerAI.getInstance().getManager().fetch().forEach(bot -> bot.setOnFirePackets(b)); + TerminatorPlus.getInstance().getManager().fetch().forEach(bot -> bot.setOnFirePackets(b)); } public void trackYVel() { @@ -138,13 +205,13 @@ public class Debugger { Player player = (Player) sender; - Bukkit.getScheduler().scheduleSyncRepeatingTask(PlayerAI.getInstance(), () -> { + Bukkit.getScheduler().scheduleSyncRepeatingTask(TerminatorPlus.getInstance(), () -> { print(player.getVelocity().getY()); }, 0, 1); } public void hideNametags() { // this works for some reason - Set bots = PlayerAI.getInstance().getManager().fetch(); + Set bots = TerminatorPlus.getInstance().getManager().fetch(); for (Bot bot : bots) { Location loc = bot.getLocation(); @@ -167,7 +234,7 @@ public class Debugger { } public void sit() { - Set bots = PlayerAI.getInstance().getManager().fetch(); + Set bots = TerminatorPlus.getInstance().getManager().fetch(); for (Bot bot : bots) { Location loc = bot.getLocation(); @@ -198,13 +265,13 @@ public class Debugger { Player player = (Player) sender; - for (Bot bot : PlayerAI.getInstance().getManager().fetch()) { + for (Bot bot : TerminatorPlus.getInstance().getManager().fetch()) { bot.faceLocation(player.getEyeLocation()); } } public void toggleAgent() { - Agent agent = PlayerAI.getInstance().getManager().getAgent(); + Agent agent = TerminatorPlus.getInstance().getManager().getAgent(); boolean b = agent.isEnabled(); agent.setEnabled(!b); diff --git a/src/main/java/net/nuggetmc/ai/utils/MathUtils.java b/src/main/java/net/nuggetmc/ai/utils/MathUtils.java index 8f3ab98..b261bc2 100644 --- a/src/main/java/net/nuggetmc/ai/utils/MathUtils.java +++ b/src/main/java/net/nuggetmc/ai/utils/MathUtils.java @@ -75,4 +75,8 @@ public class MathUtils { public static E getRandomSetElement(Set set) { return set.isEmpty() ? null : set.stream().skip(RANDOM.nextInt(set.size())).findFirst().orElse(null); } + + public static double square(double n) { + return n * n; + } } diff --git a/src/main/java/net/nuggetmc/ai/utils/PlayerUtils.java b/src/main/java/net/nuggetmc/ai/utils/PlayerUtils.java index fac6b69..0985bca 100644 --- a/src/main/java/net/nuggetmc/ai/utils/PlayerUtils.java +++ b/src/main/java/net/nuggetmc/ai/utils/PlayerUtils.java @@ -1,9 +1,52 @@ package net.nuggetmc.ai.utils; +import net.nuggetmc.ai.TerminatorPlus; import org.bukkit.GameMode; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.io.FileReader; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; public class PlayerUtils { + public static boolean isInvincible(GameMode mode) { return mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE && mode != null; } + + private static final Set USERNAME_CACHE = new HashSet<>(); + + public static String randomName() { + if (USERNAME_CACHE.isEmpty()) { + fillUsernameCache(); + } + + return MathUtils.getRandomSetElement(USERNAME_CACHE); + } + + public static void fillUsernameCache() { + String file = TerminatorPlus.getInstance().getServer().getWorldContainer().getAbsolutePath(); + file = file.substring(0, file.length() - 1) + "usercache.json"; + + JSONParser parser = new JSONParser(); + + try { + JSONArray array = (JSONArray) parser.parse(new FileReader(file)); + + for (Object obj : array) { + JSONObject jsonOBJ = (JSONObject) obj; + String username = (String) jsonOBJ.get("name"); + + USERNAME_CACHE.add(username); + } + } + + catch (IOException | ParseException e) { + Debugger.log("Failed to fetch from the usercache."); + } + } } diff --git a/src/main/java/net/nuggetmc/ai/utils/StringUtils.java b/src/main/java/net/nuggetmc/ai/utils/StringUtils.java new file mode 100644 index 0000000..49149c2 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/utils/StringUtils.java @@ -0,0 +1,7 @@ +package net.nuggetmc.ai.utils; + +public class StringUtils { + public static String trim16(String str) { + return str.length() > 16 ? str.substring(0, 16) : str; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 95be763..bbfa612 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,15 +1,15 @@ -name: PlayerAI -main: net.nuggetmc.ai.PlayerAI +name: TerminatorPlus +main: net.nuggetmc.ai.TerminatorPlus version: 3.0-BETA api-version: 1.16 author: HorseNuggets permissions: - playerai.*: - description: PlayerAI parent permission. + terminatorplus.*: + description: TerminatorPlus parent permission. default: op children: - playerai.manage: true - playerai.manage: - description: Allows for PlayerAI bot management. + terminatorplus.manage: true + terminatorplus.manage: + description: Allows for TerminatorPlus bot management. default: op \ No newline at end of file