bot physics complete
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal 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
12
PlayerAI.iml
Normal 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>
|
||||||
68
src/net/nuggetmc/ai/PlayerAI.java
Normal file
68
src/net/nuggetmc/ai/PlayerAI.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/net/nuggetmc/ai/cmd/CommandHandler.java
Normal file
112
src/net/nuggetmc/ai/cmd/CommandHandler.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/net/nuggetmc/ai/cmd/CommandInterface.java
Normal file
12
src/net/nuggetmc/ai/cmd/CommandInterface.java
Normal 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);
|
||||||
|
}
|
||||||
63
src/net/nuggetmc/ai/cmd/commands/CreateCommand.java
Normal file
63
src/net/nuggetmc/ai/cmd/commands/CreateCommand.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/net/nuggetmc/ai/cmd/commands/DebugCommand.java
Normal file
52
src/net/nuggetmc/ai/cmd/commands/DebugCommand.java
Normal 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/net/nuggetmc/ai/cmd/commands/InfoCommand.java
Normal file
40
src/net/nuggetmc/ai/cmd/commands/InfoCommand.java
Normal 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/net/nuggetmc/ai/cmd/commands/ResetCommand.java
Normal file
64
src/net/nuggetmc/ai/cmd/commands/ResetCommand.java
Normal 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 + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
282
src/net/nuggetmc/ai/npc/NPC.java
Normal file
282
src/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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/net/nuggetmc/ai/npc/NPCManager.java
Normal file
153
src/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> 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/net/nuggetmc/ai/utils/MojangAPI.java
Normal file
28
src/net/nuggetmc/ai/utils/MojangAPI.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/net/nuggetmc/ai/utils/SteveUUID.java
Normal file
16
src/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();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/plugin.yml
Normal file
20
src/plugin.yml
Normal 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
|
||||||
Reference in New Issue
Block a user