Command system rewrite using Drink and Maven support.
This commit is contained in:
54
src/main/java/net/nuggetmc/ai/PlayerAI.java
Normal file
54
src/main/java/net/nuggetmc/ai/PlayerAI.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package net.nuggetmc.ai;
|
||||
|
||||
import net.nuggetmc.ai.commands.CommandHandler;
|
||||
import net.nuggetmc.ai.npc.NPCManager;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class PlayerAI extends JavaPlugin {
|
||||
|
||||
public static final double VERSION = 1.0;
|
||||
|
||||
private static PlayerAI instance;
|
||||
|
||||
private CommandHandler handler;
|
||||
private NPCManager manager;
|
||||
|
||||
public static PlayerAI getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public CommandHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public NPCManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
// Create Instances
|
||||
this.handler = new CommandHandler();
|
||||
this.manager = new NPCManager(this);
|
||||
|
||||
// Register all the things
|
||||
this.registerEvents();
|
||||
|
||||
// Create Netty injections
|
||||
manager.connectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
manager.reset();
|
||||
manager.disconnectAll();
|
||||
}
|
||||
|
||||
private void registerEvents() {
|
||||
getServer().getPluginManager().registerEvents(manager, this);
|
||||
}
|
||||
|
||||
}
|
||||
45
src/main/java/net/nuggetmc/ai/commands/CommandHandler.java
Normal file
45
src/main/java/net/nuggetmc/ai/commands/CommandHandler.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.nuggetmc.ai.commands;
|
||||
|
||||
import com.jonahseguin.drink.CommandService;
|
||||
import com.jonahseguin.drink.Drink;
|
||||
import com.jonahseguin.drink.annotation.Command;
|
||||
import com.jonahseguin.drink.command.DrinkCommandContainer;
|
||||
import com.jonahseguin.drink.command.DrinkCommandService;
|
||||
import net.nuggetmc.ai.PlayerAI;
|
||||
import net.nuggetmc.ai.commands.commands.PlayerAICommand;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommandHandler {
|
||||
|
||||
private final DrinkCommandService drink;
|
||||
|
||||
public CommandHandler() {
|
||||
drink = (DrinkCommandService) Drink.get(PlayerAI.getInstance());
|
||||
drink.register(new PlayerAICommand(this), "playerai", "pai");
|
||||
drink.registerCommands();
|
||||
}
|
||||
|
||||
public List<String> getUsage(Class<? extends CommandInstance> clazz) {
|
||||
return getSubCommands(clazz).stream().map(c -> {
|
||||
Command command = c.getAnnotation(Command.class);
|
||||
return ChatColor.GRAY + " ▪ " + ChatColor.YELLOW + "/" + getRootName(clazz) + (command.name().isEmpty() ? "" : " " + command.name()) + ChatColor.GRAY + " ▪ "
|
||||
+ ChatColor.RESET + command.desc();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getRootName(Class<? extends CommandInstance> clazz) {
|
||||
return drink.getCommands().entrySet().stream()
|
||||
.filter(c -> c.getValue().getObject().getClass().isAssignableFrom(clazz)).map(Map.Entry::getKey).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private List<Method> getSubCommands(Class<? extends CommandInstance> clazz) {
|
||||
return Arrays.stream(clazz.getDeclaredMethods()).filter(m -> m.isAnnotationPresent(Command.class)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/java/net/nuggetmc/ai/commands/CommandInstance.java
Normal file
14
src/main/java/net/nuggetmc/ai/commands/CommandInstance.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package net.nuggetmc.ai.commands;
|
||||
|
||||
public abstract class CommandInstance {
|
||||
|
||||
private final CommandHandler commandHandler;
|
||||
|
||||
public CommandInstance(CommandHandler commandHandler) {
|
||||
this.commandHandler = commandHandler;
|
||||
}
|
||||
|
||||
public CommandHandler getCommandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.nuggetmc.ai.commands.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 net.nuggetmc.ai.PlayerAI;
|
||||
import net.nuggetmc.ai.commands.CommandHandler;
|
||||
import net.nuggetmc.ai.commands.CommandInstance;
|
||||
import net.nuggetmc.ai.npc.NPC;
|
||||
import net.nuggetmc.ai.npc.NPCManager;
|
||||
import net.nuggetmc.ai.utils.ChatUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
public class PlayerAICommand extends CommandInstance {
|
||||
|
||||
public PlayerAICommand(CommandHandler commandHandler) {
|
||||
super(commandHandler);
|
||||
}
|
||||
|
||||
@Command(name = "", desc = "Test Description")
|
||||
public void rootCommand(@Sender Player sender) {
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GOLD + "PlayerAI" + ChatColor.GRAY + " [" + ChatColor.RED + "v" + PlayerAI.VERSION + ChatColor.GRAY + "]");
|
||||
|
||||
for (String s : this.getCommandHandler().getUsage(PlayerAICommand.class)) {
|
||||
sender.sendMessage(s);
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
}
|
||||
|
||||
@Command(name = "create", desc = "Create bots.", usage = "<name> [skin]")
|
||||
public void createBotCommand(@Sender Player sender, String name, @OptArg() String skin) {
|
||||
NPC.createNPC(name, sender.getLocation(), skin.isEmpty() ? name : skin);
|
||||
}
|
||||
|
||||
@Command(name = "debug", desc = "Debug bot stats.")
|
||||
public void debugCommand(@Sender Player sender) {
|
||||
|
||||
}
|
||||
|
||||
@Command(name = "info", desc = "Information about loaded bots.")
|
||||
public void infoCommand(@Sender Player sender) {
|
||||
|
||||
}
|
||||
|
||||
@Command(name = "reset", desc = "Remove all loaded bots.")
|
||||
public void resetCommand(@Sender Player sender) {
|
||||
sender.sendMessage("Removing every bot...");
|
||||
NPCManager manager = PlayerAI.getInstance().getManager();
|
||||
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") + ".");
|
||||
}
|
||||
|
||||
}
|
||||
282
src/main/java/net/nuggetmc/ai/npc/NPC.java
Normal file
282
src/main/java/net/nuggetmc/ai/npc/NPC.java
Normal 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 regenAmount = 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 - regenAmount) {
|
||||
amount = health + regenAmount;
|
||||
} 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 bbOffset = 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 + bbOffset,
|
||||
box.maxX - bbOffset
|
||||
};
|
||||
|
||||
double[] zVals = new double[] {
|
||||
box.minZ + bbOffset,
|
||||
box.maxZ - bbOffset
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
153
src/main/java/net/nuggetmc/ai/npc/NPCManager.java
Normal file
153
src/main/java/net/nuggetmc/ai/npc/NPCManager.java
Normal 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> npcConnections = new HashMap<>();
|
||||
|
||||
public Set<NPC> fetch() {
|
||||
return npcs;
|
||||
}
|
||||
|
||||
public void add(NPC npc) {
|
||||
npcs.add(npc);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(PlayerAI.getInstance(), () -> {
|
||||
npcConnections.put(npc.getId(), npc);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
public NPCManager(PlayerAI instance) {
|
||||
plugin = instance;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (NPC npc : npcs) {
|
||||
npc.despawn();
|
||||
}
|
||||
|
||||
npcs.clear();
|
||||
npcConnections.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 = npcConnections.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;
|
||||
});
|
||||
}
|
||||
}
|
||||
7
src/main/java/net/nuggetmc/ai/utils/ChatUtils.java
Normal file
7
src/main/java/net/nuggetmc/ai/utils/ChatUtils.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
public class ChatUtils {
|
||||
public static String LINE = ChatColor.GRAY + "------------------------------------------------";
|
||||
}
|
||||
24
src/main/java/net/nuggetmc/ai/utils/MojangAPI.java
Normal file
24
src/main/java/net/nuggetmc/ai/utils/MojangAPI.java
Normal file
@@ -0,0 +1,24 @@
|
||||
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 {
|
||||
String uuid = new JsonParser().parse(new InputStreamReader(new URL("https://api.mojang.com/users/profiles/minecraft/" + name)
|
||||
.openStream())).getAsJsonObject().get("id").getAsString();
|
||||
JsonObject property = new JsonParser()
|
||||
.parse(new InputStreamReader(new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false")
|
||||
.openStream())).getAsJsonObject().get("properties").getAsJsonArray().get(0).getAsJsonObject();
|
||||
return new String[] {property.get("value").getAsString(), property.get("signature").getAsString()};
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/java/net/nuggetmc/ai/utils/SteveUUID.java
Normal file
16
src/main/java/net/nuggetmc/ai/utils/SteveUUID.java
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user