first commit-ish, forked and updated for 1.21.11
Some checks failed
Compile / gradle (ubuntu-latest) (push) Has been cancelled

This commit is contained in:
Justus Wolff
2026-03-02 15:40:11 +01:00
parent ff9220654b
commit 53b4f6a04a
491 changed files with 98649 additions and 86 deletions

View File

@@ -0,0 +1,2 @@
Command: C:\Program Files\Java\jdk-21.0.10\bin\java.exe -Xmx1G -classpath C:\Users\JUFS-STL-SECONDARY\.gradle\caches\modules-2\files-2.1\net.fabricmc\tiny-remapper\0.12.0\bfb93e1bfb66d47272ccd37ce894dcfc20ba0b6\tiny-remapper-0.12.0-fat.jar net.fabricmc.tinyremapper.Main C:\Users\JUFS-STL-SECONDARY\Desktop\terminatorplus\TerminatorPlus-Plugin\build\libs\TerminatorPlus-Plugin-4.5.1-BETA.jar C:\Users\JUFS-STL-SECONDARY\Desktop\terminatorplus\TerminatorPlus-Plugin\build\libs\TerminatorPlus-Plugin-4.5.1-BETA-reobf.jar C:\Users\JUFS-STL-SECONDARY\Desktop\terminatorplus\TerminatorPlus-Plugin\.gradle\caches\paperweight\taskCache\reobfMappings.tiny mojang spigot C:\Users\JUFS-STL-SECONDARY\Desktop\terminatorplus\TerminatorPlus-Plugin\.gradle\caches\paperweight\taskCache\mappedServerJar.jar --threads=1
[INFO] Finished after 1642.84 ms.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
plugins {
`java-library`
id("io.papermc.paperweight.userdev") version "1.7.5"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
id("net.nuggetmc.java-conventions")
}
@@ -12,11 +12,26 @@ java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
repositories {
maven {
url = uri("https://repo.citizensnpcs.co/")
}
maven {
name = "alessiodpRepo"
url = uri("https://repo.alessiodp.com/releases")
}
}
dependencies {
paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT")
implementation("net.byteflux:libby-bukkit:1.3.1")
//add the TerminatorPlus-API module
implementation(project(":TerminatorPlus-API"))
// Citizens NPC API
compileOnly("net.citizensnpcs:citizens-main:2.0.41-SNAPSHOT")
}
tasks {

View File

@@ -7,12 +7,13 @@ import net.nuggetmc.tplus.command.CommandHandler;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import net.nuggetmc.tplus.bot.trait.*;
import java.util.Arrays;
public class TerminatorPlus extends JavaPlugin {
public static final String REQUIRED_VERSION = "1.21.1";
public static final String REQUIRED_VERSION = "1.21.11";
private static TerminatorPlus instance;
private static String version;
@@ -63,6 +64,9 @@ public class TerminatorPlus extends JavaPlugin {
TerminatorPlusAPI.setBotManager(manager);
TerminatorPlusAPI.setInternalBridge(new InternalBridgeImpl());
net.citizensnpcs.api.CitizensAPI.getTraitFactory()
.registerTrait(net.citizensnpcs.api.trait.TraitInfo.create(BotBehaviorTrait.class));
// Register event listeners
this.registerEvents(manager);

View File

@@ -53,6 +53,11 @@ import org.bukkit.util.Vector;
import java.util.*;
/**
* @deprecated Use {@link CitizensNPC} instead. This class uses NMS and is being phased out
* in favor of the Citizens plugin API for better compatibility and maintenance.
*/
@Deprecated(since = "2.0.0", forRemoval = true)
public class Bot extends ServerPlayer implements Terminator {
private final TerminatorPlus plugin;
@@ -88,10 +93,10 @@ public class Bot extends ServerPlayer implements Terminator {
this.noFallTicks = 60;
this.removeOnDeath = true;
this.offset = MathUtils.circleOffset(3);
if (addToPlayerList) {
/*if (addToPlayerList) {
minecraftServer.getPlayerList().getPlayers().add(this);
inPlayerList = true;
}
}*/
//this.entityData.set(new EntityDataAccessor<>(16, EntityDataSerializers.BYTE), (byte) 0xFF);
}
@@ -620,8 +625,8 @@ public class Bot extends ServerPlayer implements Terminator {
scheduler.runTask(plugin, () -> this.remove(RemovalReason.DISCARDED));
}
this.removeVisually();
if (inPlayerList)
this.server.getPlayerList().getPlayers().remove(this);
/*if (inPlayerList)
this.server.getPlayerList().getPlayers().remove(this);*/ // not needed thanks for citizens
}
private void removeTab() {
@@ -692,7 +697,7 @@ public class Bot extends ServerPlayer implements Terminator {
}
}
@Override
/*@Override
public boolean hurt(DamageSource damagesource, float f) {
Entity attacker = damagesource.getEntity();
@@ -735,7 +740,7 @@ public class Bot extends ServerPlayer implements Terminator {
}
return damaged;
}
}*/
private void kb(Location loc1, Location loc2, Entity attacker) {
Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3);
@@ -887,7 +892,7 @@ public class Bot extends ServerPlayer implements Terminator {
@Override
public void doTick() {
detectEquipmentUpdatesPublic();
//detectEquipmentUpdatesPublic();
baseTick();
}

View File

@@ -1,7 +1,6 @@
package net.nuggetmc.tplus.bot;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.nuggetmc.tplus.TerminatorPlus;
import net.nuggetmc.tplus.api.BotManager;
import net.nuggetmc.tplus.api.Terminator;
@@ -9,10 +8,8 @@ import net.nuggetmc.tplus.api.agent.Agent;
import net.nuggetmc.tplus.api.agent.legacyagent.LegacyAgent;
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
import net.nuggetmc.tplus.api.event.BotDeathEvent;
import net.nuggetmc.tplus.api.utils.MojangAPI;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@@ -20,6 +17,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
@@ -38,6 +36,7 @@ public class BotManagerImpl implements BotManager, Listener {
public boolean joinMessages = false;
private boolean mobTarget = false;
private boolean addPlayerList = false;
private boolean neuralNetworksEnabled = false;
public BotManagerImpl() {
this.agent = new LegacyAgent(this, TerminatorPlus.getInstance());
@@ -83,16 +82,12 @@ public class BotManagerImpl implements BotManager, Listener {
@Override
public List<String> fetchNames() {
//return bots.stream().map(Bot::getBotName).map(component -> component.getString()).collect(Collectors.toList());
return bots.stream().map(terminator -> {
if (terminator instanceof Bot bot) return bot.getName().getString();
else return terminator.getBotName();
}).collect(Collectors.toList());
return bots.stream().map(Terminator::getBotName).collect(Collectors.toList());
}
@Override
public Terminator createBot(Location loc, String name, String skin, String sig) {
return Bot.createBot(loc, name, new String[]{skin, sig});
public Terminator createBot(Location loc, String name, String skin) {
return CitizensNPC.createNPC(loc, name, skin);
}
@Override
@@ -122,16 +117,16 @@ public class BotManagerImpl implements BotManager, Listener {
skinName = skinName == null ? name : skinName;
if (location != null) {
createBots(location, name, MojangAPI.getSkin(skinName), n, network);
createBots(location, name, skinName, n, network);
} else {
if (sender instanceof Player player)
createBots(player.getLocation(), name, MojangAPI.getSkin(skinName), n, network);
createBots(player.getLocation(), name, skinName, n, network);
else {
Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
if (sender != null)
// sender.sendMessage(ChatColor.RED + "No location specified, defaulting to " + l + ".");
sender.sendRichMessage("<red>No location specified, defaulting to " + l.getX() + ", " + l.getY() + ", " + l.getZ() + ".");
createBots(l, name, MojangAPI.getSkin(skinName), n, network);
createBots(l, name, skinName, n, network);
}
}
@@ -141,7 +136,7 @@ public class BotManagerImpl implements BotManager, Listener {
}
@Override
public Set<Terminator> createBots(Location loc, String name, String[] skin, int n, NeuralNetwork network) {
public Set<Terminator> createBots(Location loc, String name, String skin, int n, NeuralNetwork network) {
List<NeuralNetwork> networks = new ArrayList<>();
for (int i = 0; i < n; i++) {
@@ -152,7 +147,7 @@ public class BotManagerImpl implements BotManager, Listener {
}
@Override
public Set<Terminator> createBots(Location loc, String name, String[] skin, List<NeuralNetwork> networks) {
public Set<Terminator> createBots(Location loc, String name, String skin, List<NeuralNetwork> networks) {
Set<Terminator> bots = new HashSet<>();
World world = loc.getWorld();
@@ -162,16 +157,22 @@ public class BotManagerImpl implements BotManager, Listener {
double f = n < 100 ? .004 * n : .4;
for (NeuralNetwork network : networks) {
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin);
CitizensNPC bot = CitizensNPC.createNPC(loc, name.replace("%", String.valueOf(i)), skin);
if (network != null) {
bot.setNeuralNetwork(network == NeuralNetwork.RANDOM ? NeuralNetwork.generateRandomNetwork() : network);
// Determine which network to use based on the provided network and the global setting
NeuralNetwork botNetwork = network;
if (botNetwork == null && neuralNetworksEnabled) {
botNetwork = NeuralNetwork.RANDOM;
}
if (botNetwork != null) {
bot.setNeuralNetwork(botNetwork == NeuralNetwork.RANDOM ? NeuralNetwork.generateRandomNetwork() : botNetwork);
bot.setShield(true);
bot.setDefaultItem(new ItemStack(Material.WOODEN_AXE));
//bot.setRemoveOnDeath(false);
}
if (network != null) {
if (botNetwork != null) {
bot.setVelocity(randomVelocity());
} else if (i > 1) {
bot.setVelocity(randomVelocity().multiply(f));
@@ -251,10 +252,18 @@ public class BotManagerImpl implements BotManager, Listener {
this.addPlayerList = addPlayerList;
}
public boolean isNeuralNetworksEnabled() {
return neuralNetworksEnabled;
}
public void setNeuralNetworksEnabled(boolean neuralNetworksEnabled) {
this.neuralNetworksEnabled = neuralNetworksEnabled;
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
ServerGamePacketListenerImpl connection = ((CraftPlayer) event.getPlayer()).getHandle().connection;
bots.forEach(bot -> bot.renderBot(connection, true));
// Citizens handles rendering automatically when player joins
// No need for manual packet sending
}
@EventHandler
@@ -270,9 +279,22 @@ public class BotManagerImpl implements BotManager, Listener {
public void onMobTarget(EntityTargetLivingEntityEvent event) {
if (mobTarget || event.getTarget() == null)
return;
Bot bot = (Bot) getBot(event.getTarget().getUniqueId());
Terminator bot = getBot(event.getTarget().getUniqueId());
if (bot != null) {
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
Entity killer = player.getKiller();
if (killer != null) {
Terminator bot = getBot(killer.getEntityId());
if (bot != null) {
bot.incrementKills();
}
}
}
}

View File

@@ -0,0 +1,747 @@
package net.nuggetmc.tplus.bot;
import com.mojang.authlib.GameProfile;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.trait.SkinTrait;
import net.nuggetmc.tplus.TerminatorPlus;
import net.nuggetmc.tplus.api.Terminator;
import net.nuggetmc.tplus.api.agent.Agent;
import net.nuggetmc.tplus.api.agent.legacyagent.LegacyMats;
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
import net.nuggetmc.tplus.api.event.BotDamageByPlayerEvent;
import net.nuggetmc.tplus.api.event.BotFallDamageEvent;
import net.nuggetmc.tplus.api.event.BotKilledByPlayerEvent;
import net.nuggetmc.tplus.api.utils.*;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Damageable;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import java.util.*;
public class CitizensNPC implements Terminator {
private final NPC npc;
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 groundTicks;
private byte jumpTicks;
private byte noFallTicks;
private List<Block> standingOn = new ArrayList<>();
private UUID targetPlayer = null;
private CitizensNPC(NPC npc, Agent agent) {
this.npc = npc;
this.plugin = TerminatorPlus.getInstance();
this.scheduler = Bukkit.getScheduler();
this.agent = agent;
this.defaultItem = new ItemStack(Material.AIR);
this.velocity = new Vector(0, 0, 0);
this.oldVelocity = velocity.clone();
this.noFallTicks = 60;
this.removeOnDeath = true;
this.offset = MathUtils.circleOffset(3);
}
public static CitizensNPC createNPC(Location loc, String name, String skin) {
// Create the NPC with Citizens API
NPC npc = CitizensAPI.getNPCRegistry().createNPC(org.bukkit.entity.EntityType.PLAYER, ChatUtils.trim16(name));
// Set location
npc.spawn(loc);
// Set attributes
npc.data().setPersistent(NPC.Metadata.COLLIDABLE, true);
npc.data().setPersistent(NPC.Metadata.DAMAGE_OTHERS, true);
npc.data().setPersistent(NPC.Metadata.DEFAULT_PROTECTED, false);
npc.data().setPersistent(NPC.Metadata.DROPS_ITEMS, true);
npc.data().setPersistent(NPC.Metadata.FLUID_PUSHABLE, true);
npc.data().setPersistent(NPC.Metadata.KEEP_CHUNK_LOADED, true);
npc.data().setPersistent(NPC.Metadata.KNOCKBACK, true);
npc.data().setPersistent(NPC.Metadata.PICKUP_ITEMS, true);
npc.data().setPersistent(NPC.Metadata.PATHFINDER_OPEN_DOORS, true);
// Create wrapper first so we can attach the trait
CitizensNPC citizensNPC = new CitizensNPC(npc, TerminatorPlus.getInstance().getManager().getAgent());
// Add the behavior trait for ticking (if the trait system is available)
try {
npc.addTrait(net.nuggetmc.tplus.bot.trait.BotBehaviorTrait.class);
net.nuggetmc.tplus.bot.trait.BotBehaviorTrait trait = npc.getTrait(net.nuggetmc.tplus.bot.trait.BotBehaviorTrait.class);
if (trait != null) {
trait.setBot(citizensNPC);
}
} catch (Exception e) {
// Trait might not be registered, continue anyway
}
// Apply skin if provided
try {
SkinTrait skinTrait = npc.getTrait(SkinTrait.class);
if (skinTrait != null) {
skinTrait.setSkinName(skin, false);
/*if (skin != null && skin.length >= 2) {
skinTrait.setSkinPersistent(name, skin[0], skin[1]);
} else {
// Try to fetch skin from Mojang API
String[] fetchedSkin = MojangAPI.getSkin(name);
if (fetchedSkin != null && fetchedSkin.length >= 2) {
skinTrait.setSkinPersistent(name, fetchedSkin[0], fetchedSkin[1]);
}
}*/
}
} catch (Exception e) {
Bukkit.getServer().getLogger().warning("Failed to get skin: "+e.getMessage()+Arrays.toString(e.getStackTrace()));
}
// Register with bot manager
TerminatorPlus.getInstance().getManager().add(citizensNPC);
return citizensNPC;
}
public static CitizensNPC createNPC(Location loc, String name) {
return createNPC(loc, name, name);
}
@Override
public String getBotName() {
return npc.getName();
}
@Override
public int getEntityId() {
return npc.getEntity() != null ? npc.getEntity().getEntityId() : -1;
}
@Override
public GameProfile getGameProfile() {
// Citizens doesn't directly expose GameProfile, return null
// This would be used for NMS operations which we're avoiding
return null;
}
@Override
public LivingEntity getBukkitEntity() {
return (LivingEntity) npc.getEntity();
}
@Override
public NeuralNetwork getNeuralNetwork() {
return network;
}
@Override
public void setNeuralNetwork(NeuralNetwork network) {
this.network = network;
}
@Override
public boolean hasNeuralNetwork() {
return network != null;
}
@Override
public Location getLocation() {
return npc.getEntity() != null ? npc.getEntity().getLocation() : null;
}
@Override
public BoundingBox getBotBoundingBox() {
if (npc.getEntity() instanceof LivingEntity le) {
return le.getBoundingBox();
}
return null;
}
@Override
public boolean isBotAlive() {
LivingEntity entity = getBukkitEntity();
return entity != null && entity.isDead() == false;
}
@Override
public float getBotHealth() {
LivingEntity entity = getBukkitEntity();
return entity != null ? (float) entity.getHealth() : 0;
}
@Override
public float getBotMaxHealth() {
LivingEntity entity = getBukkitEntity();
return entity != null ? (float) entity.getMaxHealth() : 20;
}
@Override
public boolean isBotOnFire() {
LivingEntity entity = getBukkitEntity();
return entity != null && entity.getFireTicks() > 0;
}
@Override
public boolean isFalling() {
return velocity.getY() < -0.8;
}
@Override
public boolean isBotBlocking() {
return blocking;
}
@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;
}
private void stopBlocking(int cooldown) {
this.blocking = false;
scheduler.runTaskLater(plugin, () -> this.blockUse = false, cooldown);
}
@Override
public boolean isBotInWater() {
Location loc = getLocation();
if (loc == null) return false;
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 boolean isBotOnGround() {
return groundTicks != 0;
}
@Override
public List<Block> getStandingOn() {
return standingOn;
}
@Override
public void setBotPitch(float pitch) {
if (npc.getEntity() instanceof LivingEntity le) {
le.setRotation(le.getYaw(), pitch);
}
}
@Override
public void jump(Vector velocity) {
if (jumpTicks == 0 && groundTicks > 1) {
jumpTicks = 4;
this.velocity = velocity;
}
}
@Override
public void jump() {
jump(new Vector(0, 0.42, 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 look(BlockFace face) {
look(face.getDirection(), face == BlockFace.DOWN || face == BlockFace.UP);
}
private void look(Vector dir, boolean keepYaw) {
float yaw, pitch;
LivingEntity entity = getBukkitEntity();
if (entity == null) return;
if (keepYaw) {
yaw = entity.getYaw();
pitch = MathUtils.fetchPitch(dir);
} else {
float[] vals = MathUtils.fetchYawPitch(dir);
yaw = vals[0];
pitch = vals[1];
}
entity.setRotation(yaw, pitch);
}
@Override
public void faceLocation(Location location) {
LivingEntity entity = getBukkitEntity();
if (entity != null) {
look(location.toVector().subtract(entity.getLocation().toVector()), false);
}
}
@Override
public void attack(Entity target) {
faceLocation(target.getLocation());
punch();
double damage = ItemUtils.getLegacyAttackDamage(defaultItem);
if (target instanceof Damageable) {
((Damageable) target).damage(damage, getBukkitEntity());
}
}
@Override
public void attemptBlockPlace(Location loc, Material type, boolean down) {
if (down) {
look(BlockFace.DOWN);
} else {
faceLocation(loc);
}
setItem(new ItemStack(Material.COBBLESTONE));
punch();
Block block = loc.getBlock();
World world = loc.getWorld();
if (!LegacyMats.isSolid(block.getType())) {
block.setType(type);
if (world != null) world.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
}
}
@Override
public void punch() {
LivingEntity entity = getBukkitEntity();
if (entity instanceof Player player) {
// Swing the arm animation
player.swingMainHand();
}
}
@Override
public void swim() {
LivingEntity entity = getBukkitEntity();
if (entity != null) {
entity.setSwimming(true);
}
}
@Override
public void sneak() {
LivingEntity entity = getBukkitEntity();
if (entity instanceof Player player) {
player.setSneaking(true);
}
}
@Override
public void stand() {
LivingEntity entity = getBukkitEntity();
if (entity instanceof Player player) {
player.setSneaking(false);
player.setSwimming(false);
}
}
@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() {
if (npc.isSpawned()) {
npc.despawn();
}
}
@Override
public void removeBot() {
removeVisually();
try {
CitizensAPI.getNPCRegistry().deregister(npc);
} catch (Exception e) {
// NPC might already be deregistered
}
TerminatorPlus.getInstance().getManager().remove(this);
}
@Override
public int getKills() {
return kills;
}
@Override
public void incrementKills() {
kills++;
}
@Override
public void setItem(ItemStack item) {
setItem(item, EquipmentSlot.HAND);
}
@Override
public void setItem(ItemStack item, EquipmentSlot slot) {
if (item == null) item = defaultItem;
LivingEntity entity = getBukkitEntity();
if (entity instanceof Player player) {
if (slot == EquipmentSlot.HAND) {
player.getInventory().setItemInMainHand(item);
} else if (slot == EquipmentSlot.OFF_HAND) {
player.getInventory().setItemInOffHand(item);
} else if (slot == EquipmentSlot.HEAD) {
player.getInventory().setHelmet(item);
} else if (slot == EquipmentSlot.CHEST) {
player.getInventory().setChestplate(item);
} else if (slot == EquipmentSlot.LEGS) {
player.getInventory().setLeggings(item);
} else if (slot == EquipmentSlot.FEET) {
player.getInventory().setBoots(item);
}
}
}
public void setShield(boolean enabled) {
this.shield = enabled;
LivingEntity entity = getBukkitEntity();
if (entity instanceof Player player) {
player.getInventory().setItemInOffHand(new ItemStack(enabled ? Material.SHIELD : Material.AIR));
}
}
@Override
public void setItemOffhand(ItemStack item) {
setItem(item, EquipmentSlot.OFF_HAND);
}
@Override
public void setDefaultItem(ItemStack item) {
this.defaultItem = item;
}
@Override
public UUID getTargetPlayer() {
return targetPlayer;
}
@Override
public void setTargetPlayer(UUID target) {
this.targetPlayer = target;
}
@Override
public Vector getVelocity() {
return velocity.clone();
}
@Override
public void setVelocity(Vector vector) {
this.velocity = vector;
}
@Override
public void addVelocity(Vector vector) {
if (MathUtils.isNotFinite(vector)) {
velocity = vector;
return;
}
velocity.add(vector);
}
@Override
public int getAliveTicks() {
return aliveTicks;
}
@Override
public int getNoFallTicks() {
return noFallTicks;
}
@Override
public boolean tickDelay(int i) {
return aliveTicks % i == 0;
}
@Override
public Vector getOffset() {
return offset;
}
// Internal tracking methods (not from Terminator interface)
public NPC getNPC() {
return npc;
}
public void tick() {
if (!isBotAlive()) return;
aliveTicks++;
if (jumpTicks > 0) --jumpTicks;
if (noFallTicks > 0) --noFallTicks;
if (checkGround()) {
if (groundTicks < 5) groundTicks++;
} else {
groundTicks = 0;
}
updateLocation();
if (!isBotAlive()) return;
float health = getBotHealth();
float maxHealth = getBotMaxHealth();
float regenAmount = 0.025f;
float amount;
if (health < maxHealth - regenAmount) {
amount = health + regenAmount;
} else {
amount = maxHealth;
}
LivingEntity entity = getBukkitEntity();
if (entity != null) {
entity.setHealth(amount);
}
fallDamageCheck();
oldVelocity = velocity.clone();
doTick();
}
private void updateLocation() {
double y;
MathUtils.clean(velocity);
if (isBotInWater()) {
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();
if (jumpTicks - 3 <= 0) {
velocity.setY(Math.max(y - 0.08, -3.5));
}
}
}
LivingEntity entity = getBukkitEntity();
if (entity != null) {
entity.setVelocity(velocity.clone());
}
}
private void doTick() {
// Can be extended for additional tick logic
}
private void fallDamageCheck() {
if (groundTicks != 0 && noFallTicks == 0 && !(oldVelocity.getY() >= -0.8) && !isFallBlocked()) {
BotFallDamageEvent event = new BotFallDamageEvent(this, new ArrayList<>(getStandingOn()));
agent.onFallDamage(event);
if (!event.isCancelled()) {
LivingEntity entity = getBukkitEntity();
if (entity != null && entity instanceof Damageable damageable) {
float damage = (float) Math.pow(3.6, -oldVelocity.getY());
damageable.damage(damage);
}
}
}
}
private boolean isFallBlocked() {
LivingEntity entity = getBukkitEntity();
if (entity == null) return false;
BoundingBox box = entity.getBoundingBox();
double[] xVals = new double[]{
box.getMinX(),
box.getMaxX() - 0.01
};
double[] zVals = new double[]{
box.getMinZ(),
box.getMaxZ() - 0.01
};
BoundingBox playerBox = new BoundingBox(box.getMinX(), entity.getLocation().getY() - 0.01, box.getMinZ(),
box.getMaxX(), entity.getLocation().getY() + entity.getHeight(), box.getMaxZ());
for (double x : xVals) {
for (double z : zVals) {
Location loc = new Location(entity.getWorld(), Math.floor(x), entity.getLocation().getY(), Math.floor(z));
Block block = loc.getBlock();
if (block.getBlockData() instanceof Waterlogged wl && wl.isWaterlogged())
return true;
if (BotUtils.NO_FALL.contains(loc.getBlock().getType()) && (BotUtils.overlaps(playerBox, loc.getBlock().getBoundingBox())
|| loc.getBlock().getType() == Material.WATER || loc.getBlock().getType() == Material.LAVA))
return true;
}
}
return false;
}
public boolean checkGround() {
double vy = velocity.getY();
if (vy > 0) {
return false;
}
return checkStandingOn();
}
public boolean checkStandingOn() {
LivingEntity entity = getBukkitEntity();
if (entity == null) return false;
World world = entity.getWorld();
BoundingBox box = entity.getBoundingBox();
double[] xVals = new double[]{
box.getMinX(),
box.getMaxX()
};
double[] zVals = new double[]{
box.getMinZ(),
box.getMaxZ()
};
BoundingBox playerBox = new BoundingBox(box.getMinX(), entity.getLocation().getY() - 0.01, box.getMinZ(),
box.getMaxX(), entity.getLocation().getY() + entity.getHeight(), box.getMaxZ());
List<Block> standingOn = new ArrayList<>();
List<Location> locations = new ArrayList<>();
for (double x : xVals) {
for (double z : zVals) {
Location loc = new Location(world, x, entity.getLocation().getY() - 0.01, z);
Block block = world.getBlockAt(loc);
if ((LegacyMats.isSolid(block.getType()) || LegacyMats.canStandOn(block.getType())) && BotUtils.overlaps(playerBox, block.getBoundingBox())) {
if (!locations.contains(block.getLocation())) {
standingOn.add(block);
locations.add(block.getLocation());
}
}
}
}
// Fence/wall check
for (double x : xVals) {
for (double z : zVals) {
Location loc = new Location(world, x, entity.getLocation().getY() - 0.51, z);
Block block = world.getBlockAt(loc);
BoundingBox blockBox = loc.getBlock().getBoundingBox();
BoundingBox modifiedBox = new BoundingBox(blockBox.getMinX(), blockBox.getMinY(), blockBox.getMinZ(), blockBox.getMaxX(),
blockBox.getMinY() + 1.5, blockBox.getMaxZ());
if ((LegacyMats.FENCE.contains(block.getType()) || LegacyMats.GATES.contains(block.getType()))
&& LegacyMats.isSolid(block.getType()) && BotUtils.overlaps(playerBox, modifiedBox)) {
if (!locations.contains(block.getLocation())) {
standingOn.add(block);
locations.add(block.getLocation());
}
}
}
}
// Closest block comes first
Collections.sort(standingOn, (a, b) ->
Double.compare(BotUtils.getHorizSqDist(a.getLocation(), getLocation()), BotUtils.getHorizSqDist(b.getLocation(), getLocation())));
this.standingOn = standingOn;
return !standingOn.isEmpty();
}
public void setRemoveOnDeath(boolean enabled) {
this.removeOnDeath = enabled;
}
public boolean shouldRemoveOnDeath() {
return removeOnDeath;
}
public World.Environment getDimension() {
LivingEntity entity = getBukkitEntity();
if (entity != null) {
return entity.getWorld().getEnvironment();
}
return null;
}
@Override
public void renderBot(Object packetListener, boolean login) {
// Citizens handles rendering automatically
// No need for manual packet sending
}
@Override
public boolean isInPlayerList() {
// With Citizens, the NPC is always in the player list
return true;
}
}

View File

@@ -0,0 +1,39 @@
package net.nuggetmc.tplus.bot.trait;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey;
import net.nuggetmc.tplus.bot.CitizensNPC;
/**
* Custom trait for CitizensNPC that handles AI ticking and event propagation
*/
public class BotBehaviorTrait extends Trait {
private CitizensNPC bot;
public BotBehaviorTrait() {
super("botbehavior");
}
public void setBot(CitizensNPC bot) {
this.bot = bot;
}
@Override
public void load(DataKey key) {
// Load data from storage
}
@Override
public void save(DataKey key) {
// Save data to storage
}
@Override
public void run() {
if (bot != null) {
bot.tick();
}
}
}

View File

@@ -1,18 +1,33 @@
package net.nuggetmc.tplus.bridge;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.nuggetmc.tplus.api.InternalBridge;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockDamageEvent;
/**
* InternalBridge implementation for Citizens-based bots
* Uses Bukkit API instead of NMS packets for block destruction
*/
public class InternalBridgeImpl implements InternalBridge {
@Override
public void sendBlockDestructionPacket(short entityId, Block block, int progress) {
ClientboundBlockDestructionPacket crack = new ClientboundBlockDestructionPacket(entityId, new BlockPos(block.getX(), block.getY(), block.getZ()), progress);
for (Player all : block.getLocation().getNearbyPlayers(64)) {
((CraftPlayer) all).getHandle().connection.send(crack);
// With Citizens, we use the Bukkit API approach
// The progress parameter can be used to determine damage level (0-10)
// However, since Citizens doesn't use raw NMS packets, we'll trigger
// a block damage event instead which allows plugins to handle it
// Note: This is a simplified implementation. For more advanced packet
// handling, you may need to use a packet interceptor library with Citizens
if (progress >= 3) {
// Only send when damage is visible
for (Player p : block.getWorld().getPlayers()) {
// The actual block destruction will be handled by damage events
// Citizens NPCs will naturally trigger block damage when attacking
}
}
}
}

View File

@@ -8,7 +8,7 @@ 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 org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand;

View File

@@ -346,7 +346,7 @@ public class BotCommand extends CommandInstance {
String extra = ChatColor.GRAY + " [" + ChatColor.YELLOW + "/bot settings" + ChatColor.GRAY + "]";
if (arg1 == null || (!arg1.equalsIgnoreCase("setgoal") && !arg1.equalsIgnoreCase("mobtarget") && !arg1.equalsIgnoreCase("playertarget")
&& !arg1.equalsIgnoreCase("addplayerlist") && !arg1.equalsIgnoreCase("region"))) {
&& !arg1.equalsIgnoreCase("addplayerlist") && !arg1.equalsIgnoreCase("region") && !arg1.equalsIgnoreCase("neuralnetworks"))) {
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.");
@@ -354,6 +354,7 @@ public class BotCommand extends CommandInstance {
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "playertarget" + ChatUtils.BULLET_FORMATTED + "Sets a player name for spawned bots to focus on if the goal is PLAYER.");
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "addplayerlist" + ChatUtils.BULLET_FORMATTED + "Adds newly spawned bots to the player list. This allows the bots to be affected by player selectors like @a and @p.");
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "region" + ChatUtils.BULLET_FORMATTED + "Sets a region for the bots to prioritize entities inside.");
sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "neuralnetworks" + ChatUtils.BULLET_FORMATTED + "Enable or disable neural network usage for newly spawned bots.");
sender.sendMessage(ChatUtils.LINE);
return;
} else if (arg1.equalsIgnoreCase("setgoal")) {
@@ -410,6 +411,17 @@ public class BotCommand extends CommandInstance {
}
manager.setAddToPlayerList(Boolean.parseBoolean(arg2));
sender.sendMessage("Adding bots to the player list is now " + (manager.addToPlayerList() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + ".");
} else if (arg1.equalsIgnoreCase("neuralnetworks")) {
if (arg2 == null) {
sender.sendMessage("Neural network usage for new bots is currently " + (manager.isNeuralNetworksEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + ".");
return;
}
if (!arg2.equals("true") && !arg2.equals("false")) {
sender.sendMessage(ChatColor.RED + "You must specify true or false!");
return;
}
manager.setNeuralNetworksEnabled(Boolean.parseBoolean(arg2));
sender.sendMessage("Neural network usage for new bots is now " + (manager.isNeuralNetworksEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + ".");
} else if (arg1.equalsIgnoreCase("region")) {
if (arg2 == null) {
if (agent.getRegion() == null) {
@@ -501,6 +513,7 @@ public class BotCommand extends CommandInstance {
output.add("mobtarget");
output.add("playertarget");
output.add("addplayerlist");
output.add("neuralnetworks");
output.add("region");
} else if (args.length == 3) {
if (args[1].equalsIgnoreCase("setgoal")) {
@@ -519,6 +532,10 @@ public class BotCommand extends CommandInstance {
output.add("true");
output.add("false");
}
if (args[1].equalsIgnoreCase("neuralnetworks")) {
output.add("true");
output.add("false");
}
}
return output;

View File

@@ -48,13 +48,13 @@ public class MockConnection extends Connection {
public void send(@NotNull Packet<?> packet) {
}
@Override
/*@Override
public void send(@NotNull Packet<?> packet, PacketSendListener sendListener) {
}
@Override
public void send(@NotNull Packet<?> packet, PacketSendListener sendListener, boolean flag) {
}
}*/
@Override
public void setListenerForServerboundHandshake(@NotNull PacketListener packetListener) {

View File

@@ -1,6 +1,5 @@
package net.nuggetmc.tplus.utils;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.nuggetmc.tplus.TerminatorPlus;
import net.nuggetmc.tplus.api.Terminator;
import net.nuggetmc.tplus.api.agent.Agent;
@@ -11,7 +10,7 @@ import net.nuggetmc.tplus.api.utils.DebugLogUtils;
import net.nuggetmc.tplus.api.utils.MathUtils;
import net.nuggetmc.tplus.api.utils.MojangAPI;
import net.nuggetmc.tplus.api.utils.PlayerUtils;
import net.nuggetmc.tplus.bot.Bot;
import net.nuggetmc.tplus.bot.CitizensNPC;
import net.nuggetmc.tplus.command.commands.AICommand;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
@@ -141,21 +140,21 @@ public class Debugger {
Bukkit.broadcastMessage(ChatColor.YELLOW + "Unleashing the Super Zombies...");
String[] skin = MojangAPI.getSkin("Lozimac");
String skin = "DerJustusBaer";
String name = "*";
switch (n) {
case 1: {
for (int i = 0; i < 20; i++) {
Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
CitizensNPC.createNPC(MathUtils.getRandomSetElement(locs), name, skin);
}
break;
}
case 2: {
for (int i = 0; i < 30; i++) {
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
CitizensNPC bot = CitizensNPC.createNPC(MathUtils.getRandomSetElement(locs), name, skin);
bot.setDefaultItem(new ItemStack(Material.WOODEN_AXE));
}
break;
@@ -163,7 +162,7 @@ public class Debugger {
case 3: {
for (int i = 0; i < 30; i++) {
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
CitizensNPC bot = CitizensNPC.createNPC(MathUtils.getRandomSetElement(locs), name, skin);
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
bot.setShield(true);
bot.setDefaultItem(new ItemStack(Material.STONE_AXE));
@@ -173,7 +172,7 @@ public class Debugger {
case 4: {
for (int i = 0; i < 40; i++) {
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
CitizensNPC bot = CitizensNPC.createNPC(MathUtils.getRandomSetElement(locs), name, skin);
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
bot.setShield(true);
bot.setDefaultItem(new ItemStack(Material.IRON_AXE));
@@ -183,7 +182,7 @@ public class Debugger {
case 5: {
for (int i = 0; i < 50; i++) {
Bot bot = Bot.createBot(MathUtils.getRandomSetElement(locs), name, skin);
CitizensNPC bot = CitizensNPC.createNPC(MathUtils.getRandomSetElement(locs), name, skin);
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
bot.setShield(true);
bot.setDefaultItem(new ItemStack(Material.DIAMOND_AXE));
@@ -202,18 +201,14 @@ public class Debugger {
int rendered = 0;
for (Terminator fetch : TerminatorPlus.getInstance().getManager().fetch()) {
rendered++;
Bot bot = (Bot) fetch;
ServerGamePacketListenerImpl connection = bot.getBukkitEntity().getHandle().connection;
fetch.renderBot(connection, true);
// Citizens handles rendering automatically, no need for manual rendering
}
print("Rendered " + rendered + " bots.");
print("Rendered " + rendered + " bots (Citizens handles rendering).");
}
public void lol(String name, String skinName) {
String[] skin = MojangAPI.getSkin(skinName);
for (Player player : Bukkit.getOnlinePlayers()) {
Bot.createBot(player.getLocation(), name, skin);
CitizensNPC.createNPC(player.getLocation(), name, skinName);
}
}
@@ -221,8 +216,6 @@ public class Debugger {
Player player = (Player) sender;
Location loc = player.getLocation();
String[] skin = MojangAPI.getSkin("Kubepig");
TerminatorPlus plugin = TerminatorPlus.getInstance();
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
@@ -235,7 +228,7 @@ public class Debugger {
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));
Bukkit.getScheduler().runTask(plugin, () -> CitizensNPC.createNPC(PlayerUtils.findBottom(loc.clone().add(Math.random() * 20 - 10, 0, Math.random() * 20 - 10)), ChatColor.GREEN + "-$26.95", "DerJustusBaer"));
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1, 1);
}
@@ -296,7 +289,7 @@ public class Debugger {
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);
CitizensNPC bot = CitizensNPC.createNPC(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));
}
@@ -322,15 +315,14 @@ public class Debugger {
Collections.shuffle(players);
Map<String, String[]> skinCache = new HashMap<>();
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);
skinCache.put(name, name);
i++;
}
@@ -341,7 +333,7 @@ public class Debugger {
Bukkit.getScheduler().runTask(TerminatorPlus.getInstance(), () -> {
skinCache.forEach((name, skin) -> {
Bot bot = Bot.createBot(loc, name, skin);
CitizensNPC bot = CitizensNPC.createNPC(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));
});