diff --git a/src/main/java/net/nuggetmc/ai/bot/Bot.java b/src/main/java/net/nuggetmc/ai/bot/Bot.java index 1eaeb24..5cdc288 100644 --- a/src/main/java/net/nuggetmc/ai/bot/Bot.java +++ b/src/main/java/net/nuggetmc/ai/bot/Bot.java @@ -24,9 +24,13 @@ public class Bot extends EntityPlayer { public Vector velocity; private byte kbTicks; + private byte jumpTicks; + private byte groundTicks; private final double regenAmount = 0.05; private final double bbOffset = 0.05; + private final double frictionMin = 0.01; + private final double kbUp = 0.3; public Bot(MinecraftServer minecraftServer, WorldServer worldServer, GameProfile profile, PlayerInteractManager manager) { super(minecraftServer, worldServer, profile, manager); @@ -92,11 +96,22 @@ public class Bot extends EntityPlayer { } } + public Vector getVelocity() { + return velocity.clone(); + } + public void setVelocity(Vector vector) { this.velocity = vector; } public void addVelocity(Vector vector) { + try { + velocity.checkFinite(); + } catch (IllegalArgumentException e) { + velocity = vector; + return; + } + this.velocity.add(vector); } @@ -106,6 +121,13 @@ public class Bot extends EntityPlayer { if (noDamageTicks > 0) --noDamageTicks; if (kbTicks > 0) --kbTicks; + if (jumpTicks > 0) --jumpTicks; + + if (predictGround()) { + groundTicks++; + } else { + groundTicks = 0; + } Player botPlayer = this.getBukkitEntity(); if (botPlayer.isDead()) return; @@ -128,11 +150,9 @@ public class Bot extends EntityPlayer { private void updateLocation() { // Eventually there will be a whole algorithm here to slow a player down to a certain velocity depending on the liquid a player is in - velocity.setY(velocity.getY() - 0.1); - double y; - if (predictGround()) { + if (groundTicks > 0) { velocity.setY(0); addFriction(); y = 0; @@ -140,9 +160,18 @@ public class Bot extends EntityPlayer { y = velocity.getY(); } + velocity.setY(velocity.getY() - 0.1); + this.move(EnumMoveType.SELF, new Vec3D(velocity.getX(), y, velocity.getZ())); } + public void jump(Vector vel) { + if (jumpTicks == 0 && groundTicks > 1) { + jumpTicks = 4; + velocity = vel; + } + } + public boolean predictGround() { double vy = velocity.getY(); @@ -165,13 +194,7 @@ public class Bot extends EntityPlayer { for (double x : xVals) { for (double z : zVals) { - double i = locY(); - - Location test = new Location(world, x, i - 0.05, z); - - if (test.getBlock().getType().isSolid()) { - return true; - } + return world.getBlockAt(new Location(world, x, locY() - 0.01, z)).getType().isSolid(); } } @@ -179,11 +202,18 @@ public class Bot extends EntityPlayer { } public void addFriction() { - velocity.setX(velocity.getX() * 0.5); - velocity.setZ(velocity.getZ() * 0.5); + double x = velocity.getX(); + double z = velocity.getZ(); + + velocity.setX(x < frictionMin ? 0 : x * 0.5); + velocity.setZ(z < frictionMin ? 0 : z * 0.5); } public void despawn() { + getBukkitEntity().remove(); + } + + public void remove() { for (Player player : Bukkit.getOnlinePlayers()) { PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; connection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, this)); @@ -245,10 +275,14 @@ public class Bot extends EntityPlayer { private void kb(Location loc1, Location loc2) { Vector diff = loc1.toVector().subtract(loc2.toVector()).normalize(); - diff.multiply(0.25); - diff.setY(0.5); + diff.multiply(0.5); + diff.setY(kbUp); - velocity.add(diff); + Vector vel = velocity.clone().add(diff); + if (vel.length() > 1) vel.normalize(); + if (vel.getY() > kbUp) vel.setY(kbUp); + + velocity = vel; kbTicks = 10; } diff --git a/src/main/java/net/nuggetmc/ai/bot/BotManager.java b/src/main/java/net/nuggetmc/ai/bot/BotManager.java index 586f204..6031179 100644 --- a/src/main/java/net/nuggetmc/ai/bot/BotManager.java +++ b/src/main/java/net/nuggetmc/ai/bot/BotManager.java @@ -2,6 +2,7 @@ package net.nuggetmc.ai.bot; import net.minecraft.server.v1_16_R3.PlayerConnection; import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.bot.agent.BotAgent; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Particle; @@ -13,12 +14,16 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.util.Vector; +import java.text.NumberFormat; import java.util.HashSet; +import java.util.Locale; import java.util.Set; public class BotManager implements Listener { private final PlayerAI plugin; + private final BotAgent agent; + private final NumberFormat numberFormat; private final Set bots = new HashSet<>(); @@ -32,6 +37,8 @@ public class BotManager implements Listener { public BotManager(PlayerAI plugin) { this.plugin = plugin; + this.agent = new BotAgent(this); + this.numberFormat = NumberFormat.getInstance(Locale.US); } public void createBots(Player sender, String name, String skin, int n) { @@ -45,27 +52,28 @@ public class BotManager implements Listener { 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") + sender.sendMessage("Creating " + (n == 1 ? "new bot" : ChatColor.RED + numberFormat.format(n) + ChatColor.RESET + " new bots") + " with name " + ChatColor.GREEN + name - + (skin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skin) + ChatColor.RESET + "..."); + + (skin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skin) + + ChatColor.RESET + "..."); skin = skin == null ? name : skin; + double f = n < 100 ? .004 * n : .4; + 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)); + if (i > 0) bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f)); } 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 + ")."); + sender.sendMessage("Process completed (" + ChatColor.RED + ((System.currentTimeMillis() - timestamp) / 1000D) + "s" + ChatColor.RESET + ")."); } public void reset() { for (Bot bot : bots) { - bot.despawn(); + bot.remove(); } bots.clear(); diff --git a/src/main/java/net/nuggetmc/ai/bot/agent/BotAgent.java b/src/main/java/net/nuggetmc/ai/bot/agent/BotAgent.java new file mode 100644 index 0000000..6972266 --- /dev/null +++ b/src/main/java/net/nuggetmc/ai/bot/agent/BotAgent.java @@ -0,0 +1,70 @@ +package net.nuggetmc.ai.bot.agent; + +import net.nuggetmc.ai.PlayerAI; +import net.nuggetmc.ai.bot.Bot; +import net.nuggetmc.ai.bot.BotManager; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +public class BotAgent { + + private BotManager manager; + + private byte quarterTick = 0; + + public BotAgent(BotManager manager) { + this.manager = manager; + + Bukkit.getScheduler().scheduleSyncRepeatingTask(PlayerAI.getInstance(), this::tick, 0, 1); + } + + private void tick() { + quarterTick = (byte) ((quarterTick + 1) % 5); + manager.fetch().forEach(this::tickBot); + } + + private void tickBot(Bot bot) { + Location loc = bot.getLocation(); + + Player player = nearestPlayer(loc); + if (player == null) return; + + Location target = player.getLocation(); + Vector vel = target.toVector().subtract(loc.toVector()).normalize(); + + if (quarterTick == 0) { + bot.faceLocation(target); + } + + try { + vel.checkFinite(); + vel.add(bot.velocity); + } catch (IllegalArgumentException e) { + vel = bot.velocity; + } + + if (vel.length() > 1) vel.normalize(); + vel.multiply(0.3); + vel.setY(0.5); + + if (bot.predictGround()) { + bot.jump(vel); + } + } + + private Player nearestPlayer(Location loc) { + Player result = null; + + for (Player player : Bukkit.getOnlinePlayers()) { + if (loc.getWorld() != player.getWorld()) continue; + + if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) { + result = player; + } + } + + return result; + } +}