From 76efbdcb1f2ae7ac96851490d29a409e5c1b7d64 Mon Sep 17 00:00:00 2001 From: Tofaa <82680183+Tofaa2@users.noreply.github.com> Date: Thu, 18 Jan 2024 07:38:24 +0300 Subject: [PATCH] I think i finished goal selectors --- .idea/workspace.xml | 24 +++--- README.md | 3 +- .../me/tofaa/entitylib/TickingContainer.java | 5 ++ .../tofaa/entitylib/entity/WrapperEntity.java | 2 +- .../entity/WrapperEntityCreature.java | 52 +++++++++++- .../entitylib/entity/WrapperLivingEntity.java | 13 +++ .../me/tofaa/entitylib/entity/ai/AIGroup.java | 67 ++++++++++++++- .../entitylib/entity/ai/GoalSelector.java | 83 ++++++++++++++++++- .../entitylib/entity/ai/GoalSelectorList.java | 54 ++++++++++++ .../entitylib/entity/ai/TargetSelector.java | 4 - .../ai/goals/RandomHeadMovementGoal.java | 81 ++++++++++++++++++ .../entitylib/extras/CoordinateUtil.java | 38 +++++++++ .../tofaa/entitylib/world/WrapperWorld.java | 22 ----- 13 files changed, 404 insertions(+), 44 deletions(-) create mode 100644 src/main/java/me/tofaa/entitylib/entity/ai/GoalSelectorList.java delete mode 100644 src/main/java/me/tofaa/entitylib/entity/ai/TargetSelector.java create mode 100644 src/main/java/me/tofaa/entitylib/entity/ai/goals/RandomHeadMovementGoal.java create mode 100644 src/main/java/me/tofaa/entitylib/extras/CoordinateUtil.java delete mode 100644 src/main/java/me/tofaa/entitylib/world/WrapperWorld.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 8960a0e..c959e8a 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,21 +5,19 @@ - - - - - - - - - + + + - + + + - - + + + + diff --git a/README.md b/README.md index 1f7a5b2..7a00678 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # EntityLib EntityLib is a PacketEvents addon that provides an abstraction over raw entity data and packets to make it easier to work with entities as a whole. +Currently EntityLib is only stable for 1.18+, but it will support all versions that PacketEvents supports in the future. ```groovy //https://jitpack.io/#Tofaa2/EntityLib/ repositories { @@ -99,7 +100,7 @@ Once this list is complete, i will release a stable version of the library. - [ ] Add support for more actions using WrapperEntities. - [ ] More javadocs. - [x] Make ObjectData actually useful. -- [ ] Multi-version support +- [ ] Multi-version support, currently only 1.18+ is stable. - [ ] Make class names match the protocol specified names. ### Credits diff --git a/src/main/java/me/tofaa/entitylib/TickingContainer.java b/src/main/java/me/tofaa/entitylib/TickingContainer.java index 82bb8a1..535608b 100644 --- a/src/main/java/me/tofaa/entitylib/TickingContainer.java +++ b/src/main/java/me/tofaa/entitylib/TickingContainer.java @@ -1,5 +1,10 @@ package me.tofaa.entitylib; +/** + * A container that can hold and tick {@link Tickable}s. + * This is for specific users to extend if they want ticking functionality. + * + */ public interface TickingContainer { void addTickable(Tickable tickable); diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java b/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java index 3a63be4..6cb6b62 100644 --- a/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java +++ b/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java @@ -24,7 +24,7 @@ public class WrapperEntity implements Tickable { private Location location; private boolean onGround; private boolean spawned; - private Vector3d velocity = Vector3d.zero(); + protected Vector3d velocity = Vector3d.zero(); public WrapperEntity(int entityId, @NotNull UUID uuid, EntityType entityType, EntityMeta meta) { this.uuid = Optional.of(uuid); diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperEntityCreature.java b/src/main/java/me/tofaa/entitylib/entity/WrapperEntityCreature.java index 3cb1f19..76aa3f9 100644 --- a/src/main/java/me/tofaa/entitylib/entity/WrapperEntityCreature.java +++ b/src/main/java/me/tofaa/entitylib/entity/WrapperEntityCreature.java @@ -1,10 +1,14 @@ package me.tofaa.entitylib.entity; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; +import me.tofaa.entitylib.entity.ai.AIGroup; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.EntityLib; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** @@ -18,9 +22,55 @@ import java.util.UUID; * The {@link WrapperEntityCreature} can be inherited to create custom entities. *

*/ -public class WrapperEntityCreature extends WrapperLivingEntity{ +public class WrapperEntityCreature extends WrapperLivingEntity { + + private final Set aiGroups; public WrapperEntityCreature(int entityId, @NotNull UUID uuid, EntityType entityType, EntityMeta meta) { super(entityId, uuid, entityType, meta); + this.aiGroups = new HashSet<>(); } + + @Override + public void kill() { + super.kill(); + } + + /** + * Adds an {@link AIGroup} to the entity. + *

+ * The AIGroup will be updated every tick. + *

+ * + * @param aiGroup The AIGroup to add. + */ + public void addAIGroup(AIGroup aiGroup) { + aiGroups.add(aiGroup); + } + + /** + * Removes an {@link AIGroup} from the entity. + * + * @param aiGroup The AIGroup to remove. + */ + public void removeAIGroup(AIGroup aiGroup) { + aiGroups.remove(aiGroup); + } + + /** + * Removes all {@link AIGroup}s from the entity. + */ + public void clearAIGroups() { + aiGroups.clear(); + } + + /** + * Gets the {@link AIGroup}s of the entity. + * + * @return The AIGroups of the entity. + */ + public Set getAIGroups() { + return Collections.unmodifiableSet(aiGroups); + } + } diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java b/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java index cf8f0e9..54c5e5e 100644 --- a/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java +++ b/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java @@ -1,6 +1,9 @@ package me.tofaa.entitylib.entity; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; +import com.github.retrooper.packetevents.util.Vector3d; +import com.github.retrooper.packetevents.util.Vector3f; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityStatus; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.meta.types.LivingEntityMeta; import org.jetbrains.annotations.NotNull; @@ -18,6 +21,16 @@ public class WrapperLivingEntity extends WrapperEntity{ } + public void kill() { + sendStatus((byte) 3); + setHealth(0); + this.velocity = Vector3d.zero(); + } + + public void sendStatus(byte status) { + sendPacketsToViewers(new WrapperPlayServerEntityStatus(getEntityId(), status)); + } + public float getHealth() { return getMeta().getHealth(); } diff --git a/src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java b/src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java index 0a59dfa..ef2f523 100644 --- a/src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java +++ b/src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java @@ -1,4 +1,69 @@ package me.tofaa.entitylib.entity.ai; -public interface AIGroup { +import me.tofaa.entitylib.Tickable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; + +public class AIGroup implements Tickable { + + private final List goalSelectors = new GoalSelectorList(this); + private GoalSelector currentGoalSelector; + + public @NotNull Collection getGoalSelectors() { + return goalSelectors; + } + + public @Nullable GoalSelector getCurrentGoal() { + return currentGoalSelector; + } + + /** + * Adds a goal selector to the end of the list. Might be potentially unsafe to use after the entity has been spawned. + * + * @param goalSelector the goal selector to add + */ + public void addGoalSelector(@NotNull GoalSelector goalSelector) { + this.goalSelectors.add(goalSelector); + } + + public void setCurrentGoal(@Nullable GoalSelector goalSelector) { + if (goalSelector != null && goalSelector.getAIGroup() != this) { + throw new IllegalArgumentException("GoalSelector is not in this AIGroup"); + } + currentGoalSelector = goalSelector; + } + + @Override + public void update(long time) { + GoalSelector currentGoalSelector = getCurrentGoal(); + + if (currentGoalSelector != null && currentGoalSelector.shouldEnd()) { + currentGoalSelector.end(); + currentGoalSelector = null; + setCurrentGoal(null); + } + + for (GoalSelector selector : getGoalSelectors()) { + if (selector == currentGoalSelector) { + break; + } + if (selector.shouldStart()) { + if (currentGoalSelector != null) { + currentGoalSelector.end(); + } + currentGoalSelector = selector; + setCurrentGoal(currentGoalSelector); + currentGoalSelector.start(); + break; + } + } + + if (currentGoalSelector != null) { + currentGoalSelector.tick(time); + } + } + } diff --git a/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelector.java b/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelector.java index 63c5965..37e7875 100644 --- a/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelector.java +++ b/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelector.java @@ -1,4 +1,85 @@ package me.tofaa.entitylib.entity.ai; -public class GoalSelector { +import me.tofaa.entitylib.entity.WrapperEntityCreature; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; + +/** + * Goals represent tasks that a {@link me.tofaa.entitylib.entity.WrapperEntityCreature} can perform. + */ +public abstract class GoalSelector { + + private WeakReference aiGroupRef; + protected WrapperEntityCreature entity; + + public GoalSelector(WrapperEntityCreature entity) { + this.entity = entity; + } + + /** + * Whether this {@link GoalSelector} should start. + * + * @return true to start + */ + public abstract boolean shouldStart(); + + /** + * Starts this {@link GoalSelector}. + */ + public abstract void start(); + + /** + * Called every tick when this {@link GoalSelector} is running. + * + * @param time the time of the update in milliseconds + */ + public abstract void tick(long time); + + /** + * Whether this {@link GoalSelector} should end. + * + * @return true to end + */ + public abstract boolean shouldEnd(); + + /** + * Ends this {@link GoalSelector}. + */ + public abstract void end(); + + + /** + * Gets the entity behind the goal selector. + * + * @return the entity + */ + @NotNull + public WrapperEntityCreature getEntityCreature() { + return entity; + } + + /** + * Changes the entity affected by the goal selector. + *

+ * WARNING: this does not add the goal selector to {@code entityCreature}, + * this only change the internal entity AI group's field. Be sure to remove the goal from + * the previous entity AI group and add it to the new one using {@link AIGroup#getGoalSelectors()}. + * + * @param entity the new affected entity + */ + public void setEntityCreature(@NotNull WrapperEntityCreature entity) { + this.entity = entity; + } + + void setAIGroup(@NotNull AIGroup group) { + this.aiGroupRef = new WeakReference<>(group); + } + + @Nullable + protected AIGroup getAIGroup() { + return this.aiGroupRef.get(); + } + } diff --git a/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelectorList.java b/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelectorList.java new file mode 100644 index 0000000..b783262 --- /dev/null +++ b/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelectorList.java @@ -0,0 +1,54 @@ +package me.tofaa.entitylib.entity.ai; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.UnaryOperator; + +final class GoalSelectorList extends ArrayList { + + final AIGroup aiGroup; + + GoalSelectorList(AIGroup aiGroup) { + this.aiGroup = aiGroup; + } + + @Override + public GoalSelector set(int index, GoalSelector element) { + element.setAIGroup(aiGroup); + return super.set(index, element); + } + + @Override + public boolean add(GoalSelector element) { + element.setAIGroup(aiGroup); + return super.add(element); + } + + @Override + public void add(int index, GoalSelector element) { + element.setAIGroup(aiGroup); + super.add(index, element); + } + + @Override + public boolean addAll(Collection c) { + c.forEach(goalSelector -> goalSelector.setAIGroup(aiGroup)); + return super.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + c.forEach(goalSelector -> goalSelector.setAIGroup(aiGroup)); + return super.addAll(index, c); + } + + @Override + public void replaceAll(UnaryOperator operator) { + super.replaceAll(goalSelector -> { + goalSelector = operator.apply(goalSelector); + goalSelector.setAIGroup(aiGroup); + return goalSelector; + }); + } + +} \ No newline at end of file diff --git a/src/main/java/me/tofaa/entitylib/entity/ai/TargetSelector.java b/src/main/java/me/tofaa/entitylib/entity/ai/TargetSelector.java deleted file mode 100644 index 3429ed6..0000000 --- a/src/main/java/me/tofaa/entitylib/entity/ai/TargetSelector.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.tofaa.entitylib.entity.ai; - -public class TargetSelector { -} diff --git a/src/main/java/me/tofaa/entitylib/entity/ai/goals/RandomHeadMovementGoal.java b/src/main/java/me/tofaa/entitylib/entity/ai/goals/RandomHeadMovementGoal.java new file mode 100644 index 0000000..e085ace --- /dev/null +++ b/src/main/java/me/tofaa/entitylib/entity/ai/goals/RandomHeadMovementGoal.java @@ -0,0 +1,81 @@ +package me.tofaa.entitylib.entity.ai.goals; + +import com.github.retrooper.packetevents.util.Vector3d; +import me.tofaa.entitylib.entity.WrapperEntityCreature; +import me.tofaa.entitylib.entity.ai.GoalSelector; +import me.tofaa.entitylib.extras.CoordinateUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Random; +import java.util.function.Function; +import java.util.function.Supplier; + +public class RandomHeadMovementGoal extends GoalSelector { + + private static final Random RANDOM = new Random(); + private final int chancePerTick; + private final Supplier minimalLookTimeSupplier; + private final Function randomDirectionFunction; + private Vector3d lookDirection; + private int lookTime = 0; + + public RandomHeadMovementGoal(WrapperEntityCreature entityCreature, int chancePerTick) { + this(entityCreature, chancePerTick, + // These two functions act similarly enough to how MC randomly looks around. + // Look in one direction for at most 40 ticks and at minimum 20 ticks. + () -> 20 + RANDOM.nextInt(20), + // Look at a random block + (creature) -> { + final double n = Math.PI * 2 * RANDOM.nextDouble(); + return new Vector3d( + (float) Math.cos(n), + 0, + (float) Math.sin(n) + ); + }); + } + + /** + * @param entityCreature Creature that should randomly look around. + * @param chancePerTick The chance (per tick) that the entity looks around. Setting this to N would mean there is a 1 in N chance. + * @param minimalLookTimeSupplier A supplier that returns the minimal amount of time an entity looks in a direction. + * @param randomDirectionFunction A function that returns a random vector that the entity will look in/at. + */ + public RandomHeadMovementGoal( + WrapperEntityCreature entityCreature, + int chancePerTick, + @NotNull Supplier minimalLookTimeSupplier, + @NotNull Function randomDirectionFunction) { + super(entityCreature); + this.chancePerTick = chancePerTick; + this.minimalLookTimeSupplier = minimalLookTimeSupplier; + this.randomDirectionFunction = randomDirectionFunction; + } + + @Override + public boolean shouldStart() { + return RANDOM.nextInt(chancePerTick) == 0; + } + + @Override + public void start() { + lookTime = minimalLookTimeSupplier.get(); + lookDirection = randomDirectionFunction.apply(entity); + } + + @Override + public void tick(long time) { + --lookTime; + entity.teleport(CoordinateUtil.withDirection(entity.getLocation(), lookDirection)); + } + + @Override + public boolean shouldEnd() { + return this.lookTime < 0; + } + + @Override + public void end() { + + } +} diff --git a/src/main/java/me/tofaa/entitylib/extras/CoordinateUtil.java b/src/main/java/me/tofaa/entitylib/extras/CoordinateUtil.java new file mode 100644 index 0000000..25e5c39 --- /dev/null +++ b/src/main/java/me/tofaa/entitylib/extras/CoordinateUtil.java @@ -0,0 +1,38 @@ +package me.tofaa.entitylib.extras; + +import com.github.retrooper.packetevents.protocol.world.Location; +import com.github.retrooper.packetevents.util.Vector3d; + +public final class CoordinateUtil { + + private CoordinateUtil() {} + + public static Location withDirection(Location location, Vector3d direction) { + /* + * Sin = Opp / Hyp + * Cos = Adj / Hyp + * Tan = Opp / Adj + * + * x = -Opp + * z = Adj + */ + final double x = direction.getX(); + final double z = direction.getZ(); + if (x == 0 && z == 0) { + float pitch = direction.getY() > 0 ? -90f : 90f; + return new Location(location.getX(), location.getY(), location.getZ(), location.getYaw(), pitch); + } + final double theta = Math.atan2(-x, z); + final double xz = Math.sqrt(square(x) + square(z)); + final double _2PI = 2 * Math.PI; + + return new Location(location.getX(), location.getY(), location.getZ(), + (float) Math.toDegrees((theta + _2PI) % _2PI), + (float) Math.toDegrees(Math.atan(-direction.getY() / xz))); + } + + public static double square(double in) { + return in * in; + } + +} diff --git a/src/main/java/me/tofaa/entitylib/world/WrapperWorld.java b/src/main/java/me/tofaa/entitylib/world/WrapperWorld.java deleted file mode 100644 index 5504283..0000000 --- a/src/main/java/me/tofaa/entitylib/world/WrapperWorld.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.tofaa.entitylib.world; - -import com.github.retrooper.packetevents.protocol.player.User; -import me.tofaa.entitylib.entity.WrapperEntity; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -public class WrapperWorld { - - private final UUID uuid; - private final Set entities; - private final Set players; - - public WrapperWorld(UUID uuid) { - this.uuid = uuid; - this.entities = new HashSet<>(); - this.players = new HashSet<>(); - } - -}