Food bait for animals

Requires: FakePlayer & ProtocolLib

Requires: FakePlayer & ProtocolLib

FluidSpoofer.js
const ProtocolLib = Services.get("ProtocolLib");

var activeBaits = [];
var fakeIds = [];
var soundCoordinates = [];

const Bukkit = org.bukkit.Bukkit;
const dispatch = Bukkit.dispatchCommand;
const ItemStack = org.bukkit.inventory.ItemStack
const NamespacedKey = org.bukkit.NamespacedKey
const PersistentDataType = org.bukkit.persistence.PersistentDataType
const console = Bukkit.getConsoleSender();
const server = Bukkit.getServer();
const baitIndicator = "isBait";

const ArmorStand = org.bukkit.entity.ArmorStand;
const FileName = "BaitData"
const BaitsKey = "Baits";
const pickUpDistance = 3; // Blocks
const IdLenght = 16; 
const respawnLenght = 32;
DiskApi.loadFile(FileName, false, false);

function RunCommand(command) {
    task.main(function() {
        dispatch(console, command);
    });
}

function getEntityById(id) {
    for (const world of server.getWorlds()) {
        for (const entity of world.getEntities()) {
            if (entity.getEntityId() === id) return entity;
        }
    }
    return null;
}

function ArrayContains(Array, Value) {
    for (let i in Array) {
        if (Array[i] == Value) {
            return true
        }
    }
    return false
}

function GetStoredBaits() {
    let raw = DiskApi.getVar(FileName, "baitsData", "");

    return raw ? raw.split("|") : [];
}

function calculateDistance(loc1, loc2) {
    return loc1.distance(loc2);
}

function StoreBait(itemID) {
    let raw = DiskApi.getVar(FileName, "baitsData", "");
    const parts = raw ? raw.split("|") : [];

    if (!ArrayContains(parts, itemID)) {
        parts.push(itemID);
    }

    DiskApi.setVar(FileName, "baitsData", parts.join("|"));
}

function UnstoreBait(itemID) {
    let raw = DiskApi.getVar(FileName, "baitsData", "");
    const parts = raw ? raw.split("|") : [];

    const filtered = parts.filter(p => p !== itemID);
    DiskApi.setVar(FileName, "baitsData", filtered.join("|"));
}

function isFakePlayer(entity) {
    if (!entity) return false;
    return fakeIds[entity.getName()];
}

function getEntityID(entity) {
    var type = entity.getType().name().toLowerCase();
    var customName = entity.getCustomName();

    if (customName) {
        return customName.replace(type+"_", "");
    }
}

function getEntityByCustomName(customName) {
    for (const world of server.getWorlds()) {
        for (const entity of world.getEntities()) {
            const name = getEntityID(entity);
            if (name && name.equals(customName)) {
                return entity;
            }
        }
    }
    return null;
}

function getItemByUniqueID(uniqueID) {
    for (const world of server.getWorlds()) {
        for (const entity of world.getEntitiesByClass(Packages.org.bukkit.entity.Item.class)) {
            if (entity.getUniqueId().toString() === uniqueID) {
                return entity;
            }
        }
    }
    return null;
}

function setEntityID(entity, baitID) {
    var type = entity.getType().name().toLowerCase();
    entity.setCustomName(type + "_" + baitID);
}

function isFakeEntity(entity) {
    if (!entity) return false;
    var ID = getEntityID(entity);
    if (!ID) return false;
    return fakeIds[ID];
}

function markItemAsBait(item) {
    const meta = item.getItemMeta();
    meta.getPersistentDataContainer().set(
        NamespacedKey.minecraft("baititem"),
        PersistentDataType.STRING,
        "true"
    );
    item.setItemMeta(meta);
}

function isBaitItem(item) {
    if (!item || !item.hasItemMeta()) return false;
    const meta = item.getItemMeta();
    return meta.getPersistentDataContainer().has(
        NamespacedKey.minecraft("baititem"),
        PersistentDataType.STRING
    );
}

function isInventoryFull(player, stackableItem) {
    const inv = player.getInventory();
    const maxStack = stackableItem.getMaxStackSize();
    const type = stackableItem.getType();
    const meta = stackableItem.hasItemMeta() ? stackableItem.getItemMeta() : null;

    for (let i = 0; i < inv.getSize(); i++) {
        const item = inv.getItem(i);
        if (!item) return false;
        if (
            item.getType() === type &&
            item.getAmount() < maxStack &&
            (meta == null || (item.hasItemMeta() && item.getItemMeta().equals(meta)))
        ) {
            return false;
        }
    }

    return true;
}

function givePlayerBaitItem(player, material) {
    const item = new ItemStack(material, 1);
    var materialName = material.name();
    var constructedMatName = (materialName[0] + String(materialName).slice(1).toLowerCase()).replace("_", " ");
    var itemMeta = item.getItemMeta();
    itemMeta.setDisplayName("§6" + constructedMatName + "§7 §eBait§7");
    item.setItemMeta(itemMeta);
    markItemAsBait(item);
    if ( player.getInventory().addItem(item).isEmpty() ) {
        player.sendMessage("§6" + constructedMatName + "§7 §eBait§7 has been added to your inventory!")
        return true
    } else {
        return false
    }
}

function createPickupArmorStand(item, BaitID) {
  const loc = item.getLocation().clone();
  loc.setY(loc.getY() - 0.3);
  const stand = item.getWorld().spawn(loc, ArmorStand.class);

  setEntityID(stand, BaitID);
  stand.setInvisible(true);
  stand.setSmall(true);
  stand.setGravity(false);
  stand.setInvulnerable(true);
  stand.setCollidable(false);
  stand.setSilent(true);

  return stand;
}

ProtocolLib.registerListener("NORMAL", {
  onSend(event) {
    const packet = event.getPacket();
    const type = event.getPacketType().name();

    const entityId = packet.getIntegers().read(0);
    const entity = getEntityById(entityId);

    if (entity && isFakePlayer(entity)) {
        event.setCancelled(true);
    }
  }
}, ["Play.Server.SPAWN_ENTITY"]);

ProtocolLib.registerListener("NORMAL", {
    onSend(event) {
        const packet = event.getPacket();
        const entries = packet.getPlayerInfoDataLists().read(1);
        
        if (!entries || entries.isEmpty()) return;

        entries.removeIf(entry =>
            entry == null || fakeIds[entry.getProfile().getName()]
        );
        packet.getPlayerInfoDataLists().write(1, entries);
    }
}, ["Play.Server.PLAYER_INFO"]);

ProtocolLib.registerListener("NORMAL", {
    onSend(event) {
        const packet = event.getPacket();
        const soundData = packet.getIntegers();

        const x = soundData.read(0) / 8.0;
        const y = soundData.read(1) / 8.0;
        const z = soundData.read(2) / 8.0;

        for (const loc of soundCoordinates) {
            if ((Math.abs(loc.getX() - x) + Math.abs(loc.getY() - y) + Math.abs(loc.getZ() - z)) <= 0.4) {
                event.setCancelled(true);
                return;
            }
        }
    }
}, ["Play.Server.NAMED_SOUND_EFFECT"]);

function getBaitID(item) {
    return item.getUniqueId().toString().replace(/-/g, "").substring(0, IdLenght);
}

function UniqueToBaitID(uniqueId) {
    return uniqueId.toString().replace(/-/g, "").substring(0, IdLenght);
}

function spawnFakePlayer(Name, location, waitForPlayer) {
    const world = location.getWorld().getKey().toString();
    const x = location.getX();
    const y = location.getY();
    const z = location.getZ();

    RunCommand(`fp spawn ${Name} ${world} ${x} ${y} ${z}`);
    soundCoordinates.push(location)

    task.delay(0.5, function () {
        const index = soundCoordinates.indexOf(location);
        if (index !== -1) soundCoordinates.splice(index, 1);
    });

    if (waitForPlayer) {
        var fakeplayer = Bukkit.getPlayer(Name);

        if (!fakeplayer) {
            while (!fakeplayer) {
                task.wait(0.01);
                fakeplayer = Bukkit.getPlayer(Name);
            }
        }

        task.main(function() {
            fakeplayer.teleport(location)
        })

        return fakeplayer
    }
}

function updateFakePlayer(fakeplayer, material) {
    return task.main(function () {
            fakeplayer.setCollidable(false);
            fakeplayer.setSilent(true);
            fakeplayer.setGameMode(org.bukkit.GameMode.CREATIVE);
            fakeplayer.setWalkSpeed(0);
            fakeplayer.setFlySpeed(0);
            fakeplayer.setFlying(true);
    
            const inv = fakeplayer.getInventory();
            inv.setItemInMainHand(new ItemStack(material));
        })
}

function dropBait(item, player) {
    var BaitID = getBaitID(item);
    var armorStand = createPickupArmorStand(item, BaitID);
    var material = item.getItemStack().getType();
    var materialName = material.name();
    var constructedMatName = (materialName[0] + String(materialName).slice(1).toLowerCase()).replace("_", " ");
    var baitData = {};
    
    baitData["id"] = BaitID;
    baitData["storeID"] = StoreBait(item.getUniqueId().toString());
    baitData["item"] = item;
    baitData["armorStand"] = armorStand;
    baitData["tasks"] = [];
    fakeIds[BaitID] = true; // anything spawned with this ID is a bait

    item.setPickupDelay(9999999);
    item.setCustomName("§6" + constructedMatName + "§7 §eBait§7");
    item.setCustomNameVisible(true);
    item.setUnlimitedLifetime(true);

    task.spawn(function () {
        baitData["SpawnLocation"] = item.getLocation()
        var fakeplayer = spawnFakePlayer(BaitID, baitData.SpawnLocation, true);

        baitData["fakeplayer"] = fakeplayer;
        baitData.tasks.push(updateFakePlayer(fakeplayer, material))

        baitData.tasks.push(task.spawn(function() {
            while (true) {
                task.wait(0.1)
                if (baitData && baitData.SpawnLocation) {
                    task.main(function () {
                        armorStand.teleport(item.getLocation().clone().add(0, -0.7, 0));

                        if (calculateDistance(baitData.SpawnLocation, fakeplayer.getLocation()) >= respawnLenght ) {
                            fakeplayer.setHealth(0);
                            task.spawn(function() {
                                let itemLocation = item.getLocation()
                                baitData.SpawnLocation = itemLocation
                                fakeplayer = spawnFakePlayer(BaitID, itemLocation, true)
                                baitData.fakeplayer = fakeplayer;
                                updateFakePlayer(fakeplayer, material)
                            })
                        } else {
                            fakeplayer.teleport(item.getLocation().clone().add(0, -1, 0));
                        }

                    })
                }
            }
        }));

        activeBaits[BaitID] = baitData;
    })
}

function removebait(baitID) {
    var baitData = activeBaits[baitID];

    if (!baitData) return;

    for (let i = 0; i < baitData.tasks.length; i++) {
      task.cancel(baitData.tasks[i]);
    }

    UnstoreBait(baitData.storeID);

    baitData.item.remove();
    baitData.armorStand.remove();
    baitData.fakeplayer.setHealth(0);

    task.delay(0.5, function() {
        fakeIds[baitID] = null;
    })

    activeBaits[baitID] = null;
}

addCommand("bait", {
    onCommand: function(sender, args) {
        if (!sender.getLocation) {
            sender.sendMessage("§cOnly for players!.");
            return;
        }

        if (args.length < 1) {
            sender.sendMessage("§eUsage: /bait <item>");
            return;
        }

        const matName = args[0].toUpperCase();
        const material = org.bukkit.Material.matchMaterial(matName);

        if (!material || !material.isItem()) {
            sender.sendMessage("§cUnknown Item: " + matName);
            return;
        }

        const loc = sender.getLocation().clone().add(0, 1, 0);
        const world = sender.getWorld();

        var itemStack = new org.bukkit.inventory.ItemStack(material, 1);
        var item = world.dropItemNaturally(loc, itemStack);
        
        dropBait(item, sender);
        sender.sendMessage("§aDropped a bait with " + material.name());
    },

    onTabComplete: function(sender, args) {
        if (args.length === 1) {
            const prefix = args[0].toLowerCase();
            const matches = [];
            for (const mat of org.bukkit.Material.values()) {
                if (mat.isItem() && mat.name().toLowerCase().startsWith(prefix)) {
                    matches.push(mat.name().toLowerCase());
                }
            }
            return toJavaList(matches.slice(0, 40));
        }
        return toJavaList([]);
    }
});

registerEvent("org.bukkit.event.entity.EntityDamageEvent", function(event) {
    const entity = event.getEntity();
    if (isFakeEntity(entity)) {
        event.setCancelled(true);
    }
});

registerEvent("org.bukkit.event.player.PlayerInteractAtEntityEvent", function(event) {
    const entity = event.getRightClicked();
    if (isFakeEntity(entity)) {
        event.setCancelled(true);
        const player = event.getPlayer();
        const plrLoc = player.getLocation();
        const curLoc = entity.getLocation();
        if ( calculateDistance(curLoc, plrLoc) <= pickUpDistance ) {
            var baitID = getEntityID(entity)
            var baitData = activeBaits[baitID]
            var collectedItem = givePlayerBaitItem(player, baitData.item.getItemStack().getType())
            if (collectedItem) {
                removebait(baitID);
            } else {
                player.sendMessage("§cYour inventory is full");
            }
        }
    }
})

registerEvent("org.bukkit.event.player.PlayerDropItemEvent", (event) => {
    const rawItem = event.getItemDrop();
    const item = rawItem.getItemStack();
    if (isBaitItem(item)) {
        dropBait(rawItem)
    }
});

registerEvent("org.bukkit.event.entity.ItemDespawnEvent", (event) => {
    const rawItem = event.getEntity()
    const item = rawItem.getItemStack();
    if (isBaitItem(item)) {
        removebait(UniqueToBaitID(rawItem.getUniqueId().toString()))
    }
});

registerEvent("org.bukkit.event.player.PlayerJoinEvent", function (event) {
    const name = event.getPlayer().getName();
    if (fakeIds[name]) event.setJoinMessage(null);
});

registerEvent("org.bukkit.event.player.PlayerQuitEvent", function (event) {
    const name = event.getPlayer().getName();
    if (fakeIds[name]) event.setQuitMessage(null);
});

registerEvent("org.bukkit.event.entity.PlayerDeathEvent", function (event) {
    const name = event.getEntity().getName();
    if (fakeIds[name]) event.setDeathMessage(null);
});

registerEvent("org.bukkit.event.server.ServerCommandEvent", function (event) {
    const msg = event.getCommand();
    if (shouldSuppressMessage(msg)) event.setCancelled(true);
});

registerEvent("org.bukkit.event.server.BroadcastMessageEvent", function (event) {
    const msg = event.getMessage();
    if (shouldSuppressMessage(msg)) event.setMessage(null);
});

function shouldSuppressMessage(msg) {
    const words = msg.split(" ");
    for (let word of words) {
        if (word.length === IdLenght && fakeIds[word]) {
            return true;
        }
    }
    return false;
}

 
task.delay(1, function() { 
    task.main(function() {
        var baits = GetStoredBaits();
        for (let i in baits) { 
            var ItemID = baits[i];
            var item = getItemByUniqueID(ItemID);
            var baitID = UniqueToBaitID(ItemID);
            var armorStand = getEntityByCustomName(baitID);
            var fakePlayer = Bukkit.getPlayer(baitID);

            if (armorStand) {
                armorStand.remove();
            }

            if (fakePlayer) {
                fakePlayer.setHealth(0);
            }

            if (item) {
                dropBait(item)
            } else {
                log.info("Removed arbitary bait!")
                UnstoreBait(ItemID)
            }
        }
    })
})

Requires: ProtocolLib

Last updated

Was this helpful?