Moved to gradle & started work on separating API and plugin
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package net.nuggetmc.tplus;
|
||||
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.command.CommandHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TerminatorPlus extends JavaPlugin {
|
||||
|
||||
private static TerminatorPlus instance;
|
||||
private static String version;
|
||||
|
||||
private BotManager manager;
|
||||
private CommandHandler handler;
|
||||
|
||||
public static TerminatorPlus getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public BotManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
public CommandHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
version = getDescription().getVersion();
|
||||
|
||||
// Create Instances
|
||||
this.manager = new BotManager();
|
||||
this.handler = new CommandHandler(this);
|
||||
|
||||
// Register event listeners
|
||||
this.registerEvents(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
manager.reset();
|
||||
}
|
||||
|
||||
private void registerEvents(Listener... listeners) {
|
||||
Arrays.stream(listeners).forEach(li -> this.getServer().getPluginManager().registerEvents(li, this));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,791 @@
|
||||
package net.nuggetmc.tplus.bot;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.api.Terminator;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.tplus.bot.agent.Agent;
|
||||
import net.nuggetmc.tplus.bot.event.BotDamageByPlayerEvent;
|
||||
import net.nuggetmc.tplus.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.tplus.bot.event.BotKilledByPlayerEvent;
|
||||
import net.nuggetmc.tplus.utils.*;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftEquipmentSlot;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Damageable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Bot extends ServerPlayer implements Terminator {
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BukkitScheduler scheduler;
|
||||
private final Agent agent;
|
||||
private final Vector offset;
|
||||
public ItemStack defaultItem;
|
||||
private NeuralNetwork network;
|
||||
private boolean shield;
|
||||
private boolean blocking;
|
||||
private boolean blockUse;
|
||||
private Vector velocity;
|
||||
private Vector oldVelocity;
|
||||
private boolean removeOnDeath;
|
||||
private int aliveTicks;
|
||||
private int kills;
|
||||
private byte fireTicks;
|
||||
private byte groundTicks;
|
||||
private byte jumpTicks;
|
||||
private byte noFallTicks;
|
||||
private Bot(MinecraftServer minecraftServer, ServerLevel worldServer, GameProfile profile) {
|
||||
super(minecraftServer, worldServer, profile);
|
||||
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.agent = plugin.getManager().getAgent();
|
||||
this.defaultItem = new ItemStack(Material.AIR);
|
||||
this.velocity = new Vector(0, 0, 0);
|
||||
this.oldVelocity = velocity.clone();
|
||||
this.noFallTicks = 60;
|
||||
this.fireTicks = 0;
|
||||
this.removeOnDeath = true;
|
||||
this.offset = MathUtils.circleOffset(3);
|
||||
|
||||
//this.entityData.set(new EntityDataAccessor<>(16, EntityDataSerializers.BYTE), (byte) 0xFF);
|
||||
}
|
||||
|
||||
public static Bot createBot(Location loc, String name) {
|
||||
return createBot(loc, name, MojangAPI.getSkin(name));
|
||||
}
|
||||
|
||||
public static Bot createBot(Location loc, String name, String[] skin) {
|
||||
MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle();
|
||||
|
||||
UUID uuid = BotUtils.randomSteveUUID();
|
||||
|
||||
CustomGameProfile profile = new CustomGameProfile(uuid, ChatUtils.trim16(name), skin);
|
||||
|
||||
Bot bot = new Bot(nmsServer, nmsWorld, profile);
|
||||
|
||||
bot.connection = new ServerGamePacketListenerImpl(nmsServer, new Connection(PacketFlow.CLIENTBOUND) {
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
|
||||
|
||||
}
|
||||
}, bot);
|
||||
|
||||
bot.setPos(loc.getX(), loc.getY(), loc.getZ());
|
||||
bot.setRot(loc.getYaw(), loc.getPitch());
|
||||
bot.getBukkitEntity().setNoDamageTicks(0);
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(
|
||||
new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, bot)));
|
||||
nmsWorld.addFreshEntity(bot);
|
||||
bot.renderAll();
|
||||
|
||||
TerminatorPlus.getInstance().getManager().add(bot);
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBotName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeuralNetwork getNeuralNetwork() {
|
||||
return network;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeuralNetwork(NeuralNetwork network) {
|
||||
this.network = network;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNeuralNetwork() {
|
||||
return network != null;
|
||||
}
|
||||
|
||||
private void renderAll() {
|
||||
Packet<?>[] packets = getRenderPacketsNoInfo();
|
||||
Bukkit.getOnlinePlayers().forEach(p -> renderNoInfo(((CraftPlayer) p).getHandle().connection, packets, false));
|
||||
}
|
||||
|
||||
private void render(ServerGamePacketListenerImpl connection, Packet<?>[] packets, boolean login) {
|
||||
connection.send(packets[0]);
|
||||
connection.send(packets[1]);
|
||||
connection.send(packets[2]);
|
||||
|
||||
if (login) {
|
||||
scheduler.runTaskLater(plugin, () -> connection.send(packets[3]), 10);
|
||||
} else {
|
||||
connection.send(packets[3]);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderNoInfo(ServerGamePacketListenerImpl connection, Packet<?>[] packets, boolean login) {
|
||||
connection.send(packets[0]);
|
||||
connection.send(packets[1]);
|
||||
|
||||
if (login) {
|
||||
scheduler.runTaskLater(plugin, () -> connection.send(packets[2]), 10);
|
||||
} else {
|
||||
connection.send(packets[2]);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(ServerGamePacketListenerImpl connection, boolean login) {
|
||||
render(connection, getRenderPackets(), login);
|
||||
}
|
||||
|
||||
private Packet<?>[] getRenderPackets() {
|
||||
return new Packet[]{
|
||||
new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, this),
|
||||
new ClientboundAddPlayerPacket(this),
|
||||
new ClientboundSetEntityDataPacket(this.getId(), this.entityData, true),
|
||||
new ClientboundRotateHeadPacket(this, (byte) ((this.yHeadRot * 256f) / 360f))
|
||||
};
|
||||
}
|
||||
|
||||
private Packet<?>[] getRenderPacketsNoInfo() {
|
||||
return new Packet[]{
|
||||
new ClientboundAddPlayerPacket(this),
|
||||
new ClientboundSetEntityDataPacket(this.getId(), this.entityData, true),
|
||||
new ClientboundRotateHeadPacket(this, (byte) ((this.yHeadRot * 256f) / 360f))
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultItem(ItemStack item) {
|
||||
this.defaultItem = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getVelocity() {
|
||||
return velocity.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVelocity(Vector vector) {
|
||||
this.velocity = vector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVelocity(Vector vector) { // This can cause lag? (maybe i fixed it with the new static method)
|
||||
if (MathUtils.isNotFinite(vector)) {
|
||||
velocity = vector;
|
||||
return;
|
||||
}
|
||||
|
||||
velocity.add(vector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAliveTicks() {
|
||||
return aliveTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tickDelay(int i) {
|
||||
return aliveTicks % i == 0;
|
||||
}
|
||||
|
||||
private void sendPacket(Packet<?> packet) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(packet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
loadChunks();
|
||||
|
||||
super.tick();
|
||||
|
||||
if (!isAlive()) return;
|
||||
|
||||
aliveTicks++;
|
||||
|
||||
if (fireTicks > 0) --fireTicks;
|
||||
if (invulnerableTime > 0) --invulnerableTime;
|
||||
if (jumpTicks > 0) --jumpTicks;
|
||||
if (noFallTicks > 0) --noFallTicks;
|
||||
|
||||
if (checkGround()) {
|
||||
if (groundTicks < 5) groundTicks++;
|
||||
} else {
|
||||
groundTicks = 0;
|
||||
}
|
||||
|
||||
updateLocation();
|
||||
|
||||
float health = getHealth();
|
||||
float maxHealth = getMaxHealth();
|
||||
float regenAmount = 0.025f;
|
||||
float amount;
|
||||
|
||||
if (health < maxHealth - regenAmount) {
|
||||
amount = health + regenAmount;
|
||||
} else {
|
||||
amount = maxHealth;
|
||||
}
|
||||
|
||||
setHealth(amount);
|
||||
|
||||
fireDamageCheck();
|
||||
fallDamageCheck();
|
||||
|
||||
if (position().y < -64) {
|
||||
die(DamageSource.OUT_OF_WORLD);
|
||||
}
|
||||
|
||||
oldVelocity = velocity.clone();
|
||||
}
|
||||
|
||||
private void loadChunks() {
|
||||
Level world = getLevel();
|
||||
|
||||
for (int i = chunkPosition().x - 1; i <= chunkPosition().x + 1; i++) {
|
||||
for (int j = chunkPosition().z - 1; j <= chunkPosition().z + 1; j++) {
|
||||
LevelChunk chunk = world.getChunk(i, j);
|
||||
|
||||
if (!chunk.loaded) {
|
||||
chunk.loaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fireDamageCheck() {
|
||||
if (!isAlive()) {
|
||||
return; // maybe also have packet reset thing
|
||||
}
|
||||
|
||||
Material type = getLocation().getBlock().getType();
|
||||
|
||||
if (type == Material.WATER) {
|
||||
setOnFirePackets(false); // maybe also play extinguish noise?
|
||||
fireTicks = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean lava = type == org.bukkit.Material.LAVA;
|
||||
|
||||
if (lava || type == org.bukkit.Material.FIRE || type == Material.SOUL_FIRE) {
|
||||
ignite();
|
||||
}
|
||||
|
||||
if (invulnerableTime == 0) {
|
||||
if (lava) {
|
||||
hurt(DamageSource.LAVA, 4);
|
||||
invulnerableTime = 20;//this used to be 12 ticks but that would cause the bot to take damage too quickly
|
||||
} else if (fireTicks > 1) {
|
||||
hurt(DamageSource.IN_FIRE, 1);
|
||||
invulnerableTime = 20;
|
||||
}
|
||||
}
|
||||
|
||||
if (fireTicks == 1) {
|
||||
setOnFirePackets(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ignite() {
|
||||
if (fireTicks <= 1) setOnFirePackets(true);
|
||||
fireTicks = 100;
|
||||
}
|
||||
|
||||
public void setOnFirePackets(boolean onFire) {
|
||||
//entityData.set(new EntityDataAccessor<>(0, EntityDataSerializers.BYTE), onFire ? (byte) 1 : (byte) 0);
|
||||
//sendPacket(new ClientboundSetEntityDataPacket(getId(), entityData, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnFire() {
|
||||
return fireTicks != 0;
|
||||
}
|
||||
|
||||
private void fallDamageCheck() { // TODO create a better bot event system in the future, also have bot.getAgent()
|
||||
if (groundTicks != 0 && noFallTicks == 0 && !(oldVelocity.getY() >= -0.8) && !BotUtils.NO_FALL.contains(getLocation().getBlock().getType())) {
|
||||
BotFallDamageEvent event = new BotFallDamageEvent(this);
|
||||
|
||||
plugin.getManager().getAgent().onFallDamage(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
hurt(DamageSource.FALL, (float) Math.pow(3.6, -oldVelocity.getY()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFalling() {
|
||||
return velocity.getY() < -0.8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void block(int blockLength, int cooldown) {
|
||||
if (!shield || blockUse) return;
|
||||
startBlocking();
|
||||
scheduler.runTaskLater(plugin, () -> stopBlocking(cooldown), blockLength);
|
||||
}
|
||||
|
||||
private void startBlocking() {
|
||||
this.blocking = true;
|
||||
this.blockUse = true;
|
||||
startUsingItem(InteractionHand.OFF_HAND);
|
||||
sendPacket(new ClientboundSetEntityDataPacket(getId(), entityData, true));
|
||||
}
|
||||
|
||||
private void stopBlocking(int cooldown) {
|
||||
this.blocking = false;
|
||||
stopUsingItem();
|
||||
scheduler.runTaskLater(plugin, () -> this.blockUse = false, cooldown);
|
||||
sendPacket(new ClientboundSetEntityDataPacket(getId(), entityData, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlocking() {
|
||||
return blocking;
|
||||
}
|
||||
|
||||
public void setShield(boolean enabled) {
|
||||
this.shield = enabled;
|
||||
|
||||
System.out.println("set shield");
|
||||
setItemOffhand(new org.bukkit.inventory.ItemStack(enabled ? Material.SHIELD : Material.AIR));
|
||||
}
|
||||
|
||||
private void updateLocation() {
|
||||
double y;
|
||||
|
||||
MathUtils.clean(velocity); // TODO lag????
|
||||
|
||||
if (isInWater()) {
|
||||
y = Math.min(velocity.getY() + 0.1, 0.1);
|
||||
addFriction(0.8);
|
||||
velocity.setY(y);
|
||||
} else {
|
||||
if (groundTicks != 0) {
|
||||
velocity.setY(0);
|
||||
addFriction(0.5);
|
||||
y = 0;
|
||||
} else {
|
||||
y = velocity.getY();
|
||||
velocity.setY(Math.max(y - 0.1, -3.5));
|
||||
}
|
||||
}
|
||||
|
||||
this.move(MoverType.SELF, new Vec3(velocity.getX(), y, velocity.getZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInWater() {
|
||||
Location loc = getLocation();
|
||||
|
||||
for (int i = 0; i <= 2; i++) {
|
||||
Material type = loc.getBlock().getType();
|
||||
|
||||
if (type == Material.WATER || type == Material.LAVA) {
|
||||
return true;
|
||||
}
|
||||
|
||||
loc.add(0, 0.9, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jump(Vector vel) {
|
||||
if (jumpTicks == 0 && groundTicks > 1) {
|
||||
jumpTicks = 4;
|
||||
velocity = vel;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jump() {
|
||||
jump(new Vector(0, 0.5, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void walk(Vector vel) {
|
||||
double max = 0.4;
|
||||
|
||||
Vector sum = velocity.clone().add(vel);
|
||||
if (sum.length() > max) sum.normalize().multiply(max);
|
||||
|
||||
velocity = sum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attack(org.bukkit.entity.Entity entity) {
|
||||
faceLocation(entity.getLocation());
|
||||
punch();
|
||||
|
||||
double damage = ItemUtils.getLegacyAttackDamage(defaultItem);
|
||||
|
||||
if (entity instanceof Damageable) {
|
||||
((Damageable) entity).damage(damage, getBukkitEntity());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void punch() {
|
||||
swing(InteractionHand.MAIN_HAND);
|
||||
}
|
||||
|
||||
public boolean checkGround() {
|
||||
double vy = velocity.getY();
|
||||
|
||||
if (vy > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
World world = getBukkitEntity().getWorld();
|
||||
AABB box = getBoundingBox();
|
||||
|
||||
double[] xVals = new double[]{
|
||||
box.minX,
|
||||
box.maxX
|
||||
};
|
||||
|
||||
double[] zVals = new double[]{
|
||||
box.minZ,
|
||||
box.maxZ
|
||||
};
|
||||
|
||||
for (double x : xVals) {
|
||||
for (double z : zVals) {
|
||||
Location loc = new Location(world, x, position().y - 0.01, z);
|
||||
Block block = world.getBlockAt(loc);
|
||||
|
||||
if (block.getType().isSolid() && BotUtils.solidAt(loc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnGround() {
|
||||
return groundTicks != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFriction(double factor) {
|
||||
double frictionMin = 0.01;
|
||||
|
||||
double x = velocity.getX();
|
||||
double z = velocity.getZ();
|
||||
|
||||
velocity.setX(Math.abs(x) < frictionMin ? 0 : x * factor);
|
||||
velocity.setZ(Math.abs(z) < frictionMin ? 0 : z * factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVisually() {
|
||||
this.removeTab();
|
||||
this.setDead();
|
||||
}
|
||||
|
||||
private void removeTab() {
|
||||
sendPacket(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER, this));
|
||||
}
|
||||
|
||||
public void setRemoveOnDeath(boolean enabled) {
|
||||
this.removeOnDeath = enabled;
|
||||
}
|
||||
|
||||
private void setDead() {
|
||||
sendPacket(new ClientboundRemoveEntitiesPacket(getId()));
|
||||
|
||||
this.dead = true;
|
||||
this.inventoryMenu.removed(this);
|
||||
if (this.containerMenu != null) {
|
||||
this.containerMenu.removed(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void dieCheck() {
|
||||
if (removeOnDeath) {
|
||||
|
||||
// I replaced HashSet with ConcurrentHashMap.newKeySet which creates a "ConcurrentHashSet"
|
||||
// this should fix the concurrentmodificationexception mentioned above, I used the ConcurrentHashMap.newKeySet to make a "ConcurrentHashSet"
|
||||
plugin.getManager().remove(this);
|
||||
|
||||
scheduler.runTaskLater(plugin, this::setDead, 30);
|
||||
|
||||
this.removeTab();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damageSource) {
|
||||
super.die(damageSource);
|
||||
this.dieCheck();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(Entity entity) {
|
||||
if (!this.isPassengerOfSameVehicle(entity) && !entity.noPhysics && !this.noPhysics) {
|
||||
double d0 = entity.getX() - this.getZ();
|
||||
double d1 = entity.getX() - this.getZ();
|
||||
double d2 = Mth.absMax(d0, d1);
|
||||
if (d2 >= 0.009999999776482582D) {
|
||||
d2 = Math.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;
|
||||
|
||||
if (!this.isVehicle()) {
|
||||
velocity.add(new Vector(-d0, 0.0D, -d1));
|
||||
}
|
||||
|
||||
if (!entity.isVehicle()) {
|
||||
entity.push(d0, 0.0D, d1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource damagesource, float f) {
|
||||
Entity attacker = damagesource.getEntity();
|
||||
|
||||
float damage;
|
||||
|
||||
boolean playerInstance = attacker instanceof ServerPlayer;
|
||||
|
||||
Player killer;
|
||||
|
||||
if (playerInstance) {
|
||||
killer = ((ServerPlayer) attacker).getBukkitEntity();
|
||||
|
||||
BotDamageByPlayerEvent event = new BotDamageByPlayerEvent(this, killer, f);
|
||||
|
||||
agent.onPlayerDamage(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = event.getDamage();
|
||||
} else {
|
||||
killer = null;
|
||||
damage = f;
|
||||
}
|
||||
|
||||
boolean damaged = super.hurt(damagesource, damage);
|
||||
|
||||
if (!damaged && blocking) {
|
||||
getBukkitEntity().getWorld().playSound(getLocation(), Sound.ITEM_SHIELD_BLOCK, 1, 1);
|
||||
}
|
||||
|
||||
if (damaged && attacker != null) {
|
||||
if (playerInstance && !isAlive()) {
|
||||
agent.onBotKilledByPlayer(new BotKilledByPlayerEvent(this, killer));
|
||||
}
|
||||
|
||||
kb(getLocation(), attacker.getBukkitEntity().getLocation());
|
||||
}
|
||||
|
||||
return damaged;
|
||||
}
|
||||
|
||||
private void kb(Location loc1, Location loc2) {
|
||||
Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3);
|
||||
|
||||
if (isOnGround()) vel.multiply(0.8).setY(0.4);
|
||||
|
||||
velocity = vel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getKills() {
|
||||
return kills;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementKills() {
|
||||
kills++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
public void faceLocation(Location loc) {
|
||||
look(loc.toVector().subtract(getLocation().toVector()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void look(BlockFace face) {
|
||||
look(face.getDirection(), face == BlockFace.DOWN || face == BlockFace.UP);
|
||||
}
|
||||
|
||||
private void look(Vector dir, boolean keepYaw) {
|
||||
float yaw, pitch;
|
||||
|
||||
if (keepYaw) {
|
||||
yaw = this.getYRot();
|
||||
pitch = MathUtils.fetchPitch(dir);
|
||||
} else {
|
||||
float[] vals = MathUtils.fetchYawPitch(dir);
|
||||
yaw = vals[0];
|
||||
pitch = vals[1];
|
||||
|
||||
sendPacket(new ClientboundRotateHeadPacket(getBukkitEntity().getHandle(), (byte) (yaw * 256 / 360f)));
|
||||
}
|
||||
|
||||
setRot(yaw, pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attemptBlockPlace(Location loc, Material type, boolean down) {
|
||||
if (down) {
|
||||
look(BlockFace.DOWN);
|
||||
} else {
|
||||
faceLocation(loc);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(org.bukkit.inventory.ItemStack item) {
|
||||
setItem(item, EquipmentSlot.MAINHAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemOffhand(org.bukkit.inventory.ItemStack item) {
|
||||
setItem(item, EquipmentSlot.OFFHAND);
|
||||
System.out.println("set offhand");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(ItemStack item, org.bukkit.inventory.EquipmentSlot slot) {
|
||||
EquipmentSlot nmsSlot = CraftEquipmentSlot.getNMS(slot);
|
||||
setItem(item, nmsSlot);
|
||||
}
|
||||
|
||||
public void setItem(org.bukkit.inventory.ItemStack item, EquipmentSlot slot) {
|
||||
if (item == null) item = defaultItem;
|
||||
|
||||
System.out.println("set");
|
||||
if (slot == EquipmentSlot.MAINHAND) {
|
||||
getBukkitEntity().getInventory().setItemInMainHand(item);
|
||||
} else if (slot == EquipmentSlot.OFFHAND) {
|
||||
getBukkitEntity().getInventory().setItemInOffHand(item);
|
||||
}
|
||||
|
||||
System.out.println("slot = " + slot);
|
||||
System.out.println("item = " + item);
|
||||
sendPacket(new ClientboundSetEquipmentPacket(getId(), new ArrayList<>(Collections.singletonList(
|
||||
new Pair<>(slot, CraftItemStack.asNMSCopy(item))
|
||||
))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swim() {
|
||||
getBukkitEntity().setSwimming(true);
|
||||
registerPose(Pose.SWIMMING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sneak() {
|
||||
getBukkitEntity().setSneaking(true);
|
||||
registerPose(Pose.CROUCHING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stand() {
|
||||
Player player = getBukkitEntity();
|
||||
player.setSneaking(false);
|
||||
player.setSwimming(false);
|
||||
|
||||
registerPose(Pose.STANDING);
|
||||
}
|
||||
|
||||
private void registerPose(Pose pose) {
|
||||
//entityData.set(new EntityDataAccessor<>(6, EntityDataSerializers.POSE), pose);
|
||||
//sendPacket(new ClientboundSetEntityDataPacket(getId(), entityData, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
if (this.hurtTime > 0) {
|
||||
this.hurtTime -= 1;
|
||||
}
|
||||
|
||||
baseTick();
|
||||
tickEffects();
|
||||
|
||||
this.animStepO = (int) this.animStep;
|
||||
this.yBodyRotO = this.yBodyRot;
|
||||
this.yHeadRotO = this.yHeadRot;
|
||||
this.yRotO = this.getYRot();
|
||||
this.xRotO = this.getXRot();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package net.nuggetmc.tplus.bot;
|
||||
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.nuggetmc.tplus.bot.agent.Agent;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.tplus.bot.event.BotDeathEvent;
|
||||
import net.nuggetmc.tplus.utils.MojangAPI;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BotManager implements Listener {
|
||||
|
||||
private final Agent agent;
|
||||
private final Set<Bot> bots;
|
||||
private final NumberFormat numberFormat;
|
||||
|
||||
public boolean joinMessages = false;
|
||||
|
||||
public BotManager() {
|
||||
this.agent = new LegacyAgent(this);
|
||||
this.bots = ConcurrentHashMap.newKeySet(); //should fix concurrentmodificationexception
|
||||
this.numberFormat = NumberFormat.getInstance(Locale.US);
|
||||
}
|
||||
|
||||
public Set<Bot> fetch() {
|
||||
return bots;
|
||||
}
|
||||
|
||||
public void add(Bot bot) {
|
||||
if (joinMessages) {
|
||||
Bukkit.broadcastMessage(ChatColor.YELLOW + (bot.getName() + " joined the game"));
|
||||
}
|
||||
|
||||
bots.add(bot);
|
||||
}
|
||||
|
||||
public Bot getFirst(String name) {
|
||||
for (Bot bot : bots) {
|
||||
if (name.equals(bot.getName())) {
|
||||
return bot;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> fetchNames() {
|
||||
return bots.stream().map(Bot::getName).map(component -> component.getString()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Agent getAgent() {
|
||||
return agent;
|
||||
}
|
||||
|
||||
public void createBots(Player sender, String name, String skinName, int n) {
|
||||
createBots(sender, name, skinName, n, null);
|
||||
}
|
||||
|
||||
public void createBots(Player sender, String name, String skinName, int n, NeuralNetwork network) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
if (n < 1) n = 1;
|
||||
|
||||
sender.sendMessage("Creating " + (n == 1 ? "new bot" : ChatColor.RED + numberFormat.format(n) + ChatColor.RESET + " new bots")
|
||||
+ " with name " + ChatColor.GREEN + name.replace("%", ChatColor.LIGHT_PURPLE + "%" + ChatColor.RESET)
|
||||
+ (skinName == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skinName)
|
||||
+ ChatColor.RESET + "...");
|
||||
|
||||
skinName = skinName == null ? name : skinName;
|
||||
|
||||
createBots(sender.getLocation(), name, MojangAPI.getSkin(skinName), n, network);
|
||||
|
||||
sender.sendMessage("Process completed (" + ChatColor.RED + ((System.currentTimeMillis() - timestamp) / 1000D) + "s" + ChatColor.RESET + ").");
|
||||
}
|
||||
|
||||
public Set<Bot> createBots(Location loc, String name, String[] skin, int n, NeuralNetwork network) {
|
||||
List<NeuralNetwork> networks = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
networks.add(network);
|
||||
}
|
||||
|
||||
return createBots(loc, name, skin, networks);
|
||||
}
|
||||
|
||||
public Set<Bot> createBots(Location loc, String name, String[] skin, List<NeuralNetwork> networks) {
|
||||
Set<Bot> bots = new HashSet<>();
|
||||
World world = loc.getWorld();
|
||||
|
||||
int n = networks.size();
|
||||
int i = 1;
|
||||
|
||||
double f = n < 100 ? .004 * n : .4;
|
||||
|
||||
for (NeuralNetwork network : networks) {
|
||||
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin);
|
||||
|
||||
if (network != null) {
|
||||
bot.setNeuralNetwork(network == NeuralNetwork.RANDOM ? NeuralNetwork.generateRandomNetwork() : network);
|
||||
bot.setShield(true);
|
||||
bot.setDefaultItem(new ItemStack(Material.WOODEN_AXE));
|
||||
//bot.setRemoveOnDeath(false);
|
||||
}
|
||||
|
||||
if (network != null) {
|
||||
bot.setVelocity(randomVelocity());
|
||||
} else if (i > 1) {
|
||||
bot.setVelocity(randomVelocity().multiply(f));
|
||||
}
|
||||
|
||||
bots.add(bot);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (world != null) {
|
||||
world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
}
|
||||
|
||||
return bots;
|
||||
}
|
||||
|
||||
private Vector randomVelocity() {
|
||||
return new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize();
|
||||
}
|
||||
|
||||
public void remove(Bot bot) {
|
||||
bots.remove(bot);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (!bots.isEmpty()) {
|
||||
bots.forEach(Bot::removeVisually);
|
||||
bots.clear(); // Not always necessary, but a good security measure
|
||||
}
|
||||
|
||||
agent.stopAllTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bot from a Player object
|
||||
* @param player
|
||||
* @deprecated Use {@link #getBot(UUID)} instead as this may no longer work
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public Bot getBot(Player player) { // potentially memory intensive
|
||||
int id = player.getEntityId();
|
||||
return getBot(id);
|
||||
}
|
||||
|
||||
public Bot getBot(UUID uuid) {
|
||||
Entity entity = Bukkit.getEntity(uuid);
|
||||
if (entity == null) return null;
|
||||
return getBot(entity.getEntityId());
|
||||
}
|
||||
|
||||
public Bot getBot(int entityId) {
|
||||
for (Bot bot : bots) {
|
||||
if (bot.getId() == entityId) {
|
||||
return bot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
ServerGamePacketListenerImpl connection = ((CraftPlayer) event.getPlayer()).getHandle().connection;
|
||||
bots.forEach(bot -> bot.render(connection, true));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(EntityDeathEvent event) {
|
||||
LivingEntity bukkitEntity = event.getEntity();
|
||||
Bot bot = getBot(bukkitEntity.getEntityId());
|
||||
if (bot != null) {
|
||||
agent.onBotDeath(new BotDeathEvent(event, bot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.nuggetmc.tplus.bot;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.nuggetmc.tplus.utils.MojangAPI;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class CustomGameProfile extends GameProfile {
|
||||
|
||||
public CustomGameProfile(UUID uuid, String name, String[] skin) {
|
||||
super(uuid, name);
|
||||
|
||||
setSkin(skin);
|
||||
}
|
||||
|
||||
public CustomGameProfile(UUID uuid, String name, String skinName) {
|
||||
super(uuid, name);
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.nuggetmc.tplus.bot.agent;
|
||||
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.bot.event.BotDamageByPlayerEvent;
|
||||
import net.nuggetmc.tplus.bot.event.BotDeathEvent;
|
||||
import net.nuggetmc.tplus.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.tplus.bot.event.BotKilledByPlayerEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class Agent {
|
||||
|
||||
protected final TerminatorPlus plugin;
|
||||
protected final BotManager manager;
|
||||
protected final BukkitScheduler scheduler;
|
||||
protected final Set<BukkitRunnable> taskList;
|
||||
protected final Random random;
|
||||
|
||||
protected boolean enabled;
|
||||
protected int taskID;
|
||||
|
||||
protected boolean drops;
|
||||
|
||||
public Agent(BotManager manager) {
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = manager;
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.taskList = new HashSet<>();
|
||||
this.random = new Random();
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean b) {
|
||||
enabled = b;
|
||||
|
||||
if (b) {
|
||||
taskID = scheduler.scheduleSyncRepeatingTask(plugin, this::tick, 0, 1);
|
||||
} else {
|
||||
scheduler.cancelTask(taskID);
|
||||
stopAllTasks();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopAllTasks() {
|
||||
if (!taskList.isEmpty()) {
|
||||
taskList.stream().filter(t -> !t.isCancelled()).forEach(BukkitRunnable::cancel);
|
||||
taskList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDrops(boolean enabled) {
|
||||
this.drops = enabled;
|
||||
}
|
||||
|
||||
protected abstract void tick();
|
||||
|
||||
public void onFallDamage(BotFallDamageEvent event) { }
|
||||
|
||||
public void onPlayerDamage(BotDamageByPlayerEvent event) { }
|
||||
|
||||
public void onBotDeath(BotDeathEvent event) { }
|
||||
|
||||
public void onBotKilledByPlayer(BotKilledByPlayerEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
Bot bot = manager.getBot(player);
|
||||
|
||||
if (bot != null) {
|
||||
bot.incrementKills();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package net.nuggetmc.tplus.bot.agent.botagent;
|
||||
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.bot.agent.Agent;
|
||||
import net.nuggetmc.tplus.utils.MathUtils;
|
||||
import net.nuggetmc.tplus.utils.PlayerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/*
|
||||
* New bot agent!!!!!
|
||||
* this will replace legacyagent eventually
|
||||
* - basically this one will actually have A* pathfinding, whereas the legacy one only straightlines
|
||||
*/
|
||||
|
||||
public class BotAgent extends Agent {
|
||||
|
||||
private int count;
|
||||
|
||||
public BotAgent(BotManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick() {
|
||||
Set<Bot> bots = manager.fetch();
|
||||
count = bots.size();
|
||||
bots.forEach(this::tickBot);
|
||||
}
|
||||
|
||||
// This is where the code starts to get spicy
|
||||
private void tickBot(Bot bot) {
|
||||
if (!bot.isAlive()) return;
|
||||
|
||||
Location loc = bot.getLocation();
|
||||
|
||||
// if bot.hasHoldState() return; << This will be to check if a bot is mining or something similar where it can't move
|
||||
|
||||
Player player = nearestPlayer(loc);
|
||||
if (player == null) return;
|
||||
|
||||
Location target = player.getLocation();
|
||||
|
||||
if (count > 1) target.add(bot.getOffset());
|
||||
|
||||
// Make the XZ offsets stored in the bot object (so they don't form a straight line),
|
||||
// and make it so when mining and stuff, the offset is not taken into account
|
||||
|
||||
// if checkVertical(bot) { break block action add; return; }
|
||||
|
||||
BotSituation situation = new BotSituation(bot, target);
|
||||
|
||||
// based on the situation, the bot can perform different actions
|
||||
// there can be priorities assigned
|
||||
|
||||
// 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 (PlayerUtils.isInvincible(player.getGameMode()) || player.getNoDamageTicks() >= 5 || loc.distance(player.getLocation()) >= 4) return;
|
||||
|
||||
bot.attack(player);
|
||||
}
|
||||
|
||||
private void move(Bot bot, Player player, Location loc, Location target) {
|
||||
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.add(bot.getVelocity());
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (MathUtils.isNotFinite(vel)) {
|
||||
MathUtils.clean(vel);
|
||||
}
|
||||
}
|
||||
|
||||
if (vel.length() > 1) vel.normalize();
|
||||
|
||||
if (loc.distance(target) <= 5) {
|
||||
vel.multiply(0.3);
|
||||
} else {
|
||||
vel.multiply(0.4);
|
||||
}
|
||||
|
||||
vel.setY(0.4);
|
||||
|
||||
bot.jump(vel);
|
||||
}
|
||||
|
||||
private Player nearestPlayer(Location loc) {
|
||||
Player result = null;
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (PlayerUtils.isInvincible(player.getGameMode()) || loc.getWorld() != player.getWorld()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.nuggetmc.tplus.bot.agent.botagent;
|
||||
|
||||
import net.nuggetmc.tplus.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.nuggetmc.tplus.bot.agent.botagent;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum EnumTargetGoal {
|
||||
NEAREST_VULNERABLE_PLAYER("Locate the nearest real player that is in either Survival or Adventure mode."),
|
||||
NEAREST_PLAYER("Locate the nearest real online player, despite the gamemode."),
|
||||
NEAREST_HOSTILE("Locate the nearest hostile entity."),
|
||||
NEAREST_MOB("Locate the nearest mob."),
|
||||
NEAREST_BOT("Locate the nearest bot."),
|
||||
NEAREST_BOT_DIFFER("Locate the nearest bot with a different username."),
|
||||
NEAREST_BOT_DIFFER_ALPHA("Locate the nearest bot with a different username after filtering out non-alpha characters."),
|
||||
NONE("No target goal.");
|
||||
|
||||
private static final Map<String, EnumTargetGoal> VALUES = new HashMap<String, EnumTargetGoal>() {
|
||||
{
|
||||
this.put("none", NONE);
|
||||
this.put("nearestvulnerableplayer", NEAREST_VULNERABLE_PLAYER);
|
||||
this.put("nearestplayer", NEAREST_PLAYER);
|
||||
this.put("nearesthostile", NEAREST_HOSTILE);
|
||||
this.put("nearestmob", NEAREST_MOB);
|
||||
this.put("nearestbot", NEAREST_BOT);
|
||||
this.put("nearestbotdiffer", NEAREST_BOT_DIFFER);
|
||||
this.put("nearestbotdifferalpha", NEAREST_BOT_DIFFER_ALPHA);
|
||||
}
|
||||
};
|
||||
|
||||
private final String description;
|
||||
|
||||
EnumTargetGoal(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static EnumTargetGoal from(String name) {
|
||||
return VALUES.get(name);
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,190 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class LegacyBlockCheck {
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final LegacyAgent agent;
|
||||
|
||||
public LegacyBlockCheck(LegacyAgent agent) {
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
private void placeFinal(Bot bot, Player player, Location loc) {
|
||||
if (loc.getBlock().getType() != Material.COBBLESTONE) {
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
bot.setItem(new ItemStack(Material.COBBLESTONE));
|
||||
loc.getBlock().setType(Material.COBBLESTONE);
|
||||
|
||||
Block under = loc.clone().add(0, -1, 0).getBlock();
|
||||
if (under.getType() == Material.LAVA) {
|
||||
under.setType(Material.COBBLESTONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void placeBlock(Bot bot, Player player, Block block) {
|
||||
|
||||
Location loc = block.getLocation();
|
||||
|
||||
Block under = loc.clone().add(0, -1, 0).getBlock();
|
||||
|
||||
if (LegacyMats.SPAWN.contains(under.getType())) {
|
||||
placeFinal(bot, player, loc.clone().add(0, -1, 0));
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
}, 2);
|
||||
}
|
||||
|
||||
Set<Block> face = new HashSet<>(Arrays.asList(loc.clone().add(1, 0, 0).getBlock(),
|
||||
loc.clone().add(-1, 0, 0).getBlock(),
|
||||
loc.clone().add(0, 0, 1).getBlock(),
|
||||
loc.clone().add(0, 0, -1).getBlock()));
|
||||
|
||||
boolean a = false;
|
||||
for (Block side : face) {
|
||||
if (!LegacyMats.SPAWN.contains(side.getType())) {
|
||||
a = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (a) {
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Block> edge = new HashSet<>(Arrays.asList(loc.clone().add(1, -1, 0).getBlock(),
|
||||
loc.clone().add(-1, -1, 0).getBlock(),
|
||||
loc.clone().add(0, -1, 1).getBlock(),
|
||||
loc.clone().add(0, -1, -1).getBlock()));
|
||||
|
||||
boolean b = false;
|
||||
for (Block side : edge) {
|
||||
if (!LegacyMats.SPAWN.contains(side.getType())) {
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (b && LegacyMats.SPAWN.contains(under.getType())) {
|
||||
placeFinal(bot, player, loc.clone().add(0, -1, 0));
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
}, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
Block c1 = loc.clone().add(1, -1, 1).getBlock();
|
||||
Block c2 = loc.clone().add(1, -1, -1).getBlock();
|
||||
Block c3 = loc.clone().add(-1, -1, 1).getBlock();
|
||||
Block c4 = loc.clone().add(-1, -1, -1).getBlock();
|
||||
|
||||
boolean t = false;
|
||||
|
||||
if (!LegacyMats.SPAWN.contains(c1.getType()) || !LegacyMats.SPAWN.contains(c2.getType())) {
|
||||
|
||||
Block b1 = loc.clone().add(1, -1, 0).getBlock();
|
||||
if (LegacyMats.SPAWN.contains(b1.getType())) {
|
||||
placeFinal(bot, player, b1.getLocation());
|
||||
}
|
||||
|
||||
t = true;
|
||||
|
||||
} else if (!LegacyMats.SPAWN.contains(c3.getType()) || !LegacyMats.SPAWN.contains(c4.getType())) {
|
||||
|
||||
Block b1 = loc.clone().add(-1, -1, 0).getBlock();
|
||||
if (LegacyMats.SPAWN.contains(b1.getType())) {
|
||||
placeFinal(bot, player, b1.getLocation());
|
||||
}
|
||||
|
||||
t = true;
|
||||
}
|
||||
|
||||
if (t) {
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
Block b2 = loc.clone().add(0, -1, 0).getBlock();
|
||||
if (LegacyMats.SPAWN.contains(b2.getType())) {
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
placeFinal(bot, player, b2.getLocation());
|
||||
}
|
||||
}, 1);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
}, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
}
|
||||
|
||||
public void clutch(Bot bot, LivingEntity target) {
|
||||
Location botLoc = bot.getLocation();
|
||||
|
||||
Material type = botLoc.clone().add(0, -1, 0).getBlock().getType();
|
||||
Material type2 = botLoc.clone().add(0, -2, 0).getBlock().getType();
|
||||
|
||||
if (!(LegacyMats.SPAWN.contains(type) && LegacyMats.SPAWN.contains(type2))) return;
|
||||
|
||||
if (target.getLocation().getBlockY() >= botLoc.getBlockY()) {
|
||||
Location loc = botLoc.clone().add(0, -1, 0);
|
||||
|
||||
Set<Block> face = new HashSet<>(Arrays.asList(
|
||||
loc.clone().add(1, 0, 0).getBlock(),
|
||||
loc.clone().add(-1, 0, 0).getBlock(),
|
||||
loc.clone().add(0, 0, 1).getBlock(),
|
||||
loc.clone().add(0, 0, -1).getBlock()
|
||||
));
|
||||
|
||||
Location at = null;
|
||||
for (Block side : face) {
|
||||
if (!LegacyMats.SPAWN.contains(side.getType())) {
|
||||
at = side.getLocation();
|
||||
}
|
||||
}
|
||||
|
||||
if (at != null) {
|
||||
agent.slow.add(bot);
|
||||
agent.noFace.add(bot);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
bot.stand();
|
||||
agent.slow.remove(bot);
|
||||
}, 12);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
agent.noFace.remove(bot);
|
||||
}, 15);
|
||||
|
||||
Location faceLoc = at.clone().add(0, -1.5, 0);
|
||||
|
||||
bot.faceLocation(faceLoc);
|
||||
bot.look(BlockFace.DOWN);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
bot.faceLocation(faceLoc);
|
||||
}, 1);
|
||||
|
||||
bot.punch();
|
||||
bot.sneak();
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
bot.setItem(new ItemStack(Material.COBBLESTONE));
|
||||
loc.getBlock().setType(Material.COBBLESTONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class LegacyItems {
|
||||
public static final Material SHOVEL = Material.IRON_SHOVEL;
|
||||
public static final Material AXE = Material.IRON_AXE;
|
||||
public static final Material PICKAXE = Material.IRON_PICKAXE;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum LegacyLevel {
|
||||
ABOVE,
|
||||
BELOW,
|
||||
AT,
|
||||
AT_D,
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
WEST,
|
||||
NORTH_D,
|
||||
SOUTH_D,
|
||||
EAST_D,
|
||||
WEST_D;
|
||||
|
||||
private static final Set<LegacyLevel> SIDE = new HashSet<>(Arrays.asList(
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
WEST,
|
||||
NORTH_D,
|
||||
SOUTH_D,
|
||||
EAST_D,
|
||||
WEST_D
|
||||
));
|
||||
|
||||
public boolean isSide() {
|
||||
return SIDE.contains(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class LegacyMats {
|
||||
|
||||
public static final Set<Material> AIR = new HashSet<>(Arrays.asList(
|
||||
Material.WATER,
|
||||
Material.OAK_TRAPDOOR,
|
||||
Material.FIRE,
|
||||
Material.LAVA,
|
||||
Material.SNOW,
|
||||
Material.CAVE_AIR,
|
||||
Material.VINE,
|
||||
Material.FERN,
|
||||
Material.LARGE_FERN,
|
||||
Material.GRASS,
|
||||
Material.TALL_GRASS,
|
||||
Material.SEAGRASS,
|
||||
Material.TALL_SEAGRASS,
|
||||
Material.KELP,
|
||||
Material.KELP_PLANT,
|
||||
Material.SUNFLOWER,
|
||||
Material.AIR,
|
||||
Material.VOID_AIR,
|
||||
Material.FIRE,
|
||||
Material.SOUL_FIRE
|
||||
));
|
||||
|
||||
public static final Set<Material> NO_CRACK = new HashSet<>(Arrays.asList(
|
||||
Material.WATER,
|
||||
Material.FIRE,
|
||||
Material.LAVA,
|
||||
Material.CAVE_AIR,
|
||||
Material.SOUL_FIRE
|
||||
));
|
||||
|
||||
public static final Set<Material> SHOVEL = new HashSet<>(Arrays.asList(
|
||||
Material.DIRT,
|
||||
Material.GRAVEL,
|
||||
Material.SAND,
|
||||
Material.SNOW
|
||||
));
|
||||
|
||||
public static final Set<Material> AXE = new HashSet<>(Arrays.asList(
|
||||
Material.OAK_PLANKS, Material.OAK_DOOR, Material.OAK_FENCE, Material.OAK_FENCE_GATE, Material.OAK_LOG, Material.OAK_PLANKS,
|
||||
Material.OAK_SIGN, Material.OAK_SLAB, Material.OAK_STAIRS, Material.OAK_TRAPDOOR, Material.OAK_WALL_SIGN, Material.OAK_WOOD,
|
||||
Material.DARK_OAK_PLANKS, Material.DARK_OAK_DOOR, Material.DARK_OAK_FENCE, Material.DARK_OAK_FENCE_GATE, Material.DARK_OAK_LOG, Material.DARK_OAK_PLANKS,
|
||||
Material.DARK_OAK_SIGN, Material.DARK_OAK_SLAB, Material.DARK_OAK_STAIRS, Material.DARK_OAK_TRAPDOOR, Material.DARK_OAK_WALL_SIGN, Material.DARK_OAK_WOOD,
|
||||
Material.ACACIA_PLANKS, Material.ACACIA_DOOR, Material.ACACIA_FENCE, Material.ACACIA_FENCE_GATE, Material.ACACIA_LOG, Material.ACACIA_PLANKS,
|
||||
Material.ACACIA_SIGN, Material.ACACIA_SLAB, Material.ACACIA_STAIRS, Material.ACACIA_TRAPDOOR, Material.ACACIA_WALL_SIGN, Material.ACACIA_WOOD,
|
||||
Material.BIRCH_PLANKS, Material.BIRCH_DOOR, Material.BIRCH_FENCE, Material.BIRCH_FENCE_GATE, Material.BIRCH_LOG, Material.BIRCH_PLANKS,
|
||||
Material.BIRCH_SIGN, Material.BIRCH_SLAB, Material.BIRCH_STAIRS, Material.BIRCH_TRAPDOOR, Material.BIRCH_WALL_SIGN, Material.BIRCH_WOOD,
|
||||
Material.JUNGLE_PLANKS, Material.JUNGLE_DOOR, Material.JUNGLE_FENCE, Material.JUNGLE_FENCE_GATE, Material.JUNGLE_LOG, Material.JUNGLE_PLANKS,
|
||||
Material.JUNGLE_SIGN, Material.JUNGLE_SLAB, Material.JUNGLE_STAIRS, Material.JUNGLE_TRAPDOOR, Material.JUNGLE_WALL_SIGN, Material.JUNGLE_WOOD,
|
||||
Material.SPRUCE_PLANKS, Material.SPRUCE_DOOR, Material.SPRUCE_FENCE, Material.SPRUCE_FENCE_GATE, Material.SPRUCE_LOG, Material.SPRUCE_PLANKS,
|
||||
Material.SPRUCE_SIGN, Material.SPRUCE_SLAB, Material.SPRUCE_STAIRS, Material.SPRUCE_TRAPDOOR, Material.SPRUCE_WALL_SIGN, Material.SPRUCE_WOOD,
|
||||
Material.CRIMSON_PLANKS, Material.CRIMSON_DOOR, Material.CRIMSON_FENCE, Material.CRIMSON_FENCE_GATE, Material.CRIMSON_PLANKS,
|
||||
Material.CRIMSON_SIGN, Material.CRIMSON_SLAB, Material.CRIMSON_STAIRS, Material.CRIMSON_TRAPDOOR, Material.CRIMSON_WALL_SIGN,
|
||||
Material.WARPED_PLANKS, Material.WARPED_DOOR, Material.WARPED_FENCE, Material.WARPED_FENCE_GATE, Material.WARPED_PLANKS,
|
||||
Material.WARPED_SIGN, Material.WARPED_SLAB, Material.WARPED_STAIRS, Material.WARPED_TRAPDOOR, Material.WARPED_WALL_SIGN
|
||||
));
|
||||
|
||||
public static final Set<Material> BREAK = new HashSet<>(Arrays.asList(
|
||||
Material.AIR,
|
||||
Material.WATER,
|
||||
Material.LAVA,
|
||||
Material.TALL_GRASS,
|
||||
Material.SNOW,
|
||||
Material.DIRT_PATH,
|
||||
Material.CAVE_AIR,
|
||||
Material.VINE,
|
||||
Material.FERN,
|
||||
Material.LARGE_FERN,
|
||||
Material.SUGAR_CANE,
|
||||
Material.TWISTING_VINES,
|
||||
Material.WEEPING_VINES,
|
||||
Material.SEAGRASS,
|
||||
Material.TALL_SEAGRASS,
|
||||
Material.KELP,
|
||||
Material.KELP_PLANT,
|
||||
Material.SUNFLOWER,
|
||||
Material.FIRE,
|
||||
Material.SOUL_FIRE
|
||||
));
|
||||
|
||||
public static final Set<Material> WATER = new HashSet<>(Arrays.asList(
|
||||
Material.WATER,
|
||||
Material.SEAGRASS,
|
||||
Material.TALL_SEAGRASS,
|
||||
Material.KELP,
|
||||
Material.KELP_PLANT
|
||||
));
|
||||
|
||||
public static final Set<Material> SPAWN = new HashSet<>(Arrays.asList(
|
||||
Material.AIR,
|
||||
Material.TALL_GRASS,
|
||||
Material.SNOW,
|
||||
Material.CAVE_AIR,
|
||||
Material.VINE,
|
||||
Material.FERN,
|
||||
Material.LARGE_FERN,
|
||||
Material.SUGAR_CANE,
|
||||
Material.TWISTING_VINES,
|
||||
Material.WEEPING_VINES,
|
||||
Material.SEAGRASS,
|
||||
Material.TALL_SEAGRASS,
|
||||
Material.KELP,
|
||||
Material.KELP_PLANT,
|
||||
Material.SUNFLOWER,
|
||||
Material.FIRE,
|
||||
Material.SOUL_FIRE
|
||||
));
|
||||
|
||||
public static final Set<Material> FALL = new HashSet<>(Arrays.asList(
|
||||
Material.AIR,
|
||||
Material.TALL_GRASS,
|
||||
Material.SNOW,
|
||||
Material.CAVE_AIR,
|
||||
Material.VINE,
|
||||
Material.FERN,
|
||||
Material.LARGE_FERN,
|
||||
Material.SUGAR_CANE,
|
||||
Material.TWISTING_VINES,
|
||||
Material.WEEPING_VINES,
|
||||
Material.SEAGRASS,
|
||||
Material.TALL_SEAGRASS,
|
||||
Material.KELP,
|
||||
Material.KELP_PLANT,
|
||||
Material.SUNFLOWER,
|
||||
Material.WATER
|
||||
));
|
||||
|
||||
public static final Set<Material> FENCE = new HashSet<>(Arrays.asList(
|
||||
Material.OAK_FENCE,
|
||||
Material.ACACIA_FENCE,
|
||||
Material.BIRCH_FENCE,
|
||||
Material.CRIMSON_FENCE,
|
||||
Material.DARK_OAK_FENCE,
|
||||
Material.JUNGLE_FENCE,
|
||||
Material.NETHER_BRICK_FENCE,
|
||||
Material.SPRUCE_FENCE,
|
||||
Material.WARPED_FENCE,
|
||||
Material.COBBLESTONE_WALL,
|
||||
Material.ANDESITE_WALL,
|
||||
Material.BLACKSTONE_WALL,
|
||||
Material.BRICK_WALL,
|
||||
Material.GRANITE_WALL,
|
||||
Material.DIORITE_WALL,
|
||||
Material.SANDSTONE_WALL,
|
||||
Material.RED_SANDSTONE_WALL,
|
||||
Material.RED_NETHER_BRICK_WALL,
|
||||
Material.IRON_BARS,
|
||||
Material.COBWEB
|
||||
));
|
||||
|
||||
public static final Set<Material> LEAVES = new HashSet<>(Arrays.asList(
|
||||
Material.BIRCH_LEAVES,
|
||||
Material.DARK_OAK_LEAVES,
|
||||
Material.JUNGLE_LEAVES,
|
||||
Material.OAK_LEAVES,
|
||||
Material.SPRUCE_LEAVES
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class LegacyUtils {
|
||||
|
||||
public static boolean checkFreeSpace(Location a, Location b) {
|
||||
Vector v = b.toVector().subtract(a.toVector());
|
||||
|
||||
int n = 32;
|
||||
double m = 1 / (double) n;
|
||||
|
||||
double j = Math.floor(v.length() * n);
|
||||
v.multiply(m / v.length());
|
||||
|
||||
org.bukkit.World world = a.getWorld();
|
||||
if (world == null) return false;
|
||||
|
||||
for (int i = 0; i <= j; i++) {
|
||||
Block block = world.getBlockAt((a.toVector().add(v.clone().multiply(i))).toLocation(world));
|
||||
|
||||
if (!LegacyMats.AIR.contains(block.getType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Sound breakBlockSound(Block block) {
|
||||
Level nmsWorld = ((CraftWorld) block.getWorld()).getHandle();
|
||||
BlockState blockState = nmsWorld.getBlockState(new BlockPos(block.getX(), block.getY(), block.getZ()));
|
||||
net.minecraft.world.level.block.Block nmsBlock = blockState.getBlock();
|
||||
|
||||
SoundType soundEffectType = nmsBlock.getSoundType(blockState);
|
||||
|
||||
return Sound.valueOf( soundEffectType.getBreakSound().getLocation().getPath().replace(".", "_").toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class LegacyWorldManager {
|
||||
|
||||
/*
|
||||
* This is where the respawning queue will be managed
|
||||
*/
|
||||
|
||||
public static boolean aboveGround(Location loc) {
|
||||
int y = 1;
|
||||
|
||||
while (y < 25) {
|
||||
if (loc.clone().add(0, y, 0).getBlock().getType() != Material.AIR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
y++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package net.nuggetmc.tplus.bot.agent.legacyagent.ai;
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.api.Terminator;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.BotDataType;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.BotNode;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.EnumTargetGoal;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.tplus.command.commands.AICommand;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import net.nuggetmc.tplus.utils.MathUtils;
|
||||
import net.nuggetmc.tplus.utils.MojangAPI;
|
||||
import net.nuggetmc.tplus.utils.PlayerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class IntelligenceAgent {
|
||||
|
||||
/*
|
||||
* export all agent data to the plugin folder as separate folder things
|
||||
* commands /ai stop and /ai pause
|
||||
* if a session with name already exists keep adding underscores
|
||||
* /ai conclude or /ai finish
|
||||
* default anchor location, /ai relocateanchor
|
||||
*/
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BotManager manager;
|
||||
private final AICommand aiManager;
|
||||
private final BukkitScheduler scheduler;
|
||||
|
||||
private LegacyAgent agent;
|
||||
private Thread thread;
|
||||
private boolean active;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String botName;
|
||||
private final String botSkin;
|
||||
private final int cutoff;
|
||||
|
||||
private final Map<String, Bot> bots;
|
||||
|
||||
private int populationSize;
|
||||
private int generation;
|
||||
|
||||
private Player primary;
|
||||
|
||||
private final Set<CommandSender> users;
|
||||
private final Map<Integer, Set<Map<BotNode, Map<BotDataType, Double>>>> genProfiles;
|
||||
|
||||
public IntelligenceAgent(AICommand aiManager, int populationSize, String name, String skin) {
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = plugin.getManager();
|
||||
this.aiManager = aiManager;
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.name = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(Calendar.getInstance().getTime());
|
||||
this.botName = name;
|
||||
this.botSkin = skin;
|
||||
this.bots = new HashMap<>();
|
||||
this.users = new HashSet<>(Collections.singletonList(Bukkit.getConsoleSender()));
|
||||
this.cutoff = 5;
|
||||
this.genProfiles = new HashMap<>();
|
||||
this.populationSize = populationSize;
|
||||
this.active = true;
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
thread = Thread.currentThread();
|
||||
|
||||
try {
|
||||
task();
|
||||
} catch (Exception e) {
|
||||
print(e);
|
||||
print("The thread has been interrupted.");
|
||||
print("The session will now close.");
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void task() throws InterruptedException {
|
||||
setup();
|
||||
sleep(1000);
|
||||
|
||||
while (active) {
|
||||
runGeneration();
|
||||
}
|
||||
|
||||
sleep(5000);
|
||||
close();
|
||||
}
|
||||
|
||||
private void runGeneration() throws InterruptedException {
|
||||
generation++;
|
||||
|
||||
print("Starting generation " + ChatColor.RED + generation + ChatColor.RESET + "...");
|
||||
|
||||
sleep(2000);
|
||||
|
||||
String skinName = botSkin == null ? this.botName : botSkin;
|
||||
|
||||
print("Fetching skin data for " + ChatColor.GREEN + skinName + ChatColor.RESET + "...");
|
||||
|
||||
String[] skinData = MojangAPI.getSkin(skinName);
|
||||
|
||||
String botName = this.botName.endsWith("%") ? this.botName : this.botName + "%";
|
||||
|
||||
print("Creating " + (populationSize == 1 ? "new bot" : ChatColor.RED + NumberFormat.getInstance(Locale.US).format(populationSize) + ChatColor.RESET + " new bots")
|
||||
+ " with name " + ChatColor.GREEN + botName.replace("%", ChatColor.LIGHT_PURPLE + "%" + ChatColor.RESET)
|
||||
+ (botSkin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + botSkin)
|
||||
+ ChatColor.RESET + "...");
|
||||
|
||||
Set<Map<BotNode, Map<BotDataType, Double>>> loadedProfiles = genProfiles.get(generation);
|
||||
Location loc = PlayerUtils.findAbove(primary.getLocation(), 20);
|
||||
|
||||
scheduler.runTask(plugin, () -> {
|
||||
Set<Bot> bots;
|
||||
|
||||
if (loadedProfiles == null) {
|
||||
bots = manager.createBots(loc, botName, skinData, populationSize, NeuralNetwork.RANDOM);
|
||||
} else {
|
||||
List<NeuralNetwork> networks = new ArrayList<>();
|
||||
loadedProfiles.forEach(profile -> networks.add(NeuralNetwork.createNetworkFromProfile(profile)));
|
||||
|
||||
if (populationSize != networks.size()) {
|
||||
print("An exception has occured.");
|
||||
print("The stored population size differs from the size of the stored networks.");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
bots = manager.createBots(loc, botName, skinData, networks);
|
||||
}
|
||||
|
||||
bots.forEach(bot -> {
|
||||
String name = bot.getName().getString();
|
||||
|
||||
while (this.bots.containsKey(name)) {
|
||||
name += "_";
|
||||
}
|
||||
|
||||
this.bots.put(name, bot);
|
||||
});
|
||||
});
|
||||
|
||||
while (bots.size() != populationSize) {
|
||||
sleep(1000);
|
||||
}
|
||||
|
||||
sleep(2000);
|
||||
print("The bots will now attack each other.");
|
||||
|
||||
agent.setTargetType(EnumTargetGoal.NEAREST_BOT);
|
||||
|
||||
while (aliveCount() > 1) {
|
||||
sleep(1000);
|
||||
}
|
||||
|
||||
print("Generation " + ChatColor.RED + generation + ChatColor.RESET + " has ended.");
|
||||
|
||||
HashMap<Terminator, Integer> values = new HashMap<>();
|
||||
|
||||
for (Terminator bot : bots.values()) {
|
||||
values.put(bot, bot.getAliveTicks());
|
||||
}
|
||||
|
||||
List<Map.Entry<Terminator, Integer>> sorted = MathUtils.sortByValue(values);
|
||||
Set<Terminator> winners = new HashSet<>();
|
||||
|
||||
int i = 1;
|
||||
|
||||
for (Map.Entry<Terminator, Integer> entry : sorted) {
|
||||
Terminator bot = entry.getKey();
|
||||
boolean check = i <= cutoff;
|
||||
if (check) {
|
||||
print(ChatColor.GRAY + "[" + ChatColor.YELLOW + "#" + i + ChatColor.GRAY + "] " + ChatColor.GREEN + bot.getBotName()
|
||||
+ ChatUtils.BULLET_FORMATTED + ChatColor.RED + bot.getKills() + " kills");
|
||||
winners.add(bot);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
sleep(3000);
|
||||
|
||||
Map<BotNode, Map<BotDataType, List<Double>>> lists = new HashMap<>();
|
||||
|
||||
winners.forEach(bot -> {
|
||||
Map<BotNode, Map<BotDataType, Double>> data = bot.getNeuralNetwork().values();
|
||||
|
||||
data.forEach((nodeType, node) -> {
|
||||
if (!lists.containsKey(nodeType)) {
|
||||
lists.put(nodeType, new HashMap<>());
|
||||
}
|
||||
|
||||
Map<BotDataType, List<Double>> nodeValues = lists.get(nodeType);
|
||||
|
||||
node.forEach((dataType, value) -> {
|
||||
if (!nodeValues.containsKey(dataType)) {
|
||||
nodeValues.put(dataType, new ArrayList<>());
|
||||
}
|
||||
|
||||
nodeValues.get(dataType).add(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Set<Map<BotNode, Map<BotDataType, Double>>> profiles = new HashSet<>();
|
||||
|
||||
double mutationSize = Math.pow(Math.E, 2); //MathUtils.getMutationSize(generation);
|
||||
|
||||
for (int j = 0; j < populationSize; j++) {
|
||||
Map<BotNode, Map<BotDataType, Double>> profile = new HashMap<>();
|
||||
|
||||
lists.forEach((nodeType, map) -> {
|
||||
Map<BotDataType, Double> points = new HashMap<>();
|
||||
|
||||
map.forEach((dataType, dataPoints) -> {
|
||||
double value = ((int) (10 * MathUtils.generateConnectionValue(dataPoints, mutationSize))) / 10D;
|
||||
|
||||
points.put(dataType, value);
|
||||
});
|
||||
|
||||
profile.put(nodeType, points);
|
||||
});
|
||||
|
||||
profiles.add(profile);
|
||||
}
|
||||
|
||||
genProfiles.put(generation + 1, profiles);
|
||||
|
||||
sleep(2000);
|
||||
|
||||
clearBots();
|
||||
|
||||
agent.setTargetType(EnumTargetGoal.NONE);
|
||||
}
|
||||
|
||||
private int aliveCount() {
|
||||
return (int) bots.values().stream().filter(LivingEntity::isAlive).count();
|
||||
}
|
||||
|
||||
private void close() {
|
||||
aiManager.clearSession();
|
||||
stop(); // safety call
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.active) {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
if (!thread.isInterrupted()) {
|
||||
this.thread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep(long millis) throws InterruptedException {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void addUser(CommandSender sender) {
|
||||
if (users.contains(sender)) return;
|
||||
|
||||
users.add(sender);
|
||||
print(sender.getName() + " has been added to the userlist.");
|
||||
|
||||
if (primary == null && sender instanceof Player) {
|
||||
setPrimary((Player) sender);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPrimary(Player player) {
|
||||
this.primary = player;
|
||||
print(player.getName() + " has been set as the primary user.");
|
||||
}
|
||||
|
||||
private void print(Object... objects) {
|
||||
String message = ChatColor.DARK_GREEN + "[REINFORCEMENT] " + ChatColor.RESET + String.join(" ", Arrays.stream(objects).map(String::valueOf).toArray(String[]::new));
|
||||
users.forEach(u -> u.sendMessage(message));
|
||||
// log -> ChatColor.stripColor(message);
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
clearBots();
|
||||
|
||||
if (populationSize < cutoff) {
|
||||
populationSize = cutoff;
|
||||
print("The input value for the population size is lower than the cutoff (" + ChatColor.RED + cutoff + ChatColor.RESET + ")!"
|
||||
+ " The new population size is " + ChatColor.RED + populationSize + ChatColor.RESET + ".");
|
||||
}
|
||||
|
||||
if (!(manager.getAgent() instanceof LegacyAgent)) {
|
||||
print("The AI manager currently only supports " + ChatColor.AQUA + "LegacyAgent" + ChatColor.RESET + ".");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
agent = (LegacyAgent) manager.getAgent();
|
||||
agent.setTargetType(EnumTargetGoal.NONE);
|
||||
|
||||
print("The bot target goal has been set to " + ChatColor.YELLOW + EnumTargetGoal.NONE.name() + ChatColor.RESET + ".");
|
||||
print("Disabling target offsets...");
|
||||
|
||||
agent.offsets = false;
|
||||
|
||||
print("Disabling bot drops...");
|
||||
|
||||
agent.setDrops(false);
|
||||
|
||||
print(ChatColor.GREEN + "Setup is now complete.");
|
||||
}
|
||||
|
||||
private void clearBots() {
|
||||
if (!bots.isEmpty()) {
|
||||
print("Removing all cached bots...");
|
||||
|
||||
bots.values().forEach(Bot::removeVisually);
|
||||
bots.clear();
|
||||
}
|
||||
|
||||
/*print("Removing all current bots...");
|
||||
|
||||
int size = manager.fetch().size();
|
||||
manager.reset();
|
||||
|
||||
String formatted = NumberFormat.getNumberInstance(Locale.US).format(size);
|
||||
print("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + ".");
|
||||
|
||||
bots.clear();*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.nuggetmc.tplus.bot.event;
|
||||
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BotDamageByPlayerEvent {
|
||||
|
||||
private final Bot bot;
|
||||
private final Player player;
|
||||
|
||||
private float damage;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public BotDamageByPlayerEvent(Bot bot, Player player, float damage) {
|
||||
this.bot = bot;
|
||||
this.player = player;
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public float getDamage() {
|
||||
return damage;
|
||||
}
|
||||
|
||||
public void setDamage(float damage) {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.nuggetmc.tplus.bot.event;
|
||||
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
|
||||
public class BotDeathEvent extends EntityDeathEvent {
|
||||
|
||||
private final Bot bot;
|
||||
|
||||
public BotDeathEvent(EntityDeathEvent event, Bot bot) {
|
||||
super(event.getEntity(), event.getDrops(), event.getDroppedExp());
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.nuggetmc.tplus.bot.event;
|
||||
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
|
||||
public class BotFallDamageEvent {
|
||||
|
||||
private final Bot bot;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public BotFallDamageEvent(Bot bot) {
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.nuggetmc.tplus.bot.event;
|
||||
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BotKilledByPlayerEvent {
|
||||
|
||||
// eventually also call this event for deaths from other damage causes within combat time
|
||||
// (like hitting the ground too hard)
|
||||
|
||||
private final Bot bot;
|
||||
private final Player player;
|
||||
|
||||
public BotKilledByPlayerEvent(Bot bot, Player player) {
|
||||
this.bot = bot;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package net.nuggetmc.tplus.command;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.command.annotation.Command;
|
||||
import net.nuggetmc.tplus.command.annotation.Require;
|
||||
import net.nuggetmc.tplus.command.commands.AICommand;
|
||||
import net.nuggetmc.tplus.command.commands.BotCommand;
|
||||
import net.nuggetmc.tplus.command.commands.MainCommand;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import net.nuggetmc.tplus.utils.Debugger;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommandHandler {
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
|
||||
private final Map<String, List<String>> help;
|
||||
private final Map<String, CommandInstance> commandMap;
|
||||
|
||||
public CommandHandler(TerminatorPlus plugin) {
|
||||
this.plugin = plugin;
|
||||
this.help = new HashMap<>();
|
||||
this.commandMap = new HashMap<>();
|
||||
this.registerCommands();
|
||||
}
|
||||
|
||||
public Map<String, CommandInstance> getCommands() {
|
||||
return commandMap;
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
registerCommands(
|
||||
new MainCommand(this, "terminatorplus", "The TerminatorPlus main command.", "tplus"),
|
||||
new BotCommand(this, "bot", "The root command for bot management.", "npc"),
|
||||
new AICommand(this, "ai", "The root command for bot AI training.")
|
||||
);
|
||||
}
|
||||
|
||||
private void registerCommands(CommandInstance... commands) {
|
||||
String fallback = plugin.getName().toLowerCase();
|
||||
SimpleCommandMap bukkitCommandMap = ((CraftServer) plugin.getServer()).getCommandMap();
|
||||
|
||||
for (CommandInstance command : commands) {
|
||||
commandMap.put(command.getName(), command);
|
||||
bukkitCommandMap.register(fallback, command);
|
||||
|
||||
Method[] methods = command.getClass().getMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(Command.class)) {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
} catch (SecurityException e) {
|
||||
Debugger.log("Failed to access method " + method.getName() + ".");
|
||||
continue;
|
||||
}
|
||||
|
||||
Command cmd = method.getAnnotation(Command.class);
|
||||
|
||||
String perm = "";
|
||||
if (method.isAnnotationPresent(Require.class)) {
|
||||
Require require = method.getAnnotation(Require.class);
|
||||
perm = require.value();
|
||||
}
|
||||
|
||||
String autofillName = cmd.autofill();
|
||||
Method autofiller = null;
|
||||
|
||||
if (!autofillName.isEmpty()) {
|
||||
for (Method m : methods) {
|
||||
if (m.getName().equals(autofillName)) {
|
||||
autofiller = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String methodName = cmd.name();
|
||||
CommandMethod commandMethod = new CommandMethod(methodName, Sets.newHashSet(cmd.aliases()), cmd.desc(), perm, command, method, autofiller);
|
||||
|
||||
command.addMethod(methodName, commandMethod);
|
||||
}
|
||||
}
|
||||
|
||||
setHelp(command);
|
||||
}
|
||||
}
|
||||
|
||||
public CommandInstance getCommand(String name) {
|
||||
return commandMap.get(name);
|
||||
}
|
||||
|
||||
public void sendRootInfo(CommandInstance commandInstance, CommandSender sender) {
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GOLD + plugin.getName() + ChatUtils.BULLET_FORMATTED + ChatColor.GRAY
|
||||
+ "[" + ChatColor.YELLOW + "/" + commandInstance.getName() + ChatColor.GRAY + "]");
|
||||
help.get(commandInstance.getName()).forEach(sender::sendMessage);
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
}
|
||||
|
||||
private void setHelp(CommandInstance commandInstance) {
|
||||
help.put(commandInstance.getName(), getCommandInfo(commandInstance));
|
||||
}
|
||||
|
||||
private List<String> getCommandInfo(CommandInstance commandInstance) {
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
for (CommandMethod method : commandInstance.getMethods().values()) {
|
||||
if (!method.getMethod().getAnnotation(Command.class).visible() || method.getName().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output.add(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "/" + commandInstance.getName() + " " + method.getName()
|
||||
+ ChatUtils.BULLET_FORMATTED + method.getDescription());
|
||||
}
|
||||
|
||||
return output.stream().sorted().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package net.nuggetmc.tplus.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.nuggetmc.tplus.command.annotation.Arg;
|
||||
import net.nuggetmc.tplus.command.annotation.OptArg;
|
||||
import net.nuggetmc.tplus.command.annotation.TextArg;
|
||||
import net.nuggetmc.tplus.command.exception.ArgCountException;
|
||||
import net.nuggetmc.tplus.command.exception.ArgParseException;
|
||||
import net.nuggetmc.tplus.command.exception.NonPlayerException;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class CommandInstance extends BukkitCommand {
|
||||
|
||||
protected final CommandHandler commandHandler;
|
||||
|
||||
private final Map<String, CommandMethod> methods;
|
||||
|
||||
private static final String MANAGE_PERMISSION = "terminatorplus.manage";
|
||||
|
||||
public CommandInstance(CommandHandler handler, String name, String description, @Nullable String... aliases) {
|
||||
super(name, description, "", aliases == null ? new ArrayList<>() : Arrays.asList(aliases));
|
||||
|
||||
this.commandHandler = handler;
|
||||
this.methods = new HashMap<>();
|
||||
}
|
||||
|
||||
public Map<String, CommandMethod> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
protected void addMethod(String name, CommandMethod method) {
|
||||
methods.put(name, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@Nonnull CommandSender sender, @Nonnull String label, @Nonnull String[] args) {
|
||||
if (!sender.hasPermission(MANAGE_PERMISSION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandMethod method;
|
||||
|
||||
if (args.length == 0) {
|
||||
method = methods.get("");
|
||||
} else if (methods.containsKey(args[0])) {
|
||||
method = methods.get(args[0]);
|
||||
} else {
|
||||
method = methods.get("");
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
sender.sendMessage(ChatColor.RED + "There is no root command present for the " + ChatColor.YELLOW + getName() + ChatColor.RED + " command.");
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> arguments = new ArrayList<>(Arrays.asList(args));
|
||||
|
||||
if (arguments.size() > 0) {
|
||||
arguments.remove(0);
|
||||
}
|
||||
|
||||
List<Object> parsedArguments = new ArrayList<>();
|
||||
|
||||
int index = 0;
|
||||
|
||||
try {
|
||||
for (Parameter parameter : method.getMethod().getParameters()) {
|
||||
Class<?> type = parameter.getType();
|
||||
|
||||
boolean required = !parameter.isAnnotationPresent(OptArg.class);
|
||||
|
||||
if (type == CommandSender.class) {
|
||||
parsedArguments.add(sender);
|
||||
} else if (type == Player.class) {
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NonPlayerException();
|
||||
}
|
||||
|
||||
parsedArguments.add(sender);
|
||||
} else if (type == List.class) {
|
||||
parsedArguments.add(arguments);
|
||||
}
|
||||
|
||||
else {
|
||||
if (parameter.isAnnotationPresent(TextArg.class)) {
|
||||
if (index >= arguments.size()) {
|
||||
parsedArguments.add("");
|
||||
} else {
|
||||
parsedArguments.add(StringUtils.join(arguments.subList(index, arguments.size()), " "));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index >= arguments.size() && required) {
|
||||
throw new ArgCountException();
|
||||
}
|
||||
|
||||
String arg;
|
||||
|
||||
if (index >= arguments.size()) {
|
||||
arg = null;
|
||||
} else {
|
||||
arg = arguments.get(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (type == String.class) {
|
||||
parsedArguments.add(arg);
|
||||
} else if (type == int.class) {
|
||||
if (arg == null) {
|
||||
parsedArguments.add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
parsedArguments.add(Integer.parseInt(arg));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgParseException(parameter);
|
||||
}
|
||||
} else if (type == double.class) {
|
||||
if (arg == null) {
|
||||
parsedArguments.add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
parsedArguments.add(Double.parseDouble(arg));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgParseException(parameter);
|
||||
}
|
||||
} else if (type == float.class) {
|
||||
if (arg == null) {
|
||||
parsedArguments.add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
parsedArguments.add(Float.parseFloat(arg));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgParseException(parameter);
|
||||
}
|
||||
} else if (type == boolean.class) {
|
||||
if (arg == null) {
|
||||
parsedArguments.add(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.equalsIgnoreCase("true") || arg.equalsIgnoreCase("false")) {
|
||||
parsedArguments.add(Boolean.parseBoolean(arg));
|
||||
} else {
|
||||
throw new ArgParseException(parameter);
|
||||
}
|
||||
} else {
|
||||
parsedArguments.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (NonPlayerException e) {
|
||||
sender.sendMessage("This is a player-only command.");
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (ArgParseException e) {
|
||||
Parameter parameter = e.getParameter();
|
||||
String name = getArgumentName(parameter);
|
||||
sender.sendMessage("The parameter " + ChatColor.YELLOW + name + ChatColor.RESET + " must be of type " + ChatColor.YELLOW + parameter.getType().toString() + ChatColor.RESET + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (ArgCountException e) {
|
||||
List<String> usageArgs = new ArrayList<>();
|
||||
|
||||
Arrays.stream(method.getMethod().getParameters()).forEach(parameter -> {
|
||||
Class<?> type = parameter.getType();
|
||||
|
||||
if (type != CommandSender.class && type != Player.class){
|
||||
usageArgs.add(getArgumentName(parameter));
|
||||
}
|
||||
});
|
||||
|
||||
sender.sendMessage("Command Usage: " + org.bukkit.ChatColor.YELLOW + "/" + getName() + (method.getName().isEmpty() ? "" : " " + method.getName())
|
||||
+ " " + StringUtils.join(usageArgs, " "));
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
method.getMethod().invoke(method.getHandler(), parsedArguments.toArray());
|
||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||
sender.sendMessage(ChatColor.RED + "Failed to perform command.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String getArgumentName(Parameter parameter) {
|
||||
if (parameter.isAnnotationPresent(OptArg.class)) {
|
||||
OptArg arg = parameter.getAnnotation(OptArg.class);
|
||||
|
||||
if (!arg.value().isEmpty()) {
|
||||
return "[" + ChatUtils.camelToDashed(arg.value()) + "]";
|
||||
}
|
||||
} else if (parameter.isAnnotationPresent(Arg.class)) {
|
||||
Arg arg = parameter.getAnnotation(Arg.class);
|
||||
|
||||
if (!arg.value().isEmpty()) {
|
||||
return "<" + ChatUtils.camelToDashed(arg.value()) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
return "<" + ChatUtils.camelToDashed(parameter.getName()) + ">";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> tabComplete(@Nonnull CommandSender sender, @Nonnull String label, @Nonnull String[] args) {
|
||||
if (args.length == 1) {
|
||||
return methods.keySet().stream().filter(c -> !c.isEmpty()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (args.length > 1) {
|
||||
CommandMethod commandMethod = methods.get(args[0]);
|
||||
Method autofiller = commandMethod.getAutofiller();
|
||||
|
||||
if (autofiller != null) {
|
||||
try {
|
||||
return (List<String>) autofiller.invoke(commandMethod.getHandler(), sender, args);
|
||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.nuggetmc.tplus.command;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
public class CommandMethod {
|
||||
|
||||
private final String name;
|
||||
private final Set<String> aliases;
|
||||
private final String description;
|
||||
private final String permission;
|
||||
|
||||
private final CommandInstance handler;
|
||||
|
||||
private final Method method;
|
||||
private final Method autofiller;
|
||||
|
||||
public CommandMethod(String name, Set<String> aliases, String description, String permission, CommandInstance handler, Method method, Method autofiller) {
|
||||
this.name = name;
|
||||
this.aliases = aliases;
|
||||
this.description = description;
|
||||
this.permission = permission;
|
||||
this.handler = handler;
|
||||
this.method = method;
|
||||
this.autofiller = autofiller;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Set<String> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public CommandInstance getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public Method getAutofiller() {
|
||||
return autofiller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "{name=\"" + name + "\",aliases=" + aliases + ",description=\"" + description + "\",permission=\"" + permission + "\",method=" + method + ",autofiller=" + autofiller + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Arg {
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
public @interface Autofill {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface Command {
|
||||
String name() default "";
|
||||
|
||||
String[] aliases() default {};
|
||||
|
||||
String desc() default "Blank description.";
|
||||
|
||||
String autofill() default "";
|
||||
|
||||
boolean visible() default true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OptArg {
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface Require {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.nuggetmc.tplus.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface TextArg {
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package net.nuggetmc.tplus.command.commands;
|
||||
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.ai.IntelligenceAgent;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.tplus.command.CommandHandler;
|
||||
import net.nuggetmc.tplus.command.CommandInstance;
|
||||
import net.nuggetmc.tplus.command.annotation.Arg;
|
||||
import net.nuggetmc.tplus.command.annotation.Autofill;
|
||||
import net.nuggetmc.tplus.command.annotation.Command;
|
||||
import net.nuggetmc.tplus.command.annotation.OptArg;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import net.nuggetmc.tplus.utils.MathUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AICommand extends CommandInstance {
|
||||
|
||||
/*
|
||||
* ideas
|
||||
* ability to export neural network data to a text file, and also load from them
|
||||
* maybe also have a custom extension like .tplus and encrypt it in base64
|
||||
*/
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BotManager manager;
|
||||
private final BukkitScheduler scheduler;
|
||||
|
||||
private IntelligenceAgent agent;
|
||||
|
||||
public AICommand(CommandHandler handler, String name, String description, String... aliases) {
|
||||
super(handler, name, description, aliases);
|
||||
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = plugin.getManager();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
}
|
||||
|
||||
@Command
|
||||
public void root(CommandSender sender, List<String> args) {
|
||||
commandHandler.sendRootInfo(this, sender);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "random",
|
||||
desc = "Create bots with random neural networks, collecting feed data."
|
||||
)
|
||||
public void random(Player sender, @Arg("amount") int amount, @Arg("name") String name, @OptArg("skin") String skin) {
|
||||
manager.createBots(sender, name, skin, amount, NeuralNetwork.RANDOM);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "reinforcement",
|
||||
desc = "Begin an AI training session."
|
||||
)
|
||||
public void reinforcement(Player sender, @Arg("population-size") int populationSize, @Arg("name") String name, @OptArg("skin") String skin) {
|
||||
if (agent != null) {
|
||||
sender.sendMessage("A session is already active.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Starting a new session...");
|
||||
|
||||
agent = new IntelligenceAgent(this, populationSize, name, skin);
|
||||
agent.addUser(sender);
|
||||
}
|
||||
|
||||
public IntelligenceAgent getSession() {
|
||||
return agent;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "stop",
|
||||
desc = "End a currently running AI training session."
|
||||
)
|
||||
public void stop(CommandSender sender) {
|
||||
if (agent == null) {
|
||||
sender.sendMessage("No session is currently active.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Stopping the current session...");
|
||||
String name = agent.getName();
|
||||
clearSession();
|
||||
|
||||
scheduler.runTaskLater(plugin, () -> sender.sendMessage("The session " + ChatColor.YELLOW + name + ChatColor.RESET + " has been closed."), 10);
|
||||
}
|
||||
|
||||
public void clearSession() {
|
||||
if (agent != null) {
|
||||
agent.stop();
|
||||
agent = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasActiveSession() {
|
||||
return agent != null;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "info",
|
||||
desc = "Display neural network information about a bot.",
|
||||
autofill = "infoAutofill"
|
||||
)
|
||||
public void info(CommandSender sender, @Arg("bot-name") String name) {
|
||||
sender.sendMessage("Processing request...");
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
Bot bot = manager.getFirst(name);
|
||||
|
||||
if (bot == null) {
|
||||
sender.sendMessage("Could not find bot " + ChatColor.GREEN + name + ChatColor.RESET + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot.hasNeuralNetwork()) {
|
||||
sender.sendMessage("The bot " + ChatColor.GREEN + name + ChatColor.RESET + " does not have a neural network!");
|
||||
return;
|
||||
}
|
||||
|
||||
NeuralNetwork network = bot.getNeuralNetwork();
|
||||
List<String> strings = new ArrayList<>();
|
||||
|
||||
network.nodes().forEach((nodeType, node) -> {
|
||||
strings.add("");
|
||||
strings.add(ChatColor.YELLOW + "\"" + nodeType.name().toLowerCase() + "\"" + ChatColor.RESET + ":");
|
||||
List<String> values = new ArrayList<>();
|
||||
node.getValues().forEach((dataType, value) -> values.add(ChatUtils.BULLET_FORMATTED + "node"
|
||||
+ dataType.getShorthand().toUpperCase() + ": " + ChatColor.RED + MathUtils.round2Dec(value)));
|
||||
strings.addAll(values);
|
||||
});
|
||||
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "NeuralNetwork" + ChatUtils.BULLET_FORMATTED + ChatColor.GRAY + "[" + ChatColor.GREEN + name + ChatColor.GRAY + "]");
|
||||
strings.forEach(sender::sendMessage);
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
sender.sendMessage(ChatUtils.EXCEPTION_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Autofill
|
||||
public List<String> infoAutofill(CommandSender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
return manager.fetchNames();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
package net.nuggetmc.tplus.command.commands;
|
||||
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.BotManager;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.EnumTargetGoal;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.tplus.command.CommandHandler;
|
||||
import net.nuggetmc.tplus.command.CommandInstance;
|
||||
import net.nuggetmc.tplus.command.annotation.Arg;
|
||||
import net.nuggetmc.tplus.command.annotation.Autofill;
|
||||
import net.nuggetmc.tplus.command.annotation.Command;
|
||||
import net.nuggetmc.tplus.command.annotation.OptArg;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import net.nuggetmc.tplus.utils.Debugger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class BotCommand extends CommandInstance {
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final CommandHandler handler;
|
||||
private final BotManager manager;
|
||||
private final LegacyAgent agent;
|
||||
private final BukkitScheduler scheduler;
|
||||
private final DecimalFormat formatter;
|
||||
|
||||
private AICommand aiManager;
|
||||
|
||||
public BotCommand(CommandHandler handler, String name, String description, String... aliases) {
|
||||
super(handler, name, description, aliases);
|
||||
|
||||
this.handler = commandHandler;
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = plugin.getManager();
|
||||
this.agent = (LegacyAgent) manager.getAgent();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.formatter = new DecimalFormat("0.##");
|
||||
this.armorTiers = new HashMap<>();
|
||||
|
||||
this.armorTierSetup();
|
||||
}
|
||||
|
||||
@Command
|
||||
public void root(CommandSender sender) {
|
||||
commandHandler.sendRootInfo(this, sender);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "create",
|
||||
desc = "Create a bot."
|
||||
)
|
||||
public void create(Player sender, @Arg("name") String name, @OptArg("skin") String skin) {
|
||||
manager.createBots(sender, name, skin, 1);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "multi",
|
||||
desc = "Create multiple bots at once."
|
||||
)
|
||||
public void multi(Player sender, @Arg("amount") int amount, @Arg("name") String name, @OptArg("skin") String skin) {
|
||||
manager.createBots(sender, name, skin, amount);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "give",
|
||||
desc = "Gives a specified item to all bots."
|
||||
)
|
||||
public void give(CommandSender sender, @Arg("item-name") String itemName) {
|
||||
Material type = Material.matchMaterial(itemName);
|
||||
|
||||
if (type == null) {
|
||||
sender.sendMessage("The item " + ChatColor.YELLOW + itemName + ChatColor.RESET + " is not valid!");
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack item = new ItemStack(type);
|
||||
|
||||
manager.fetch().forEach(bot -> bot.setDefaultItem(item));
|
||||
|
||||
sender.sendMessage("Successfully set the default item to " + ChatColor.YELLOW + item.getType() + ChatColor.RESET + " for all current bots.");
|
||||
}
|
||||
|
||||
private final Map<String, ItemStack[]> armorTiers;
|
||||
|
||||
private void armorTierSetup() {
|
||||
armorTiers.put("leather", new ItemStack[] {
|
||||
new ItemStack(Material.LEATHER_BOOTS),
|
||||
new ItemStack(Material.LEATHER_LEGGINGS),
|
||||
new ItemStack(Material.LEATHER_CHESTPLATE),
|
||||
new ItemStack(Material.LEATHER_HELMET),
|
||||
});
|
||||
|
||||
armorTiers.put("chain", new ItemStack[] {
|
||||
new ItemStack(Material.CHAINMAIL_BOOTS),
|
||||
new ItemStack(Material.CHAINMAIL_LEGGINGS),
|
||||
new ItemStack(Material.CHAINMAIL_CHESTPLATE),
|
||||
new ItemStack(Material.CHAINMAIL_HELMET),
|
||||
});
|
||||
|
||||
armorTiers.put("gold", new ItemStack[] {
|
||||
new ItemStack(Material.GOLDEN_BOOTS),
|
||||
new ItemStack(Material.GOLDEN_LEGGINGS),
|
||||
new ItemStack(Material.GOLDEN_CHESTPLATE),
|
||||
new ItemStack(Material.GOLDEN_HELMET),
|
||||
});
|
||||
|
||||
armorTiers.put("iron", new ItemStack[] {
|
||||
new ItemStack(Material.IRON_BOOTS),
|
||||
new ItemStack(Material.IRON_LEGGINGS),
|
||||
new ItemStack(Material.IRON_CHESTPLATE),
|
||||
new ItemStack(Material.IRON_HELMET),
|
||||
});
|
||||
|
||||
armorTiers.put("diamond", new ItemStack[] {
|
||||
new ItemStack(Material.DIAMOND_BOOTS),
|
||||
new ItemStack(Material.DIAMOND_LEGGINGS),
|
||||
new ItemStack(Material.DIAMOND_CHESTPLATE),
|
||||
new ItemStack(Material.DIAMOND_HELMET),
|
||||
});
|
||||
|
||||
armorTiers.put("netherite", new ItemStack[] {
|
||||
new ItemStack(Material.NETHERITE_BOOTS),
|
||||
new ItemStack(Material.NETHERITE_LEGGINGS),
|
||||
new ItemStack(Material.NETHERITE_CHESTPLATE),
|
||||
new ItemStack(Material.NETHERITE_HELMET),
|
||||
});
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "armor",
|
||||
desc = "Gives all bots an armor set.",
|
||||
autofill = "armorAutofill"
|
||||
)
|
||||
@SuppressWarnings("deprecation")
|
||||
public void armor(CommandSender sender, @Arg("armor-tier") String armorTier) {
|
||||
String tier = armorTier.toLowerCase();
|
||||
|
||||
if (!armorTiers.containsKey(tier)) {
|
||||
sender.sendMessage(ChatColor.YELLOW + tier + ChatColor.RESET + " is not a valid tier!");
|
||||
sender.sendMessage("Available tiers: " + ChatColor.YELLOW + String.join(ChatColor.RESET + ", " + ChatColor.YELLOW, armorTiers.keySet()));
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack[] armor = armorTiers.get(tier);
|
||||
|
||||
manager.fetch().forEach(bot -> {
|
||||
bot.getBukkitEntity().getInventory().setArmorContents(armor);
|
||||
bot.getBukkitEntity().updateInventory();
|
||||
|
||||
// packet sending to ensure
|
||||
bot.setItem(armor[0], EquipmentSlot.FEET);
|
||||
bot.setItem(armor[1], EquipmentSlot.LEGS);
|
||||
bot.setItem(armor[2], EquipmentSlot.CHEST);
|
||||
bot.setItem(armor[3], EquipmentSlot.HEAD);
|
||||
});
|
||||
|
||||
sender.sendMessage("Successfully set the armor tier to " + ChatColor.YELLOW + tier + ChatColor.RESET + " for all current bots.");
|
||||
}
|
||||
|
||||
@Autofill
|
||||
public List<String> armorAutofill(CommandSender sender, String[] args) {
|
||||
return args.length == 2 ? new ArrayList<>(armorTiers.keySet()) : null;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "info",
|
||||
desc = "Information about loaded bots.",
|
||||
autofill = "infoAutofill"
|
||||
)
|
||||
public void info(CommandSender sender, @Arg("bot-name") String name) {
|
||||
if (name == null) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Bot GUI coming soon!");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Processing request...");
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
Bot bot = manager.getFirst(name);
|
||||
|
||||
if (bot == null) {
|
||||
sender.sendMessage("Could not find bot " + ChatColor.GREEN + name + ChatColor.RESET + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* time created
|
||||
* current life (how long it has lived for)
|
||||
* health
|
||||
* inventory
|
||||
* current target
|
||||
* current kills
|
||||
* skin
|
||||
* neural network values (network name if loaded, otherwise RANDOM)
|
||||
*/
|
||||
|
||||
String botName = bot.getName().getString();
|
||||
String world = ChatColor.YELLOW + bot.getBukkitEntity().getWorld().getName();
|
||||
Location loc = bot.getLocation();
|
||||
String strLoc = ChatColor.YELLOW + formatter.format(loc.getBlockX()) + ", " + formatter.format(loc.getBlockY()) + ", " + formatter.format(loc.getBlockZ());
|
||||
Vector vel = bot.getVelocity();
|
||||
String strVel = ChatColor.AQUA + formatter.format(vel.getX()) + ", " + formatter.format(vel.getY()) + ", " + formatter.format(vel.getZ());
|
||||
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GREEN + botName);
|
||||
sender.sendMessage(ChatUtils.BULLET_FORMATTED + "World: " + world);
|
||||
sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Position: " + strLoc);
|
||||
sender.sendMessage(ChatUtils.BULLET_FORMATTED + "Velocity: " + strVel);
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
sender.sendMessage(ChatUtils.EXCEPTION_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Autofill
|
||||
public List<String> infoAutofill(CommandSender sender, String[] args) {
|
||||
return args.length == 2 ? manager.fetchNames() : null;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "reset",
|
||||
desc = "Remove all loaded bots."
|
||||
)
|
||||
public void reset(CommandSender sender) {
|
||||
sender.sendMessage("Removing every bot...");
|
||||
int size = manager.fetch().size();
|
||||
manager.reset();
|
||||
sender.sendMessage("Removed " + ChatColor.RED + ChatUtils.NUMBER_FORMAT.format(size) + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + ".");
|
||||
|
||||
if (aiManager == null) {
|
||||
this.aiManager = (AICommand) handler.getCommand("ai");
|
||||
}
|
||||
|
||||
if (aiManager != null && aiManager.hasActiveSession()) {
|
||||
Bukkit.dispatchCommand(sender, "ai stop");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* EVENTUALLY, we should make a command parent hierarchy system soon too! (so we don't have to do this crap)
|
||||
* basically, in the @Command annotation, you can include a "parent" for the command, so it will be a subcommand under the specified parent
|
||||
*/
|
||||
@Command(
|
||||
name = "settings",
|
||||
desc = "Make changes to the global configuration file and bot-specific settings.",
|
||||
aliases = "options",
|
||||
autofill = "settingsAutofill"
|
||||
)
|
||||
public void settings(CommandSender sender, List<String> args) {
|
||||
String arg1 = args.isEmpty() ? null : args.get(0);
|
||||
String arg2 = args.size() < 2 ? null : args.get(1);
|
||||
|
||||
String extra = ChatColor.GRAY + " [" + ChatColor.YELLOW + "/bot settings" + ChatColor.GRAY + "]";
|
||||
|
||||
if (arg1 == null || (!arg1.equals("setgoal"))) {
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GOLD + "Bot Settings" + extra);
|
||||
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "setgoal" + ChatUtils.BULLET_FORMATTED + "Set the global bot target selection method.");
|
||||
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "mobtarget" + ChatUtils.BULLET_FORMATTED + "Allow all future bots spawned to be targetted by hostile mobs.");
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
return;
|
||||
}
|
||||
|
||||
EnumTargetGoal goal = EnumTargetGoal.from(arg2 == null ? "" : arg2);
|
||||
|
||||
if (goal == null) {
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GOLD + "Goal Selection Types" + extra);
|
||||
Arrays.stream(EnumTargetGoal.values()).forEach(g -> sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + g.name().replace("_", "").toLowerCase()
|
||||
+ ChatUtils.BULLET_FORMATTED + g.description()));
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
return;
|
||||
}
|
||||
|
||||
agent.setTargetType(goal);
|
||||
|
||||
sender.sendMessage("The global bot goal has been set to " + ChatColor.BLUE + goal.name() + ChatColor.RESET + ".");
|
||||
}
|
||||
|
||||
@Autofill
|
||||
public List<String> settingsAutofill(CommandSender sender, String[] args) {
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
// More settings:
|
||||
// setitem
|
||||
// tpall
|
||||
// tprandom
|
||||
// hidenametags or nametags <show/hide>
|
||||
// sitall
|
||||
// lookall
|
||||
|
||||
if (args.length == 2) {
|
||||
output.add("setgoal");
|
||||
}
|
||||
|
||||
else if (args.length == 3) {
|
||||
if (args[1].equalsIgnoreCase("setgoal")) {
|
||||
Arrays.stream(EnumTargetGoal.values()).forEach(goal -> output.add(goal.name().replace("_", "").toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "debug",
|
||||
desc = "Debug plugin code.",
|
||||
visible = false
|
||||
)
|
||||
public void debug(CommandSender sender, @Arg("expression") String expression) {
|
||||
new Debugger(sender).execute(expression);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package net.nuggetmc.tplus.command.commands;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.command.CommandHandler;
|
||||
import net.nuggetmc.tplus.command.CommandInstance;
|
||||
import net.nuggetmc.tplus.command.annotation.Command;
|
||||
import net.nuggetmc.tplus.utils.ChatUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class MainCommand extends CommandInstance {
|
||||
|
||||
private BaseComponent[] rootInfo;
|
||||
|
||||
public MainCommand(CommandHandler handler, String name, String description, String... aliases) {
|
||||
super(handler, name, description, aliases);
|
||||
}
|
||||
|
||||
@Command
|
||||
public void root(CommandSender sender) {
|
||||
if (rootInfo == null) {
|
||||
rootInfoSetup();
|
||||
}
|
||||
|
||||
sender.spigot().sendMessage(rootInfo);
|
||||
}
|
||||
|
||||
private void rootInfoSetup() {
|
||||
ComponentBuilder message = new ComponentBuilder();
|
||||
String pluginName = TerminatorPlus.getInstance().getName();
|
||||
|
||||
message.append(ChatUtils.LINE + "\n");
|
||||
message.append(ChatColor.GOLD + pluginName + ChatColor.GRAY + " [v" + TerminatorPlus.getVersion() + "]\n");
|
||||
message.append("\nPlugin Information:\n");
|
||||
message.append(ChatUtils.BULLET_FORMATTED + "Author" + ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "HorseNuggets\n");
|
||||
message.append(ChatUtils.BULLET_FORMATTED + "Links" + ChatUtils.BULLET_FORMATTED);
|
||||
message.append(ChatColor.RED + "YouTube");
|
||||
message.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://youtube.com/horsenuggets"));
|
||||
message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to visit HorseNuggets' " + ChatColor.RED + "YouTube" + ChatColor.RESET + "!")));
|
||||
message.append(", ");
|
||||
message.event((ClickEvent) null);
|
||||
message.event((HoverEvent) null);
|
||||
message.append(ChatColor.BLUE + "Discord");
|
||||
message.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://discord.gg/horsenuggets"));
|
||||
message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to visit HorseNuggets' " + ChatColor.BLUE + "Discord" + ChatColor.RESET + "!")));
|
||||
message.append("\n");
|
||||
message.event((ClickEvent) null);
|
||||
message.event((HoverEvent) null);
|
||||
message.append("\nPlugin Commands:\n");
|
||||
|
||||
commandHandler.getCommands().forEach((name, command) -> {
|
||||
if (!name.equalsIgnoreCase(pluginName)) {
|
||||
message.append(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "/" + name + ChatUtils.BULLET_FORMATTED + command.getDescription() + "\n");
|
||||
}
|
||||
});
|
||||
|
||||
message.append(ChatUtils.LINE);
|
||||
|
||||
this.rootInfo = message.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.nuggetmc.tplus.command.exception;
|
||||
|
||||
public class ArgCountException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.nuggetmc.tplus.command.exception;
|
||||
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
public class ArgParseException extends Exception {
|
||||
|
||||
private final Parameter parameter;
|
||||
|
||||
public ArgParseException(Parameter parameter) {
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
public Parameter getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.nuggetmc.tplus.command.exception;
|
||||
|
||||
public class NonPlayerException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.nuggetmc.tplus.command.nms;
|
||||
|
||||
public class TPCommand {
|
||||
// this class (as well as some others like "give", "effect", etc.) will listen into the PlayerCommandPreProcessEvent
|
||||
// and will perform the actions accordingly to the bots as well as the real players
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BotUtils {
|
||||
|
||||
public static final Set<Material> NO_FALL = new HashSet<>(Arrays.asList(
|
||||
Material.WATER,
|
||||
Material.LAVA,
|
||||
Material.TWISTING_VINES,
|
||||
Material.VINE
|
||||
));
|
||||
|
||||
public static UUID randomSteveUUID() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
if (uuid.hashCode() % 2 == 0) {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
return randomSteveUUID();
|
||||
}
|
||||
|
||||
public static boolean solidAt(Location loc) { // not perfect, still cuts corners of fences
|
||||
Block block = loc.getBlock();
|
||||
BoundingBox box = block.getBoundingBox();
|
||||
Vector position = loc.toVector();
|
||||
|
||||
double x = position.getX();
|
||||
double y = position.getY();
|
||||
double z = position.getZ();
|
||||
|
||||
double minX = box.getMinX();
|
||||
double minY = box.getMinY();
|
||||
double minZ = box.getMinZ();
|
||||
|
||||
double maxX = box.getMaxX();
|
||||
double maxY = box.getMaxY();
|
||||
double maxZ = box.getMaxZ();
|
||||
|
||||
return x > minX && x < maxX && y > minY && y < maxY && z > minZ && z < maxZ;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import net.nuggetmc.tplus.bot.Bot;
|
||||
import net.nuggetmc.tplus.bot.agent.Agent;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.tplus.bot.agent.legacyagent.ai.IntelligenceAgent;
|
||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.tplus.command.commands.AICommand;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.permissions.ServerOperator;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.beans.Statement;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Debugger {
|
||||
|
||||
private static final String PREFIX = ChatColor.YELLOW + "[DEBUG] " + ChatColor.RESET;
|
||||
|
||||
private final CommandSender sender;
|
||||
|
||||
public Debugger(CommandSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public static void log(Object... objects) {
|
||||
String[] values = formStringArray(objects);
|
||||
String message = PREFIX + String.join(" ", values);
|
||||
|
||||
Bukkit.getConsoleSender().sendMessage(message);
|
||||
Bukkit.getOnlinePlayers().stream().filter(ServerOperator::isOp).forEach(p -> p.sendMessage(message));
|
||||
}
|
||||
|
||||
private static String[] formStringArray(Object[] objects) {
|
||||
return Arrays.stream(objects).map(String::valueOf).toArray(String[]::new);
|
||||
}
|
||||
|
||||
private void print(Object... objects) {
|
||||
sender.sendMessage(PREFIX + String.join(" ", formStringArray(objects)));
|
||||
}
|
||||
|
||||
public void execute(String cmd) {
|
||||
try {
|
||||
int[] pts = {cmd.indexOf('('), cmd.indexOf(')')};
|
||||
if (pts[0] == -1 || pts[1] == -1) throw new IllegalArgumentException();
|
||||
|
||||
String name = cmd.substring(0, pts[0]);
|
||||
String content = cmd.substring(pts[0] + 1, pts[1]);
|
||||
|
||||
Object[] args = content.isEmpty() ? null : buildObjects(content);
|
||||
|
||||
Statement statement = new Statement(this, name, args);
|
||||
print("Running the expression \"" + ChatColor.AQUA + cmd + ChatColor.RESET + "\"...");
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
print("Error: the expression \"" + ChatColor.AQUA + cmd + ChatColor.RESET + "\" failed to execute.");
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] buildObjects(String content) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
|
||||
if (!content.isEmpty()) {
|
||||
String[] values = content.split(",");
|
||||
|
||||
for (String str : values) {
|
||||
String value = str.startsWith(" ") ? str.substring(1) : str;
|
||||
Object obj = value;
|
||||
|
||||
try {
|
||||
obj = Double.parseDouble(value);
|
||||
} catch (NumberFormatException ignored) { }
|
||||
|
||||
try {
|
||||
obj = Integer.parseInt(value);
|
||||
} catch (NumberFormatException ignored) { }
|
||||
|
||||
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
|
||||
obj = Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
list.add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
/*
|
||||
* DEBUGGER METHODS
|
||||
*/
|
||||
|
||||
public void mobWaves(int n) {
|
||||
World world = Bukkit.getWorld("world");
|
||||
|
||||
if (world == null) {
|
||||
print("world is null");
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Location> locs = new HashSet<>();
|
||||
locs.add(new Location(world, 128, 36, -142));
|
||||
locs.add(new Location(world, 236, 44, -179));
|
||||
locs.add(new Location(world, 310, 36, -126));
|
||||
locs.add(new Location(world, 154, 35, -101));
|
||||
locs.add(new Location(world, 202, 46, -46));
|
||||
locs.add(new Location(world, 274, 52, -44));
|
||||
locs.add(new Location(world, 297, 38, -97));
|
||||
locs.add(new Location(world, 271, 43, -173));
|
||||
locs.add(new Location(world, 216, 50, -187));
|
||||
locs.add(new Location(world, 181, 35, -150));
|
||||
|
||||
if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
|
||||
Bukkit.dispatchCommand(player, "team join a @a");
|
||||
|
||||
Bukkit.broadcastMessage(ChatColor.YELLOW + "Starting wave " + ChatColor.RED + n + ChatColor.YELLOW + "...");
|
||||
|
||||
Bukkit.broadcastMessage(ChatColor.YELLOW + "Unleashing the Super Zombies...");
|
||||
|
||||
String[] skin = MojangAPI.getSkin("Lozimac");
|
||||
|
||||
String name = "*";
|
||||
|
||||
switch (n) {
|
||||
case 1: {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
|
||||
bot.setDefaultItem(new ItemStack(Material.WOODEN_AXE));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
|
||||
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
|
||||
bot.setShield(true);
|
||||
bot.setDefaultItem(new ItemStack(Material.STONE_AXE));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: {
|
||||
for (int i = 0; i < 40; i++) {
|
||||
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
|
||||
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
|
||||
bot.setShield(true);
|
||||
bot.setDefaultItem(new ItemStack(Material.IRON_AXE));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
|
||||
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
|
||||
bot.setShield(true);
|
||||
bot.setDefaultItem(new ItemStack(Material.DIAMOND_AXE));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.broadcastMessage(ChatColor.YELLOW + "The Super Zombies have been unleashed.");
|
||||
|
||||
hideNametags();
|
||||
}
|
||||
}
|
||||
|
||||
public void lol(String name, String skinName) {
|
||||
String[] skin = MojangAPI.getSkin(skinName);
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Bot.createBot(player.getLocation(), name, skin);
|
||||
}
|
||||
}
|
||||
|
||||
public void colorTest() {
|
||||
Player player = (Player) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
String[] skin = MojangAPI.getSkin("Kubepig");
|
||||
|
||||
TerminatorPlus plugin = TerminatorPlus.getInstance();
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
for (int n = 1; n <= 40; n++) {
|
||||
int wait = (int) (Math.pow(1.05, 130 - n) + 100);
|
||||
|
||||
try {
|
||||
Thread.sleep(wait);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> Bot.createBot(PlayerUtils.findBottom(loc.clone().add(Math.random() * 20 - 10, 0, Math.random() * 20 - 10)), ChatColor.GREEN + "-$26.95", skin));
|
||||
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void tpall() {
|
||||
Player player = (Player) sender;
|
||||
TerminatorPlus.getInstance().getManager().fetch().stream().filter(LivingEntity::isAlive).forEach(bot -> bot.getBukkitEntity().teleport(player));
|
||||
}
|
||||
|
||||
public void viewsession() {
|
||||
IntelligenceAgent session = ((AICommand) TerminatorPlus.getInstance().getHandler().getCommand("ai")).getSession();
|
||||
Bukkit.getOnlinePlayers().stream().filter(ServerOperator::isOp).forEach(session::addUser);
|
||||
}
|
||||
|
||||
public void block() {
|
||||
TerminatorPlus.getInstance().getManager().fetch().forEach(bot -> bot.block(10, 10));
|
||||
}
|
||||
|
||||
public void offsets(boolean b) {
|
||||
Agent agent = TerminatorPlus.getInstance().getManager().getAgent();
|
||||
if (!(agent instanceof LegacyAgent)) {
|
||||
print("This method currently only supports " + ChatColor.AQUA + "LegacyAgent" + ChatColor.RESET + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
LegacyAgent legacyAgent = (LegacyAgent) agent;
|
||||
legacyAgent.offsets = b;
|
||||
|
||||
print("Bot target offsets are now "
|
||||
+ (legacyAgent.offsets ? ChatColor.GREEN + "ENABLED" : ChatColor.RED + "DISABLED")
|
||||
+ ChatColor.RESET + ".");
|
||||
}
|
||||
|
||||
public void confuse(int n) {
|
||||
if (!(sender instanceof Player)) return;
|
||||
|
||||
Player player = (Player) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
double f = n < 100 ? .004 * n : .4;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Player target = Bukkit.getOnlinePlayers().stream().skip((int) (Bukkit.getOnlinePlayers().size() * Math.random())).findFirst().orElse(null);
|
||||
String name = target == null ? "Steve" : target.getName();
|
||||
Bot bot = Bot.createBot(loc, name);
|
||||
bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f));
|
||||
bot.faceLocation(bot.getLocation().add(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5));
|
||||
}
|
||||
|
||||
player.getWorld().spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
}
|
||||
|
||||
public void dreamsmp() {
|
||||
spawnBots(Arrays.asList(
|
||||
"Dream", "GeorgeNotFound", "Callahan", "Sapnap", "awesamdude", "Ponk", "BadBoyHalo", "TommyInnit", "Tubbo_", "ItsFundy", "Punz",
|
||||
"Purpled", "WilburSoot", "Jschlatt", "Skeppy", "The_Eret", "JackManifoldTV", "Nihachu", "Quackity", "KarlJacobs", "HBomb94",
|
||||
"Technoblade", "Antfrost", "Ph1LzA", "ConnorEatsPants", "CaptainPuffy", "Vikkstar123", "LazarCodeLazar", "Ranboo", "FoolishG",
|
||||
"hannahxxrose", "Slimecicle", "Michaelmcchill"
|
||||
));
|
||||
}
|
||||
|
||||
private void spawnBots(List<String> players) {
|
||||
if (!(sender instanceof Player)) return;
|
||||
|
||||
print("Processing request asynchronously...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(TerminatorPlus.getInstance(), () -> {
|
||||
try {
|
||||
print("Fetching skin data from the Mojang API for:");
|
||||
|
||||
Player player = (Player) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
Collections.shuffle(players);
|
||||
|
||||
Map<String, String[]> skinCache = new HashMap<>();
|
||||
|
||||
int size = players.size();
|
||||
int i = 1;
|
||||
|
||||
for (String name : players) {
|
||||
print(name, ChatColor.GRAY + "(" + ChatColor.GREEN + i + ChatColor.GRAY + "/" + size + ")");
|
||||
String[] skin = MojangAPI.getSkin(name);
|
||||
skinCache.put(name, skin);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
print("Creating bots...");
|
||||
|
||||
double f = .004 * players.size();
|
||||
|
||||
Bukkit.getScheduler().runTask(TerminatorPlus.getInstance(), () -> {
|
||||
skinCache.forEach((name, skin) -> {
|
||||
Bot bot = Bot.createBot(loc, name, skin);
|
||||
bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f));
|
||||
bot.faceLocation(bot.getLocation().add(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5));
|
||||
});
|
||||
|
||||
player.getWorld().spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
|
||||
print("Done.");
|
||||
});
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void item() {
|
||||
TerminatorPlus.getInstance().getManager().fetch().forEach(b -> b.setDefaultItem(new ItemStack(Material.IRON_SWORD)));
|
||||
}
|
||||
|
||||
public void j(boolean b) {
|
||||
TerminatorPlus.getInstance().getManager().joinMessages = b;
|
||||
}
|
||||
|
||||
public void epic(int n) {
|
||||
if (!(sender instanceof Player)) return;
|
||||
|
||||
print("Fetching names asynchronously...");
|
||||
|
||||
List<String> players = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String name = PlayerUtils.randomName();
|
||||
players.add(name);
|
||||
print(name);
|
||||
}
|
||||
|
||||
spawnBots(players);
|
||||
}
|
||||
|
||||
public void tp() {
|
||||
Bot bot = MathUtils.getRandomSetElement(TerminatorPlus.getInstance().getManager().fetch().stream().filter(LivingEntity::isAlive).collect(Collectors.toSet()));
|
||||
|
||||
if (bot == null) {
|
||||
print("Failed to locate a bot.");
|
||||
return;
|
||||
}
|
||||
|
||||
print("Located bot", (ChatColor.GREEN.toString() + bot.getName() + ChatColor.RESET.toString() + "."));
|
||||
|
||||
if (sender instanceof Player) {
|
||||
print("Teleporting...");
|
||||
((Player) sender).teleport(bot.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
public void setTarget(int n) {
|
||||
print("This has been established as a feature as \"" + ChatColor.AQUA + "/bot settings setgoal" + ChatColor.RESET + "\"!");
|
||||
}
|
||||
|
||||
public void fire(boolean b) {
|
||||
TerminatorPlus.getInstance().getManager().fetch().forEach(bot -> bot.setOnFirePackets(b));
|
||||
}
|
||||
|
||||
public void trackYVel() {
|
||||
if (!(sender instanceof Player)) return;
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(TerminatorPlus.getInstance(), () -> {
|
||||
print(player.getVelocity().getY());
|
||||
}, 0, 1);
|
||||
}
|
||||
|
||||
public void hideNametags() { // this works for some reason
|
||||
Set<Bot> bots = TerminatorPlus.getInstance().getManager().fetch();
|
||||
|
||||
for (Bot bot : bots) {
|
||||
Location loc = bot.getLocation();
|
||||
World world = loc.getWorld();
|
||||
|
||||
if (world == null) continue;
|
||||
|
||||
loc.setX(loc.getBlockX());
|
||||
loc.setY(loc.getBlockY());
|
||||
loc.setZ(loc.getBlockZ());
|
||||
|
||||
loc.add(0.5, 0.5, 0.5);
|
||||
|
||||
ArmorStand seat = (ArmorStand) world.spawnEntity(loc, EntityType.ARMOR_STAND);
|
||||
seat.setVisible(false);
|
||||
seat.setSmall(true);
|
||||
|
||||
bot.getBukkitEntity().setPassenger(seat);
|
||||
}
|
||||
}
|
||||
|
||||
public void sit() {
|
||||
Set<Bot> bots = TerminatorPlus.getInstance().getManager().fetch();
|
||||
|
||||
for (Bot bot : bots) {
|
||||
Location loc = bot.getLocation();
|
||||
World world = loc.getWorld();
|
||||
|
||||
if (world == null) continue;
|
||||
|
||||
loc.setX(loc.getBlockX());
|
||||
loc.setY(loc.getBlockY());
|
||||
loc.setZ(loc.getBlockZ());
|
||||
|
||||
loc.add(0.5, -1.5, 0.5);
|
||||
|
||||
ArmorStand seat = (ArmorStand) world.spawnEntity(loc, EntityType.ARMOR_STAND);
|
||||
seat.setVisible(false);
|
||||
seat.setGravity(false);
|
||||
seat.setSmall(true);
|
||||
|
||||
seat.addPassenger(bot.getBukkitEntity());
|
||||
}
|
||||
}
|
||||
|
||||
public void look() {
|
||||
if (!(sender instanceof Player)) {
|
||||
print("Unspecified player.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
for (Bot bot : TerminatorPlus.getInstance().getManager().fetch()) {
|
||||
bot.faceLocation(player.getEyeLocation());
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleAgent() {
|
||||
Agent agent = TerminatorPlus.getInstance().getManager().getAgent();
|
||||
|
||||
boolean b = agent.isEnabled();
|
||||
agent.setEnabled(!b);
|
||||
|
||||
print("The Bot Agent is now "
|
||||
+ (b ? ChatColor.RED + "DISABLED" : ChatColor.GREEN + "ENABLED")
|
||||
+ ChatColor.RESET + ".");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class ItemUtils {
|
||||
|
||||
public static double getLegacyAttackDamage(ItemStack item) {
|
||||
switch (item.getType()) {
|
||||
default:
|
||||
return 0.25;
|
||||
|
||||
case WOODEN_SHOVEL:
|
||||
case GOLDEN_SHOVEL:
|
||||
case WOODEN_HOE:
|
||||
case GOLDEN_HOE:
|
||||
case STONE_HOE:
|
||||
case IRON_HOE:
|
||||
case DIAMOND_HOE:
|
||||
case NETHERITE_HOE:
|
||||
return 1;
|
||||
|
||||
case WOODEN_PICKAXE:
|
||||
case GOLDEN_PICKAXE:
|
||||
case STONE_SHOVEL:
|
||||
return 2;
|
||||
|
||||
case WOODEN_AXE:
|
||||
case GOLDEN_AXE:
|
||||
case STONE_PICKAXE:
|
||||
case IRON_SHOVEL:
|
||||
return 3;
|
||||
|
||||
case WOODEN_SWORD:
|
||||
case GOLDEN_SWORD:
|
||||
case STONE_AXE:
|
||||
case IRON_PICKAXE:
|
||||
case DIAMOND_SHOVEL:
|
||||
return 4;
|
||||
|
||||
case STONE_SWORD:
|
||||
case IRON_AXE:
|
||||
case DIAMOND_PICKAXE:
|
||||
case NETHERITE_SHOVEL:
|
||||
return 5;
|
||||
|
||||
case IRON_SWORD:
|
||||
case DIAMOND_AXE:
|
||||
case NETHERITE_PICKAXE:
|
||||
return 6;
|
||||
|
||||
case DIAMOND_SWORD:
|
||||
case NETHERITE_AXE:
|
||||
return 7;
|
||||
|
||||
case NETHERITE_SWORD:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MojangAPI {
|
||||
|
||||
private static final boolean CACHE_ENABLED = false;
|
||||
|
||||
private static final Map<String, String[]> CACHE = new HashMap<>();
|
||||
|
||||
public static String[] getSkin(String name) {
|
||||
if (CACHE_ENABLED && CACHE.containsKey(name)) {
|
||||
return CACHE.get(name);
|
||||
}
|
||||
|
||||
String[] values = pullFromAPI(name);
|
||||
CACHE.put(name, values);
|
||||
return values;
|
||||
}
|
||||
|
||||
// CATCHING NULL ILLEGALSTATEEXCEPTION BAD!!!! eventually fix from the getAsJsonObject thingy
|
||||
public static String[] pullFromAPI(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 (IOException | IllegalStateException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
import net.nuggetmc.tplus.TerminatorPlus;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class PlayerUtils {
|
||||
|
||||
public static boolean isInvincible(GameMode mode) {
|
||||
return mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE && mode != null;
|
||||
}
|
||||
|
||||
private static final Set<String> USERNAME_CACHE = new HashSet<>();
|
||||
|
||||
public static String randomName() {
|
||||
if (USERNAME_CACHE.isEmpty()) {
|
||||
fillUsernameCache();
|
||||
}
|
||||
|
||||
return MathUtils.getRandomSetElement(USERNAME_CACHE);
|
||||
}
|
||||
|
||||
public static void fillUsernameCache() {
|
||||
String file = TerminatorPlus.getInstance().getServer().getWorldContainer().getAbsolutePath();
|
||||
file = file.substring(0, file.length() - 1) + "usercache.json";
|
||||
|
||||
JSONParser parser = new JSONParser();
|
||||
|
||||
try {
|
||||
JSONArray array = (JSONArray) parser.parse(new FileReader(file));
|
||||
|
||||
for (Object obj : array) {
|
||||
JSONObject jsonOBJ = (JSONObject) obj;
|
||||
String username = (String) jsonOBJ.get("name");
|
||||
|
||||
USERNAME_CACHE.add(username);
|
||||
}
|
||||
}
|
||||
|
||||
catch (IOException | ParseException e) {
|
||||
Debugger.log("Failed to fetch from the usercache.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Location findAbove(Location loc, int amount) {
|
||||
boolean check = false;
|
||||
|
||||
for (int i = 0; i <= amount; i++) {
|
||||
if (loc.clone().add(0, i, 0).getBlock().getType().isSolid()) {
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
return loc;
|
||||
} else {
|
||||
return loc.clone().add(0, amount, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static Location findBottom(Location loc) {
|
||||
loc.setY(loc.getBlockY());
|
||||
|
||||
for (int i = 0; i < 255; i++) {
|
||||
Location check = loc.clone().add(0, -i, 0);
|
||||
|
||||
if (check.getY() <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (check.getBlock().getType().isSolid()) {
|
||||
return check.add(0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.nuggetmc.tplus.utils;
|
||||
|
||||
public class Singularity {
|
||||
|
||||
private Object value;
|
||||
|
||||
public Singularity(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Singularity() {
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return value != null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user