Added EntityAI to facilitate AI integration with other entity types (eg FakePlayer)

This commit is contained in:
Felix Cravic 2020-12-12 05:50:05 +01:00
parent 137273f05f
commit 7da5550858
3 changed files with 121 additions and 60 deletions

View File

@ -3,6 +3,7 @@ package net.minestom.server.entity;
import com.extollit.gaming.ai.path.HydrazinePathFinder; import com.extollit.gaming.ai.path.HydrazinePathFinder;
import com.extollit.gaming.ai.path.model.IPath; import com.extollit.gaming.ai.path.model.IPath;
import net.minestom.server.attribute.Attributes; import net.minestom.server.attribute.Attributes;
import net.minestom.server.entity.ai.EntityAI;
import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.ai.GoalSelector;
import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.ai.TargetSelector;
import net.minestom.server.entity.pathfinding.NavigableEntity; import net.minestom.server.entity.pathfinding.NavigableEntity;
@ -24,9 +25,8 @@ import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
public abstract class EntityCreature extends LivingEntity implements NavigableEntity { public abstract class EntityCreature extends LivingEntity implements NavigableEntity, EntityAI {
private int removalAnimationDelay = 1000; private int removalAnimationDelay = 1000;
@ -82,57 +82,13 @@ public abstract class EntityCreature extends LivingEntity implements NavigableEn
@Override @Override
public void update(long time) { public void update(long time) {
// AI
if (getInstance() == null) { aiTick(time);
return;
}
// Goal selectors
{
// 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 current goal selector
if (currentGoalSelector != null) {
currentGoalSelector.tick(time);
}
}
// Path finding // Path finding
pathFindingTick(getAttributeValue(Attributes.MOVEMENT_SPEED)); pathFindingTick(getAttributeValue(Attributes.MOVEMENT_SPEED));
// Fire, item pickup, ...
super.update(time); super.update(time);
} }
@ -242,26 +198,29 @@ public abstract class EntityCreature extends LivingEntity implements NavigableEn
this.removalAnimationDelay = removalAnimationDelay; this.removalAnimationDelay = removalAnimationDelay;
} }
/**
* Gets the goal selectors of this entity.
*
* @return a modifiable list containing the entity goal selectors
*/
@NotNull @NotNull
@Override
public List<GoalSelector> getGoalSelectors() { public List<GoalSelector> getGoalSelectors() {
return goalSelectors; return goalSelectors;
} }
/**
* Gets the target selectors of this entity.
*
* @return a modifiable list containing the entity target selectors
*/
@NotNull @NotNull
@Override
public List<TargetSelector> getTargetSelectors() { public List<TargetSelector> getTargetSelectors() {
return targetSelectors; return targetSelectors;
} }
@Nullable
@Override
public GoalSelector getCurrentGoalSelector() {
return currentGoalSelector;
}
@Override
public void setCurrentGoalSelector(GoalSelector currentGoalSelector) {
this.currentGoalSelector = currentGoalSelector;
}
/** /**
* Gets the entity target. * Gets the entity target.
* *

View File

@ -0,0 +1,102 @@
package net.minestom.server.entity.ai;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Represents an entity which can contain
* {@link GoalSelector goal selectors} and {@link TargetSelector target selectors}.
*/
public interface EntityAI {
/**
* Gets the goal selectors of this entity.
*
* @return a modifiable list containing the entity goal selectors
*/
@NotNull
List<GoalSelector> getGoalSelectors();
/**
* Gets the target selectors of this entity.
*
* @return a modifiable list containing the entity target selectors
*/
@NotNull
List<TargetSelector> getTargetSelectors();
/**
* Gets the current entity goal selector.
*
* @return the current entity goal selector, null if not any
*/
@Nullable
GoalSelector getCurrentGoalSelector();
/**
* Changes the entity current goal selector.
* <p>
* Mostly unsafe since the current goal selector should normally
* be chosen during the entity tick method.
*
* @param goalSelector the new entity goal selector, null to disable it
*/
void setCurrentGoalSelector(@Nullable GoalSelector goalSelector);
/**
* Performs an AI tick, it includes finding a new {@link GoalSelector}
* or tick the current one,
*
* @param time the tick time in milliseconds
*/
default void aiTick(long time) {
GoalSelector currentGoalSelector = getCurrentGoalSelector();
// true if the goal selector changed this tick
boolean newGoalSelector = false;
if (currentGoalSelector == null) {
// No goal selector, get a new one
currentGoalSelector = findGoal();
newGoalSelector = currentGoalSelector != null;
} else {
final boolean stop = currentGoalSelector.shouldEnd();
if (stop) {
// The current goal selector stopped, find a new one
currentGoalSelector.end();
currentGoalSelector = findGoal();
newGoalSelector = currentGoalSelector != null;
}
}
// Start the new goal selector
if (newGoalSelector) {
setCurrentGoalSelector(currentGoalSelector);
currentGoalSelector.start();
}
// Execute tick for the current goal selector
if (currentGoalSelector != null) {
currentGoalSelector.tick(time);
}
}
/**
* Finds a new {@link GoalSelector} for the entity.
* <p>
* Uses {@link GoalSelector#shouldStart()} and return the goal selector if true.
*
* @return the goal selector found, null if not any
*/
private GoalSelector findGoal() {
for (GoalSelector goalSelector : getGoalSelectors()) {
final boolean start = goalSelector.shouldStart();
if (start) {
return goalSelector;
}
}
return null;
}
}

View File

@ -71,7 +71,7 @@ public class RedstonePlacementRule extends BlockPlacementRule {
// TODO power // TODO power
final String[] properties = new String[]{ final String[] properties = {
"east=" + east, "east=" + east,
"north=" + north, "north=" + north,
"power=" + power, "power=" + power,