fixed a lot of velocity and packet efficiency stuff, also worked a bit on the agent

This commit is contained in:
batchprogrammer314
2021-07-10 23:51:14 -05:00
parent 7bd2e50afd
commit 7c2de37bd8
11 changed files with 281 additions and 78 deletions

View File

@@ -1,20 +1,28 @@
package net.nuggetmc.ai.bot;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
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.Material;
import org.bukkit.SoundCategory;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
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;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -38,7 +46,7 @@ public class Bot extends EntityPlayer {
datawatcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF);
}
public static Bot createBot(Location loc, String name, String skin) {
public static Bot createBot(Location loc, String name, String[] skin) {
MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
WorldServer nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle();
@@ -61,25 +69,41 @@ public class Bot extends EntityPlayer {
return bot;
}
public Vector prevVel = new Vector(0, 0, 0);
public int velCount;
public static Bot createBot(Location loc, String name, String skinName) {
return createBot(loc, name, MojangAPI.getSkin(skinName));
}
private void renderAll() {
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
render(connection, false);
Packet<?>[] packets = getRenderPackets();
Bukkit.getOnlinePlayers().forEach(p -> render(((CraftPlayer) p).getHandle().playerConnection, packets, false));
}
public void render(PlayerConnection connection, Packet<?>[] packets, boolean login) {
connection.sendPacket(packets[0]);
connection.sendPacket(packets[1]);
connection.sendPacket(packets[2]);
if (login) {
Bukkit.getScheduler().runTaskLater(PlayerAI.getInstance(), () -> connection.sendPacket(packets[3]), 10);
} else {
connection.sendPacket(packets[3]);
}
}
public void render(PlayerConnection connection, boolean login) {
connection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, this));
connection.sendPacket(new PacketPlayOutNamedEntitySpawn(this));
connection.sendPacket(new PacketPlayOutEntityMetadata(this.getId(), this.getDataWatcher(), true));
render(connection, getRenderPackets(), login);
}
PacketPlayOutEntityHeadRotation rotationPacket = new PacketPlayOutEntityHeadRotation(this, (byte) ((this.yaw * 256f) / 360f));
if (login) {
Bukkit.getScheduler().runTaskLater(PlayerAI.getInstance(), () -> connection.sendPacket(rotationPacket), 10);
} else {
connection.sendPacket(rotationPacket);
}
private Packet<?>[] getRenderPackets() {
return new Packet[] {
new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, this),
new PacketPlayOutNamedEntitySpawn(this),
new PacketPlayOutEntityMetadata(this.getId(), this.getDataWatcher(), true),
new PacketPlayOutEntityHeadRotation(this, (byte) ((this.yaw * 256f) / 360f))
};
}
public Vector getOffset() {
@@ -119,8 +143,8 @@ public class Bot extends EntityPlayer {
if (kbTicks > 0) --kbTicks;
if (jumpTicks > 0) --jumpTicks;
if (isOnGround()) {
groundTicks++;
if (checkGround()) {
if (groundTicks < 5) groundTicks++;
} else {
groundTicks = 0;
}
@@ -157,7 +181,7 @@ public class Bot extends EntityPlayer {
y = velocity.getY();
}
velocity.setY(velocity.getY() - 0.1);
velocity.setY(Math.max(velocity.getY() - 0.1, -3.5));
this.move(EnumMoveType.SELF, new Vec3D(velocity.getX(), y, velocity.getZ()));
}
@@ -183,8 +207,8 @@ public class Bot extends EntityPlayer {
swingHand(EnumHand.MAIN_HAND);
}
@Override
public boolean isOnGround() {
public boolean checkGround() {
double k = 0.01;
double vy = velocity.getY();
if (vy > 0) {
@@ -195,13 +219,13 @@ public class Bot extends EntityPlayer {
AxisAlignedBB box = getBoundingBox();
double[] xVals = new double[] {
box.minX,
box.maxX
box.minX + k,
box.maxX - k
};
double[] zVals = new double[] {
box.minZ,
box.maxZ
box.minZ + k,
box.maxZ - k
};
for (double x : xVals) {
@@ -215,14 +239,19 @@ public class Bot extends EntityPlayer {
return false;
}
@Override
public boolean isOnGround() {
return groundTicks != 0;
}
public void addFriction() {
double frictionMin = 0.01;
double x = velocity.getX();
double z = velocity.getZ();
velocity.setX(x < frictionMin ? 0 : x * 0.5);
velocity.setZ(z < frictionMin ? 0 : z * 0.5);
velocity.setX(Math.abs(x) < frictionMin ? 0 : x * 0.5);
velocity.setZ(Math.abs(z) < frictionMin ? 0 : z * 0.5);
}
public void despawn() {
@@ -290,14 +319,11 @@ public class Bot extends EntityPlayer {
}
private void kb(Location loc1, Location loc2) {
double f = 4;
double kbUp = 0.3;
Vector diff = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(f).setY(kbUp);
Vector vel = velocity.clone().add(diff).multiply(0.3 / f);
Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3);
if (vel.length() > 1) vel.normalize();
if (groundTicks != 0) vel.multiply(0.8).setY(0.4);
if (isOnGround()) vel.multiply(0.8).setY(0.4);
else if (vel.getY() > kbUp) vel.setY(kbUp);
velocity = vel;
@@ -309,17 +335,73 @@ public class Bot extends EntityPlayer {
}
public void faceLocation(Location loc) {
CraftPlayer botPlayer = getBukkitEntity();
Vector dir = loc.toVector().subtract(botPlayer.getLocation().toVector());
look(loc.toVector().subtract(getLocation().toVector()), false);
}
float[] vals = MathUtils.fetchYawPitch(dir);
public void look(BlockFace face) {
look(face.getDirection(), face == BlockFace.DOWN || face == BlockFace.UP);
}
setYawPitch(vals[0], vals[1]);
private void look(Vector dir, boolean keepYaw) {
float yaw, pitch;
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutEntityHeadRotation(botPlayer.getHandle(), (byte) (vals[0] * 256 / 360f)));
if (keepYaw) {
yaw = this.yaw;
pitch = MathUtils.fetchPitch(dir);
} else {
float[] vals = MathUtils.fetchYawPitch(dir);
yaw = vals[0];
pitch = vals[1];
}
setYawPitch(yaw, pitch);
PacketPlayOutEntityHeadRotation packet = new PacketPlayOutEntityHeadRotation(getBukkitEntity().getHandle(), (byte) (yaw * 256 / 360f));
Bukkit.getOnlinePlayers().forEach(p -> ((CraftPlayer) p).getHandle().playerConnection.sendPacket(packet));
}
public void attemptBlockPlace(Location loc, Material type) {
setItem(new org.bukkit.inventory.ItemStack(Material.COBBLESTONE));
punch();
Block block = loc.getBlock();
World world = loc.getWorld();
if (!block.getType().isSolid()) {
block.setType(type);
if (world != null) world.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
}
}
public void setItem(org.bukkit.inventory.ItemStack item) {
if (item == null) item = new org.bukkit.inventory.ItemStack(Material.AIR);
CraftPlayer player = getBukkitEntity();
player.getInventory().setItemInMainHand(item);
List<Pair<EnumItemSlot, ItemStack>> equipment = new ArrayList<>();
equipment.add(new Pair<>(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(item)));
PacketPlayOutEntityEquipment packet = new PacketPlayOutEntityEquipment(player.getEntityId(), equipment);
Bukkit.getOnlinePlayers().forEach(p -> ((CraftPlayer) p).getHandle().playerConnection.sendPacket(packet));
}
public void swim() {
registerPose(EntityPose.SWIMMING);
}
public void sneak() {
registerPose(EntityPose.CROUCHING);
}
public void stand() {
registerPose(EntityPose.STANDING);
}
private void registerPose(EntityPose pose) {
datawatcher.set(DataWatcherRegistry.s.a(6), pose);
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(getId(), datawatcher, false);
Bukkit.getOnlinePlayers().forEach(p -> ((CraftPlayer) p).getHandle().playerConnection.sendPacket(packet));
}
@Override

View File

@@ -3,6 +3,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 net.nuggetmc.ai.utils.MojangAPI;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Particle;
@@ -45,7 +46,7 @@ public class BotManager implements Listener {
return agent;
}
public void createBots(Player sender, String name, String skin, int n) {
public void createBots(Player sender, String name, String skinName, int n) {
long timestamp = System.currentTimeMillis();
if (n < 1) n = 1;
@@ -54,17 +55,19 @@ public class BotManager implements Listener {
Location loc = sender.getLocation();
if (name.length() > 16) name = name.substring(0, 16);
if (skin != null && skin.length() > 16) skin = skin.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
+ (skin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skin)
+ (skinName == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skinName)
+ ChatColor.RESET + "...");
skin = skin == null ? name : skin;
skinName = skinName == null ? name : skinName;
double f = n < 100 ? .004 * n : .4;
String[] skin = MojangAPI.getSkin(skinName);
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(f));
@@ -76,19 +79,13 @@ public class BotManager implements Listener {
}
public void reset() {
for (Bot bot : bots) {
bot.remove();
}
bots.forEach(Bot::remove);
bots.clear();
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
PlayerConnection connection = ((CraftPlayer) event.getPlayer()).getHandle().playerConnection;
for (Bot bot : bots) {
bot.render(connection, true);
}
bots.forEach(b -> b.render(connection, true));
}
}

View File

@@ -8,17 +8,25 @@ import java.util.UUID;
public class CustomGameProfile extends GameProfile {
public CustomGameProfile(UUID uuid, String name, String skin) {
public CustomGameProfile(UUID uuid, String name, String[] skin) {
super(uuid, name);
setSkin(skin);
}
public void setSkin(String skin) {
String[] vals = MojangAPI.getSkin(skin);
public CustomGameProfile(UUID uuid, String name, String skinName) {
super(uuid, name);
if (vals != null) {
getProperties().put("textures", new Property("textures", vals[0], vals[1]));
setSkin(skinName);
}
public void setSkin(String skinName) {
setSkin(MojangAPI.getSkin(skinName));
}
public void setSkin(String[] skin) {
if (skin != null) {
getProperties().put("textures", new Property("textures", skin[0], skin[1]));
}
}
}

View File

@@ -15,15 +15,12 @@ import java.util.Set;
public class BotAgent {
private PlayerAI plugin;
private BotManager manager;
private final PlayerAI plugin;
private final BotManager manager;
private final BukkitScheduler scheduler;
private boolean enabled;
private int taskID;
private int count;
public BotAgent(BotManager manager) {
@@ -54,6 +51,7 @@ public class BotAgent {
bots.forEach(this::tickBot);
}
// This is where the code starts to get spicy
private void tickBot(Bot bot) {
Location loc = bot.getLocation();
@@ -71,17 +69,45 @@ public class BotAgent {
// if checkVertical(bot) { break block action add; return; }
// BotSituation situation = BotSituation.create(bot);
BotSituation situation = new BotSituation(bot, target);
// based on the situation, the bot can perform different actions
// there can be priorities assigned
if (bot.tickDelay(3)) attack(bot, player, loc);
// for building up, bot.setAction(BotAction.TOWER) or bot.startBuildingUp()
VerticalDisplacement disp = situation.getVerticalDisplacement();
// Later on maybe do bot.setAction(Action.MOVE) and what not instead of hardcoding it here
// bot.setSneaking(false);
move(bot, player, loc, target);
/*if (disp == VerticalDisplacement.ABOVE) {
if (bot.isOnGround()) { // checks this twice, again during .jump()
bot.sneak();
bot.look(BlockFace.DOWN);
bot.jump();
// bot.setSneaking(true);
// delay -> block place underneath and .setSneaking(false) << check possibilities of cancelling (add a cancel system)
// catch exceptions for slabs
scheduler.runTaskLater(plugin, () -> {
if (bot.isAlive()) {
bot.setItem(new ItemStack(Material.COBBLESTONE));
bot.attemptBlockPlace(loc, Material.COBBLESTONE);
}
}, 6);
} // maybe they will be in water or something, do not make them just do nothing here
} else {
move(bot, player, loc, target);
}*/
if (bot.tickDelay(3)) attack(bot, player, loc);
}
private void attack(Bot bot, Player player, Location loc) {
if (player.getNoDamageTicks() >= 5 || loc.distance(player.getLocation()) >= 4) return;
if (!PlayerUtils.isVulnerableGameMode(player.getGameMode()) || player.getNoDamageTicks() >= 5 || loc.distance(player.getLocation()) >= 4) return;
bot.attack(player);
}
@@ -90,9 +116,12 @@ public class BotAgent {
Vector vel = target.toVector().subtract(loc.toVector()).normalize();
if (bot.tickDelay(5)) bot.faceLocation(player.getLocation());
if (!bot.isOnGround()) return; // calling this a second time later on
bot.stand(); // eventually create a memory system so packets do not have to be sent every tick
bot.setItem(null); // method to check item in main hand, bot.getItemInHand()
try {
vel.checkFinite();
vel.add(bot.velocity);
} catch (IllegalArgumentException e) {
if (!MathUtils.isFinite(vel)) {

View File

@@ -0,0 +1,23 @@
package net.nuggetmc.ai.bot.agent;
import net.nuggetmc.ai.bot.Bot;
import org.bukkit.Location;
public class BotSituation {
private final VerticalDisplacement disp;
/*
* aboveGround
*/
public BotSituation(Bot bot, Location target) {
Location loc = bot.getLocation();
this.disp = VerticalDisplacement.fetch(loc.getBlockY(), target.getBlockY());
}
public VerticalDisplacement getVerticalDisplacement() {
return disp;
}
}

View File

@@ -0,0 +1,16 @@
package net.nuggetmc.ai.bot.agent;
public enum VerticalDisplacement {
AT,
ABOVE,
BELOW;
public static VerticalDisplacement fetch(int botY, int targetY) {
int diff = botY - targetY;
if (diff >= 2) return BELOW;
if (diff <= -2) return ABOVE;
return AT;
}
}

View File

@@ -5,7 +5,7 @@ import com.jonahseguin.drink.annotation.Command;
import com.jonahseguin.drink.command.DrinkCommandService;
import com.jonahseguin.drink.utils.ChatUtils;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.command.commands.PlayerAICommand;
import net.nuggetmc.ai.command.commands.MainCommand;
import org.bukkit.ChatColor;
import java.lang.reflect.Method;
@@ -22,11 +22,11 @@ public class CommandHandler {
public CommandHandler(PlayerAI plugin) {
drink = (DrinkCommandService) Drink.get(plugin);
drink.register(new PlayerAICommand(this), "playerai.manage", "bot", "playerai", "pai", "ai", "npc");
drink.register(new MainCommand(this), "playerai.manage", "bot", "playerai", "pai", "ai", "npc");
drink.registerCommands();
help = new HashMap<>();
setHelps(PlayerAICommand.class);
setHelps(MainCommand.class);
}
@SafeVarargs

View File

@@ -14,12 +14,12 @@ import org.bukkit.entity.Player;
import java.text.NumberFormat;
import java.util.Locale;
public class PlayerAICommand extends CommandInstance {
public class MainCommand extends CommandInstance {
private PlayerAI plugin;
private BotManager manager;
public PlayerAICommand(CommandHandler commandHandler) {
public MainCommand(CommandHandler commandHandler) {
super(commandHandler);
this.plugin = PlayerAI.getInstance();

View File

@@ -3,6 +3,7 @@ package net.nuggetmc.ai.utils;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.bot.Bot;
import net.nuggetmc.ai.bot.agent.BotAgent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.World;
@@ -10,6 +11,7 @@ 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 java.beans.Statement;
import java.util.ArrayList;
@@ -27,9 +29,17 @@ public class Debugger {
this.sender = sender;
}
public static void log(Object... objects) {
String[] values = formStringArray(objects);
Bukkit.getOnlinePlayers().stream().filter(ServerOperator::isOp).forEach(p -> p.sendMessage(PREFIX + String.join(" ", values)));
}
private static String[] formStringArray(Object[] objects) {
return Arrays.stream(objects).map(Object::toString).toArray(String[]::new);
}
private void print(Object... objects) {
String[] values = Arrays.stream(objects).map(Object::toString).toArray(String[]::new);
sender.sendMessage(PREFIX + String.join(" ", values));
sender.sendMessage(PREFIX + String.join(" ", formStringArray(objects)));
}
public void execute(String cmd) {
@@ -40,7 +50,7 @@ public class Debugger {
String name = cmd.substring(0, pts[0]);
String content = cmd.substring(pts[0] + 1, pts[1]);
Statement statement = new Statement(this, name, new Object[]{content});
Statement statement = new Statement(this, name, content.isEmpty() ? null : new Object[]{content});
print("Running the expression \"" + ChatColor.AQUA + cmd + ChatColor.RESET + "\"...");
statement.execute();
}
@@ -65,6 +75,20 @@ public class Debugger {
return list.toArray();
}
public void trackYVel() {
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
Bukkit.getScheduler().scheduleSyncRepeatingTask(PlayerAI.getInstance(), () -> {
print(player.getVelocity().getY());
}, 0, 1);
}
public void t() {
Bukkit.dispatchCommand(sender, "bot debug t(" + !PlayerUtils.getAllTargetable() + ")");
}
public void t(String content) {
Object[] obj = buildObjects(content);
@@ -83,7 +107,7 @@ public class Debugger {
}
}
public void hideNametags(String content) { // this works for some reason
public void hideNametags() { // this works for some reason
Set<Bot> bots = PlayerAI.getInstance().getManager().fetch();
for (Bot bot : bots) {
@@ -106,7 +130,7 @@ public class Debugger {
}
}
public void sit(String content) {
public void sit() {
Set<Bot> bots = PlayerAI.getInstance().getManager().fetch();
for (Bot bot : bots) {
@@ -130,7 +154,7 @@ public class Debugger {
}
}
public void look(String content) {
public void look() {
if (!(sender instanceof Player)) {
print("Unspecified player.");
return;
@@ -152,7 +176,7 @@ public class Debugger {
Arrays.stream(buildObjects(content)).forEach(this::print);
}
public void toggleAgent(String content) {
public void toggleAgent() {
BotAgent agent = PlayerAI.getInstance().getManager().getAgent();
boolean b = agent.isEnabled();

View File

@@ -28,6 +28,26 @@ public class MathUtils {
return out;
}
public static float fetchPitch(Vector dir) {
double x = dir.getX();
double z = dir.getZ();
float result;
if (x == 0.0D && z == 0.0D) {
result = (float) (dir.getY() > 0.0D ? -90 : 90);
}
else {
double x2 = NumberConversions.square(x);
double z2 = NumberConversions.square(z);
double xz = Math.sqrt(x2 + z2);
result = (float) Math.toDegrees(Math.atan(-dir.getY() / xz));
}
return result;
}
public static Vector circleOffset(double r) {
double rad = 2 * Math.random() * Math.PI;

View File

@@ -7,7 +7,11 @@ public class PlayerUtils {
public static boolean allTargetable;
public static boolean isTargetable(GameMode mode) {
return allTargetable || mode == GameMode.SURVIVAL || mode == GameMode.ADVENTURE;
return allTargetable || isVulnerableGameMode(mode);
}
public static boolean isVulnerableGameMode(GameMode mode) {
return mode == GameMode.SURVIVAL || mode == GameMode.ADVENTURE;
}
public static void setAllTargetable(boolean b) {