bot physics complete

This commit is contained in:
batchprogrammer314
2021-06-26 19:43:58 -05:00
commit 26959ace6c
14 changed files with 943 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 batchprogrammer314
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

12
PlayerAI.iml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="spigot" level="project" />
</component>
</module>

View File

@@ -0,0 +1,68 @@
package net.nuggetmc.ai;
import net.nuggetmc.ai.cmd.CommandHandler;
import net.nuggetmc.ai.cmd.CommandInterface;
import net.nuggetmc.ai.cmd.commands.CreateCommand;
import net.nuggetmc.ai.cmd.commands.DebugCommand;
import net.nuggetmc.ai.cmd.commands.InfoCommand;
import net.nuggetmc.ai.cmd.commands.ResetCommand;
import net.nuggetmc.ai.npc.NPCManager;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
public class PlayerAI extends JavaPlugin {
private static PlayerAI instance;
private final CommandHandler HANDLER;
private final NPCManager MANAGER;
public PlayerAI() {
instance = this;
this.HANDLER = new CommandHandler();
this.MANAGER = new NPCManager(this);
}
public static PlayerAI getInstance() {
return instance;
}
public CommandHandler getHandler() {
return HANDLER;
}
public NPCManager getManager() {
return MANAGER;
}
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(MANAGER, this);
registerCommands();
MANAGER.connectAll();
}
@Override
public void onDisable() {
MANAGER.reset();
MANAGER.disconnectAll();
}
private void registerCommands() {
HANDLER.register(new CommandInterface[] {
new CreateCommand(),
new InfoCommand(),
new DebugCommand(),
new ResetCommand()
});
PluginCommand command = getCommand("playerai");
if (command != null) {
command.setExecutor(HANDLER);
command.setTabCompleter(HANDLER);
}
}
}

View File

@@ -0,0 +1,112 @@
package net.nuggetmc.ai.cmd;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import java.util.*;
public class CommandHandler implements CommandExecutor, TabCompleter {
private final Map<String, CommandInterface> COMMANDS = new HashMap<>();
private final String PREFIX = "bot";
public void register(CommandInterface[] subCmds) {
for (CommandInterface cmd : subCmds) {
COMMANDS.put(cmd.getName(), cmd);
}
}
public String fetchName(CommandInterface subCmd) {
String name = subCmd.getClass().getSimpleName();
return name.substring(0, name.length() - 7).toLowerCase();
}
private boolean exists(String name) {
return COMMANDS.containsKey(name.toLowerCase());
}
private CommandInterface getExecutor(String name) {
return COMMANDS.get(name.toLowerCase());
}
public String nonPlayerMsg() {
return ChatColor.RED + "You must be a player to execute this command!";
}
public String usageMsg(CommandInterface subCmd) {
return ChatColor.RED + "Invalid arguments!\nUsage: /" + PREFIX + " " + subCmd.getName() + " " + subCmd.getUsage();
}
private void sendCmdInfo(CommandSender sender) {
sender.sendMessage(ChatColor.GRAY + "----------------------------------------");
sender.sendMessage(ChatColor.GOLD + "PlayerAI" + ChatColor.GRAY + " [" + ChatColor.RED + "v1.0" + ChatColor.GRAY + "]");
for (Map.Entry<String, CommandInterface> entry : COMMANDS.entrySet()) {
sender.sendMessage(ChatColor.GRAY + "" + ChatColor.YELLOW + "/" + PREFIX + " " + entry.getKey() + ChatColor.GRAY + ""
+ ChatColor.RESET + entry.getValue().getDescription());
}
sender.sendMessage(ChatColor.GRAY + "----------------------------------------");
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (args.length == 0 || !exists(args[0])) {
sendCmdInfo(sender);
return true;
}
getExecutor(args[0]).onCommand(sender, cmd, label, Arrays.copyOfRange(args, 1, args.length));
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
if (!cmd.getName().equals("playerai")) return null;
List<String> groupnames;
int n = args.length;
switch (n) {
case 1:
groupnames = new ArrayList<>(COMMANDS.keySet());
String arg = args[n - 1];
if (!isEmptyTab(arg)) {
return autofill(groupnames, arg);
}
return groupnames;
default:
return null;
}
}
private boolean isEmptyTab(String s) {
return s == null || s.equals("") || s.equals(" ") || s.isEmpty();
}
private List<String> autofill(List<String> groupnames, String input) {
List<String> list = new ArrayList<>();
for (String entry : groupnames) {
if (entry.length() >= input.length()) {
if (input.equalsIgnoreCase(entry.substring(0, input.length()))) {
list.add(entry);
}
}
}
if (list.isEmpty()) {
return groupnames;
}
return list;
}
}

View File

@@ -0,0 +1,12 @@
package net.nuggetmc.ai.cmd;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
public interface CommandInterface {
String getName();
String getDescription();
String getUsage();
void onCommand(CommandSender sender, Command cmd, String label, String[] args);
}

View File

@@ -0,0 +1,63 @@
package net.nuggetmc.ai.cmd.commands;
import net.md_5.bungee.api.ChatColor;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.cmd.CommandHandler;
import net.nuggetmc.ai.cmd.CommandInterface;
import net.nuggetmc.ai.npc.NPC;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CreateCommand implements CommandInterface {
private final CommandHandler HANDLER;
private final String NAME;
private final String DESCRIPTION = "Create NPCs.";
private final String CMD_ARGS = "<name> [skin]";
public CreateCommand() {
this.HANDLER = PlayerAI.getInstance().getHandler();
this.NAME = HANDLER.fetchName(this);
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getUsage() {
return CMD_ARGS;
}
@Override
public void onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(HANDLER.nonPlayerMsg());
return;
}
if (args.length == 0) {
sender.sendMessage(HANDLER.usageMsg(this));
return;
}
String skin;
if (args.length > 1) {
skin = args[1];
} else {
skin = args[0];
}
Player player = (Player) sender;
NPC npc = NPC.createNPC(args[0], player.getLocation(), skin);
}
}

View File

@@ -0,0 +1,52 @@
package net.nuggetmc.ai.cmd.commands;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.cmd.CommandHandler;
import net.nuggetmc.ai.cmd.CommandInterface;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashSet;
import java.util.Set;
public class DebugCommand implements CommandInterface {
private final CommandHandler HANDLER;
private final String NAME;
private final String DESCRIPTION = "Debug NPC stats.";
private final String CMD_ARGS = "";
public DebugCommand() {
this.HANDLER = PlayerAI.getInstance().getHandler();
this.NAME = HANDLER.fetchName(this);
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getUsage() {
return CMD_ARGS;
}
private boolean active = false;
private Set<BukkitRunnable> tasks = new HashSet<>();
private double round2Dec(double n) {
return Math.round(n * 100) / 100.0;
}
@Override
public void onCommand(CommandSender sender, Command cmd, String label, String[] args) {
}
}

View File

@@ -0,0 +1,40 @@
package net.nuggetmc.ai.cmd.commands;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.cmd.CommandHandler;
import net.nuggetmc.ai.cmd.CommandInterface;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
public class InfoCommand implements CommandInterface {
private final CommandHandler HANDLER;
private final String NAME;
private final String DESCRIPTION = "Information about loaded NPCs.";
private final String CMD_ARGS = "";
public InfoCommand() {
this.HANDLER = PlayerAI.getInstance().getHandler();
this.NAME = HANDLER.fetchName(this);
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getUsage() {
return CMD_ARGS;
}
@Override
public void onCommand(CommandSender sender, Command cmd, String label, String[] args) {
}
}

View File

@@ -0,0 +1,64 @@
package net.nuggetmc.ai.cmd.commands;
import net.md_5.bungee.api.ChatColor;
import net.nuggetmc.ai.PlayerAI;
import net.nuggetmc.ai.cmd.CommandHandler;
import net.nuggetmc.ai.cmd.CommandInterface;
import net.nuggetmc.ai.npc.NPCManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.text.NumberFormat;
import java.util.Locale;
public class ResetCommand implements CommandInterface {
private final PlayerAI INSTANCE;
private final CommandHandler HANDLER;
private final NPCManager MANAGER;
private final String NAME;
private final String DESCRIPTION = "Information about loaded NPCs.";
private final String CMD_ARGS = "";
public ResetCommand() {
this.INSTANCE = PlayerAI.getInstance();
this.HANDLER = INSTANCE.getHandler();
this.MANAGER = INSTANCE.getManager();
this.NAME = HANDLER.fetchName(this);
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getUsage() {
return CMD_ARGS;
}
@Override
public void onCommand(CommandSender sender, Command cmd, String label, String[] args) {
sender.sendMessage("Removing every NPC...");
int size = MANAGER.fetch().size();
MANAGER.reset();
String en;
if (size == 1) {
en = "y";
} else {
en = "ies";
}
String formatted = NumberFormat.getNumberInstance(Locale.US).format(size);
sender.sendMessage("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + en + ".");
}
}

View File

@@ -0,0 +1,282 @@
package net.nuggetmc.ai.npc;
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.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;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.UUID;
public class NPC extends EntityPlayer {
public Vector velocity;
private byte kbTicks;
private final double REGEN_AMOUNT = 0.05;
public NPC(MinecraftServer minecraftServer, WorldServer worldServer, GameProfile profile, PlayerInteractManager manager) {
super(minecraftServer, worldServer, profile, manager);
velocity = new Vector(0, 0, 0);
kbTicks = 0;
}
public static NPC createNPC(String name, Location loc, String skin) {
MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
WorldServer nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
UUID uuid = SteveUUID.generate();
GameProfile profile = new GameProfile(uuid, name);
PlayerInteractManager interactManager = new PlayerInteractManager(nmsWorld);
if (skin != null) {
setSkin(profile, skin);
}
NPC npc = new NPC(nmsServer, nmsWorld, profile, interactManager);
npc.playerConnection = new PlayerConnection(nmsServer, new NetworkManager(EnumProtocolDirection.CLIENTBOUND), npc);
npc.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
npc.getBukkitEntity().setNoDamageTicks(0);
nmsWorld.addEntity(npc);
sendSpawnPackets(npc);
PlayerAI.getInstance().getManager().add(npc);
return npc;
}
private static void setSkin(GameProfile profile, String skin) {
String[] vals = MojangAPI.getSkin(skin);
if (vals != null) {
profile.getProperties().put("textures", new Property("textures", vals[0], vals[1]));
}
}
private static void sendSpawnPackets(NPC npc) {
DataWatcher watcher = npc.getDataWatcher();
watcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF);
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
npc.render(connection, false);
}
}
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));
connection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, this));
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);
}
}
public void setVelocity(Vector vector) {
this.velocity = vector;
}
public void addVelocity(Vector vector) {
this.velocity.add(vector);
}
@Override
public void tick() {
super.tick();
if (noDamageTicks > 0) --noDamageTicks;
if (kbTicks > 0) --kbTicks;
Player playerNPC = this.getBukkitEntity();
if (playerNPC.isDead()) return;
double health = playerNPC.getHealth();
double maxHealth = playerNPC.getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue();
double amount;
if (health < maxHealth - REGEN_AMOUNT) {
amount = health + REGEN_AMOUNT;
} else {
amount = maxHealth;
}
playerNPC.setHealth(amount);
updateLocation();
}
private void updateLocation() {
velocity.setY(velocity.getY() - 0.1);
if (predictGround()) {
velocity.setY(0);
addFriction();
}
this.move(EnumMoveType.SELF, new Vec3D(velocity.getX(), velocity.getY(), velocity.getZ()));
}
private final double OFFSET = 0.05;
public boolean predictGround() {
double vy = velocity.getY();
if (vy > 0) {
return false;
}
double m = vy / 20.0;
World world = getBukkitEntity().getWorld();
AxisAlignedBB box = getBoundingBox();
double[] xVals = new double[] {
box.minX + OFFSET,
box.maxX - OFFSET
};
double[] zVals = new double[] {
box.minZ + OFFSET,
box.maxZ - OFFSET
};
for (double x : xVals) {
for (double z : zVals) {
double i = locY();
for (int n = 0; n < 20; n++) {
Location test = new Location(world, x, i, z);
if (test.getBlock().getType().isSolid()) {
return true;
}
i += m;
}
}
}
return false;
}
public void addFriction() {
velocity.setX(velocity.getX() * 0.5);
velocity.setZ(velocity.getZ() * 0.5);
}
public void despawn() {
getBukkitEntity().remove();
}
@Override
public void collide(Entity entity) {
if (!this.isSameVehicle(entity) && !entity.noclip && !this.noclip) {
double d0 = entity.locX() - this.locX();
double d1 = entity.locZ() - this.locZ();
double d2 = MathHelper.a(d0, d1);
if (d2 >= 0.009999999776482582D) {
d2 = MathHelper.sqrt(d2);
d0 /= d2;
d1 /= d2;
double d3 = 1.0D / d2;
if (d3 > 1.0D) {
d3 = 1.0D;
}
d0 *= d3;
d1 *= d3;
d0 *= 0.05000000074505806D;
d1 *= 0.05000000074505806D;
d0 *= 1.0F - this.I;
d1 *= 1.0F - this.I;
if (!this.isVehicle()) {
velocity.add(new Vector(-d0 * 3, 0.0D, -d1 * 3));
}
if (!entity.isVehicle()) {
entity.i(d0, 0.0D, d1);
}
}
}
}
@Override
public boolean damageEntity(DamageSource damagesource, float f) {
boolean damaged = super.damageEntity(damagesource, f);
net.minecraft.server.v1_16_R3.Entity attacker = damagesource.getEntity();
if (damaged && kbTicks == 0 && attacker != null) {
Player player = getBukkitEntity();
CraftEntity entity = attacker.getBukkitEntity();
Location loc1 = player.getLocation();
Location loc2 = entity.getLocation();
kb(player, loc1, loc2);
}
return damaged;
}
private void kb(Player playerNPC, Location loc1, Location loc2) {
Vector diff = loc1.toVector().subtract(loc2.toVector()).normalize();
diff.multiply(0.25);
diff.setY(0.5);
velocity.add(diff);
kbTicks = 10;
}
public void faceLocation(Location loc) {
try {
CraftPlayer playerNPC = this.getBukkitEntity();
Vector dir = loc.toVector().subtract(playerNPC.getLocation().toVector()).normalize();
Location facing = playerNPC.getLocation().setDirection(dir);
playerNPC.teleport(facing);
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
connection.sendPacket(new PacketPlayOutEntityHeadRotation(playerNPC.getHandle(), (byte) (facing.getYaw() * 256 / 360)));
}
} catch (IllegalArgumentException ignored) { }
}
@Override
public void playerTick() {
if (this.hurtTicks > 0) {
this.hurtTicks -= 1;
}
entityBaseTick();
tickPotionEffects();
this.aU = (int) this.aT;
this.aL = this.aK;
this.lastYaw = this.yaw;
this.lastPitch = this.pitch;
}
}

View File

@@ -0,0 +1,153 @@
package net.nuggetmc.ai.npc;
import io.netty.channel.*;
import net.minecraft.server.v1_16_R3.PacketPlayOutEntityMetadata;
import net.minecraft.server.v1_16_R3.PacketPlayOutNamedEntitySpawn;
import net.minecraft.server.v1_16_R3.PacketPlayOutPlayerInfo;
import net.minecraft.server.v1_16_R3.PlayerConnection;
import net.nuggetmc.ai.PlayerAI;
import org.bukkit.Bukkit;
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.event.player.PlayerQuitEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class NPCManager implements Listener {
private PlayerAI plugin;
private final Set<NPC> NPCS = new HashSet<>();
private final Map<Integer, NPC> NPC_CONNECTIONS = new HashMap<>();
public Set<NPC> fetch() {
return NPCS;
}
public void add(NPC npc) {
NPCS.add(npc);
Bukkit.getScheduler().runTaskLater(PlayerAI.getInstance(), () -> {
NPC_CONNECTIONS.put(npc.getId(), npc);
}, 10);
}
public NPCManager(PlayerAI instance) {
plugin = instance;
}
public void reset() {
for (NPC npc : NPCS) {
npc.despawn();
}
NPCS.clear();
NPC_CONNECTIONS.clear();
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
PlayerConnection connection = ((CraftPlayer) event.getPlayer()).getHandle().playerConnection;
for (NPC npc : NPCS) {
npc.render(connection, true);
}
injectPlayer(event.getPlayer());
}
public void connectAll() {
for (Player player : Bukkit.getOnlinePlayers()) {
injectPlayer(player);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
removePlayer(event.getPlayer());
}
public void disconnectAll() {
for (Player player : Bukkit.getOnlinePlayers()) {
removePlayer(player);
}
}
private void injectPlayer(Player player) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void channelRead(ChannelHandlerContext channelHandlerContext, Object packet) throws Exception {
super.channelRead(channelHandlerContext, packet);
}
@Override
public void write(ChannelHandlerContext channelHandlerContext, Object packet, ChannelPromise channelPromise) throws Exception {
if (packet instanceof PacketPlayOutNamedEntitySpawn) {
renderNPC(player, (PacketPlayOutNamedEntitySpawn) packet);
}
super.write(channelHandlerContext, packet, channelPromise);
}
};
ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().playerConnection.networkManager.channel.pipeline();
try {
pipeline.addBefore("packet_handler", player.getName(), channelDuplexHandler);
} catch (IllegalArgumentException ignore) { }
}
private void renderNPC(Player player, PacketPlayOutNamedEntitySpawn packet) {
PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection;
Field field;
try {
field = packet.getClass().getDeclaredField("a");
} catch (NoSuchFieldException e) {
return;
}
field.setAccessible(true);
Object obj;
try {
obj = field.get(packet);
} catch (IllegalAccessException e) {
return;
}
if (!(obj instanceof Integer)) return;
int n = (int) obj;
NPC npc = NPC_CONNECTIONS.get(n);
if (npc == null) return;
connection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, npc));
connection.sendPacket(new PacketPlayOutEntityMetadata(npc.getId(), npc.getDataWatcher(), true));
PacketPlayOutPlayerInfo noTabPacket = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, npc);
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (!connection.isDisconnected()) {
connection.sendPacket(noTabPacket);
}
}, 5);
}
public void removePlayer(Player player) {
Channel channel = ((CraftPlayer) player).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> {
channel.pipeline().remove(player.getName());
return null;
});
}
}

View File

@@ -0,0 +1,28 @@
package net.nuggetmc.ai.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.InputStreamReader;
import java.net.URL;
public class MojangAPI {
public static String[] getSkin(String name) {
try {
URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
InputStreamReader reader = new InputStreamReader(url.openStream());
String uuid = new JsonParser().parse(reader).getAsJsonObject().get("id").getAsString();
URL url2 = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false");
InputStreamReader reader2 = new InputStreamReader(url2.openStream());
JsonObject property = new JsonParser().parse(reader2).getAsJsonObject().get("properties").getAsJsonArray().get(0).getAsJsonObject();
String texture = property.get("value").getAsString();
String signature = property.get("signature").getAsString();
return new String[] {texture, signature};
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,16 @@
package net.nuggetmc.ai.utils;
import java.util.UUID;
public class SteveUUID {
public static UUID generate() {
UUID uuid = UUID.randomUUID();
if (uuid.hashCode() % 2 == 0) {
return uuid;
}
return generate();
}
}

20
src/plugin.yml Normal file
View File

@@ -0,0 +1,20 @@
name: PlayerAI
main: net.nuggetmc.ai.PlayerAI
version: 1.0
api-version: 1.16
author: HorseNuggets
commands:
playerai:
description: The PlayerAI base command.
permission: playerai.manage
usage: /playerai
aliases: [bot, pai, ai]
permissions:
playerai.*:
description: PlayerAI parent permission.
default: op
children:
playerai.manage: true
playerai.manage:
description: Allows for PlayerAI NPC management.
default: op