Basic goal selector API

This commit is contained in:
Felix Cravic 2020-08-06 11:56:43 +02:00
parent 20e78afb40
commit 3fd1efb120
9 changed files with 284 additions and 79 deletions

View File

@ -1,5 +1,6 @@
package fr.themode.demo;
import fr.themode.demo.entity.ChickenCreature;
import fr.themode.demo.generator.ChunkGeneratorDemo;
import fr.themode.demo.generator.NoiseTestGenerator;
import net.minestom.server.MinecraftServer;
@ -148,9 +149,8 @@ public class PlayerInit {
p.teleport(player.getPosition());
}*/
/*ChickenCreature chickenCreature = new ChickenCreature(player.getPosition());
ChickenCreature chickenCreature = new ChickenCreature(player.getPosition());
chickenCreature.setInstance(player.getInstance());
chickenCreature.setAttribute(Attribute.MOVEMENT_SPEED, 0.4f);*/
/*EntityZombie zombie = new EntityZombie(player.getPosition());
zombie.setAttribute(Attribute.MOVEMENT_SPEED, 0.25f);
@ -230,6 +230,7 @@ public class PlayerInit {
AdvancementRoot root = new AdvancementRoot(ColoredText.of("title"), ColoredText.of(ChatColor.BLUE + "description"),
Material.APPLE, FrameType.TASK, 0, 0,
"minecraft:textures/block/red_wool.png");
root.setAchieved(true);
AdvancementTab tab = advancementManager.createTab("root", root);
Advancement advancement = new Advancement(ColoredText.of("adv"), ColoredText.of("desc"),
Material.WOODEN_AXE, FrameType.CHALLENGE, 1, 0)
@ -240,7 +241,8 @@ public class PlayerInit {
root.setTitle(ColoredText.of("test ttlechange"));
Advancement advancement2 = new Advancement(ColoredText.of("adv"), ColoredText.of("desc"),
Advancement advancement2 = new Advancement(ColoredText.of(ChatColor.BLUE + "Title"),
ColoredText.of("description of the advancement"),
Material.GOLD_BLOCK, FrameType.CHALLENGE, 3, 0)
.showToast(true).setHidden(false);
tab.createAdvancement("second2", advancement2, root);

View File

@ -1,83 +1,19 @@
package fr.themode.demo.entity;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.ai.goal.DoNothingGoal;
import net.minestom.server.entity.type.EntityChicken;
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
public class ChickenCreature extends EntityChicken {
public ChickenCreature(Position defaultPosition) {
super(defaultPosition);
goalSelectors.add(new DoNothingGoal(this, 500, 0.1f));
}
@Override
public void spawn() {
}
@Override
public void update(long time) {
super.update(time);
float speed = 0.075f;
if (hasPassenger()) {
Entity passenger = getPassengers().iterator().next();
if (passenger instanceof Player) {
Player player = (Player) passenger;
PlayerVehicleInformation vehicleInformation = player.getVehicleInformation();
float sideways = vehicleInformation.getSideways();
float forward = vehicleInformation.getForward();
boolean jump = vehicleInformation.shouldJump();
boolean unmount = vehicleInformation.shouldUnmount();
if (jump && isOnGround()) {
setVelocity(new Vector(0, 6, 0));
}
boolean updateView = forward > 0;
if (sideways == 0f && forward == 0f)
return;
float yaw = player.getPosition().getYaw();
yaw %= 360;
sideways = yaw + (updateView ? -sideways * 90 : sideways * 90);
if (forward > 0) {
forward = yaw * forward;
} else {
forward = yaw + forward * 360;
}
yaw = (forward + sideways) / 2 % 360;
double radian = Math.toRadians(yaw + 90);
double cos = Math.cos(radian);
double sin = Math.sin(radian);
float x = (float) cos * speed;
float z = (float) sin * speed;
/*BlockPosition blockPosition = getPosition().toBlockPosition();
BlockPosition belowPosition = blockPosition.clone().add(0, -1, 0);
BlockPosition upPosition = blockPosition.clone().add(0, 1, 0);
boolean airCurrent = getInstance().getBlockId(blockPosition) == 0;
boolean airBelow = getInstance().getBlockId(belowPosition) == 0;
boolean airUp = getInstance().getBlockId(upPosition) == 0;
boolean shouldJump = false;
int boundingBoxY = (int) Math.ceil(getBoundingBox().getY());
for (int i = 0; i < boundingBoxY; i++) {
}*/
//System.out.println("test: "+player.isVehicleJump());
//System.out.println(getInstance().getBlockId(getPosition().toBlockPosition()));
move(x, 0, z, updateView);
}
} else {
//move(-0.5f * speed, 0, 0.5f * speed, false);
}
}
}

View File

@ -133,6 +133,25 @@ public class BossBar implements Viewable {
updateStyle();
}
/**
* Get the bossbar flags
*
* @return the flags
*/
public byte getFlags() {
return flags;
}
/**
* Set the bossbar flags
*
* @param flags the bossbar flags
* @see <a href="https://wiki.vg/Protocol#Boss_Bar">Boss bar packet</a>
*/
public void setFlags(byte flags) {
this.flags = flags;
}
/**
* Delete the boss bar and remove all of its viewers
*/

View File

@ -5,6 +5,8 @@ import com.extollit.gaming.ai.path.model.PathObject;
import net.minestom.server.MinecraftServer;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.entity.ai.GoalSelector;
import net.minestom.server.entity.ai.TargetSelector;
import net.minestom.server.entity.pathfinding.PFPathingEntity;
import net.minestom.server.event.entity.EntityAttackEvent;
import net.minestom.server.event.item.ArmorEquipEvent;
@ -19,12 +21,22 @@ import net.minestom.server.utils.item.ItemStackUtils;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.utils.validate.Check;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public abstract class EntityCreature extends LivingEntity {
private PFPathingEntity pathingEntity = new PFPathingEntity(this);
private HydrazinePathFinder pathFinder;
private PathObject path;
protected List<GoalSelector> goalSelectors = new ArrayList<>();
protected List<TargetSelector> targetSelectors = new ArrayList<>();
private GoalSelector currentGoalSelector;
private Entity target;
// Equipments
private ItemStack mainHandItem;
private ItemStack offHandItem;
@ -51,6 +63,49 @@ public abstract class EntityCreature extends LivingEntity {
@Override
public void update(long time) {
{
// Supplier used to get the next goal selector which should start
// (null if not found)
final Supplier<GoalSelector> goalSelectorSupplier = () -> {
for (GoalSelector goalSelector : goalSelectors) {
final boolean start = goalSelector.shouldStart();
if (start) {
return goalSelector;
}
}
return null;
};
// true if the goal selector changed this tick
boolean newGoalSelector = false;
if (currentGoalSelector == null) {
// No goal selector, get a new one
this.currentGoalSelector = goalSelectorSupplier.get();
newGoalSelector = currentGoalSelector != null;
} else {
final boolean stop = currentGoalSelector.shouldEnd();
if (stop) {
// The current goal selector stopped, find a new one
this.currentGoalSelector.end();
this.currentGoalSelector = goalSelectorSupplier.get();
newGoalSelector = currentGoalSelector != null;
}
}
// Start the new goal selector
if (newGoalSelector) {
this.currentGoalSelector.start();
}
// Execute tick for the goal selector
if (currentGoalSelector != null) {
currentGoalSelector.tick();
}
}
// Path finding
path = pathFinder.update();
if (path != null) {
@ -180,6 +235,42 @@ public abstract class EntityCreature extends LivingEntity {
return result;
}
/**
* Get the goal selectors of this entity
*
* @return a modifiable list containing the entity goal selectors
*/
public List<GoalSelector> getGoalSelectors() {
return goalSelectors;
}
/**
* Get the target selectors of this entity
*
* @return a modifiable list containing the entity target selectors
*/
public List<TargetSelector> getTargetSelectors() {
return targetSelectors;
}
/**
* Get the entity target
*
* @return the entity target
*/
public Entity getTarget() {
return target;
}
/**
* Change the entity target
*
* @param target the new entity target
*/
public void setTarget(Entity target) {
this.target = target;
}
@Override
public ItemStack getItemInMainHand() {
return mainHandItem;

View File

@ -0,0 +1,66 @@
package net.minestom.server.entity.ai;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature;
public abstract class GoalSelector {
private EntityCreature entityCreature;
public GoalSelector(EntityCreature entityCreature) {
this.entityCreature = entityCreature;
}
/**
* Whether or not this {@link GoalSelector} should start.
*
* @return true to start
*/
public abstract boolean shouldStart();
/**
* Start this {@link GoalSelector}
*/
public abstract void start();
/**
* Called every tick when this {@link GoalSelector} is running
*/
public abstract void tick();
/**
* Whether or not this {@link GoalSelector} should end.
*
* @return true to end
*/
public abstract boolean shouldEnd();
/**
* End this {@link GoalSelector}
*/
public abstract void end();
/**
* Get the entity linked to this goal selector
*
* @return the entity
*/
public EntityCreature getEntityCreature() {
return entityCreature;
}
/**
* Find a target based on the entity {@link TargetSelector}
*
* @return the target entity, null if not found
*/
public Entity findTarget() {
for (TargetSelector targetSelector : entityCreature.getTargetSelectors()) {
final Entity entity = targetSelector.findTarget();
if (entity != null) {
return entity;
}
}
return null;
}
}

View File

@ -0,0 +1,36 @@
package net.minestom.server.entity.ai;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature;
/**
* The target selector is called each time the entity receives an "attack" instruction
* without having a target
*/
public abstract class TargetSelector {
private final EntityCreature entityCreature;
public TargetSelector(EntityCreature entityCreature) {
this.entityCreature = entityCreature;
}
/**
* Find the target
* <p>
* returning null means that this target selector didn't find any entity,
* the next {@link TargetSelector} will be called until the end of the list or an entity is found
*
* @return the target, null if not any
*/
public abstract Entity findTarget();
/**
* Get the entity linked to this target selector
*
* @return the entity
*/
public EntityCreature getEntityCreature() {
return entityCreature;
}
}

View File

@ -0,0 +1,54 @@
package net.minestom.server.entity.ai.goal;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.ai.GoalSelector;
import net.minestom.server.utils.MathUtils;
import java.util.Random;
public class DoNothingGoal extends GoalSelector {
private static final Random RANDOM = new Random();
private long time;
private float chance;
private long startTime;
/**
* Create a DoNothing goal
*
* @param entityCreature the entity
* @param time the time in milliseconds where nothing happen
* @param chance the chance to do nothing (0-1)
*/
public DoNothingGoal(EntityCreature entityCreature, long time, float chance) {
super(entityCreature);
this.time = time;
this.chance = MathUtils.setBetween(chance, 0, 1);
}
@Override
public void end() {
this.startTime = 0;
}
@Override
public boolean shouldEnd() {
return System.currentTimeMillis() - startTime >= time;
}
@Override
public boolean shouldStart() {
return RANDOM.nextFloat() <= chance;
}
@Override
public void start() {
this.startTime = System.currentTimeMillis();
}
@Override
public void tick() {
}
}

View File

@ -48,6 +48,7 @@ public class ChunkBatch implements InstanceBatch {
}
private void addBlockData(byte x, int y, byte z, boolean customBlock, short blockId, short customBlockId, Data data) {
// TODO store a single long with bitwise operators (xyz;boolean,short,short,boolean) with the data in a map
BlockData blockData = new BlockData();
blockData.x = x;
blockData.y = y;

View File

@ -23,10 +23,10 @@ public class ChunkUtils {
* @return true if the chunk is unloaded, false otherwise
*/
public static boolean isChunkUnloaded(Instance instance, float x, float z) {
int chunkX = getChunkCoordinate((int) x);
int chunkZ = getChunkCoordinate((int) z);
final int chunkX = getChunkCoordinate((int) x);
final int chunkZ = getChunkCoordinate((int) z);
Chunk chunk = instance.getChunk(chunkX, chunkZ);
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
return isChunkUnloaded(chunk);
}
@ -53,8 +53,8 @@ public class ChunkUtils {
* @return an array containing both the chunk X and Z (index 0 = X; index 1 = Z)
*/
public static int[] getChunkCoord(long index) {
int chunkX = (int) (index >> 32);
int chunkZ = (int) index;
final int chunkX = (int) (index >> 32);
final int chunkZ = (int) index;
return new int[]{chunkX, chunkZ};
}
@ -74,8 +74,8 @@ public class ChunkUtils {
int counter = 0;
for (int x = startLoop; x < endLoop; x++) {
for (int z = startLoop; z < endLoop; z++) {
int chunkX = getChunkCoordinate((int) (position.getX() + Chunk.CHUNK_SIZE_X * x));
int chunkZ = getChunkCoordinate((int) (position.getZ() + Chunk.CHUNK_SIZE_Z * z));
final int chunkX = getChunkCoordinate((int) (position.getX() + Chunk.CHUNK_SIZE_X * x));
final int chunkZ = getChunkCoordinate((int) (position.getZ() + Chunk.CHUNK_SIZE_Z * z));
visibleChunks[counter] = getChunkIndex(chunkX, chunkZ);
counter++;
}
@ -106,7 +106,7 @@ public class ChunkUtils {
* @return the instance position of the block located in {@code index}
*/
public static BlockPosition getBlockPosition(int index, int chunkX, int chunkZ) {
int[] pos = indexToPosition(index, chunkX, chunkZ);
final int[] pos = indexToPosition(index, chunkX, chunkZ);
return new BlockPosition(pos[0], pos[1], pos[2]);
}
@ -119,7 +119,7 @@ public class ChunkUtils {
*/
public static int[] indexToPosition(int index, int chunkX, int chunkZ) {
int z = (byte) (index >> 12 & 0xF);
int y = (index >>> 4 & 0xFF);
final int y = (index >>> 4 & 0xFF);
int x = (byte) (index >> 0 & 0xF);
x += 16 * chunkX;