Skip to content

100% packet‐based entities blocks in a familiar structure for all

Daniel edited this page May 5, 2024 · 1 revision

Working with entities is so simple that you can look at this example and understand everything yourself.

PacketEntity packetEntity = PacketEntity.entity(type, location); // Creating a PacketBased entity
// Any values can be applied to the Entity, either from the Property enum or your own
Property.ENTITY_TEAM.apply(packetEntity, TeamColor.GOLD); // Setting the color the entity will glow with
EntityMeta meta = packetEntity.getMeta(); // Getting and processing meta for the entity as needed
if (meta instanceof ZombieMeta zombieMeta) {
    zombieMeta.setBaby(true); // If it's a zombie, make it a baby
}
meta.setGlowing(true); // Making the entity glow
packetEntity.addGoal(new FallEntityGoal()); // Teaching the entity to fall
packetEntity.addGoal(new LookEntityGoal(LookEntityGoal.Mode.PER_PLAYER)); // Teaching the entity to look at players
packetEntity.setViewedByEveryone(false); // Indicating that the entity cannot be seen by everyone
// Setting the filter for players who can see the entity
packetEntity.addFilter(player1 -> !player1.getName().contains("trash developer")); 

if (packetEntity instanceof PacketLivingEntity livingEntity) {
    // If our entity is living, equip it with a diamond helmet
    livingEntity.setItem(EquipmentSlot.HELMET, new ItemStack(Material.DIAMOND_HELMET));
}
// Registering the entity in our repository so that it gets its lifecycle
// If you don't want the entity to participate in the lifecycle of PacketEntityWorldRepository, you can skip this step and just call packetEntity#spawn manually
IoC.getBean(WorldRepositoryService.class)
        .get()
        .flatMap(repository -> repository.getRepository(PacketEntity.class, location.getWorld()))
        .flatMap(repository -> repository.add(packetEntity))
        .subscribe();
packetEntity.moveTo(player.getLocation()).subscribe(); // Asking the entity to move towards the player

As for Properties, you can assign them to any PacketEntity because they have a Map<String, Object>, essentially serving as a custom storage for each entity that you can utilize. You can find the list of reserved Properties in Jyraf in the Property enum.

It's important to note that IoC#getBean was used as an example of how to manually retrieve a component from the container; you don't necessarily have to do it the same way.

As for goals, you can independently implement PacketGoal<PacketEntity> to track and manipulate entities by receiving corresponding hooks such as beforeSpawn, onSpawn, onTick, beforeDespawn, and onDespawn. Each PacketGoal has its own priority equivalent to Bukkit's EventPriority. The return result in onTick should be DESTROY if the goal is completed, CONTINUE if the goal is not completed but subsequent goals are allowed to be processed, and PENDING if the goal is not completed and subsequent goals should not be processed.

Here's an example implementation of FallEntityGoal from Jyraf.

public class FallEntityGoal extends PacketGoal<PacketEntity> {
    public FallEntityGoal() {
        this(Priority.NORMAL);
    }

    public FallEntityGoal(Priority priority) {
        super(priority);
    }

    @Override
    public Result onTick(PacketEntity entry, Player... players) {
        Location location = entry.getLocation();
        ImmutableVector origin = ImmutableVector.of(location);
        World world = location.getWorld();
        double distanceAboveGround = origin.getDistanceAboveGround(world, true);
        if (distanceAboveGround >= 0.1) {
            ImmutableVector destiny = origin.subtract(new ImmutableVector(0d, distanceAboveGround, 0d));
            ImmutableVector direction = destiny.subtract(origin).normalize();
            Double entitySpeed = Property.ENTITY_SPEED.parse(entry, Double.class);
            double delta = entitySpeed * distanceAboveGround;
            double speed = FastMath.max(entitySpeed, FastMath.min(1, delta));
            if (distanceAboveGround > 1) {
                entry.velocity(direction.multiply(speed), Arrays.asList(players));
            }
            origin = origin.add(direction.multiply(speed));
            location.set(origin.getX(), origin.getY(), origin.getZ());
            entry.teleport(location, Arrays.asList(players));
        }
        return Result.CONTINUE;
    }
}

For PacketBlock, everything is absolutely identical.

BlockData blockData = material.createBlockData(); // Create BlockData for our type
// Handle BlockData as you wish
if (blockData instanceof Directional directional) {
    directional.setFacing(player.getFacing().getOppositeFace());
}
PacketBlock packetBlock = PacketBlock.of(blockData, location); // Create PacketBlock based on our BlockData and Location
packetBlock.setViewedByEveryone(true); // Allow everyone to see our PacketBlock
packetBlock.addGoal(...); // Optionally, we can also add a PacketGoal for the block
Property.VIEW_DISTANCE.apply(packetBlock, 10); // Similarly, we can apply some Properties
packetBlock.spawn(players); // We can spawn PacketBlock for a list of players or use WorldRepository for PacketBlock
if (packetBlock instanceof PacketChestBlock chestBlock) { // Similarly to PacketEntity metadata case, we can find out which block this is specifically
    chestBlock.open(players); // Start the chest opening animation
}
Clone this wiki locally