-
Notifications
You must be signed in to change notification settings - Fork 1
100% packet‐based entities blocks in a familiar structure for all
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
}
- For whom is this made and what do you need to know before starting?
- Getting started - Install as a dependency
- Inversion of Control (IoC)
- Listeners and how they are related to IoC
- Building commands based on annotations
- Creating configuration files based on classes and annotations
- Database integration without leaving the class
- Superior and intuitively understandable serialization
- User-friendly serialization of Kyori components
- Inventory as a user interface built on frames
- Different colliders and their intersections
- Thread-safe iterations through chunks with their own lifecycle for anything you desire
- 100% packet-based entities/blocks in a familiar structure for all
- Addressing needs and other minor functionalities
- Conclusion, justification, and acceptance