Allow querying action result.

This commit is contained in:
Florian Nücke
2021-01-14 18:11:33 +01:00
parent bf3a0da468
commit 4087fd4bb6
5 changed files with 204 additions and 62 deletions

View File

@@ -19,6 +19,7 @@ import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.network.message.*;
import li.cil.oc2.common.serialization.NBTSerialization;
import li.cil.oc2.common.util.NBTTagIds;
import li.cil.oc2.common.util.NBTUtils;
import li.cil.oc2.common.util.WorldUtils;
import li.cil.oc2.common.vm.*;
import net.minecraft.block.SoundType;
@@ -67,7 +68,8 @@ public final class RobotEntity extends Entity {
private static final String BUS_ELEMENT_TAG_NAME = "bus_element";
private static final String COMMAND_PROCESSOR_TAG_NAME = "commands";
private static final int MAX_QUEUED_ACTIONS = 15;
private static final int MAX_QUEUED_ACTIONS = 16;
private static final int MAX_QUEUED_RESULTS = 16;
private static final int MEMORY_SLOTS = 4;
private static final int HARD_DRIVE_SLOTS = 2;
@@ -80,7 +82,7 @@ public final class RobotEntity extends Entity {
private final Consumer<WorldEvent.Unload> worldUnloadListener = this::handleWorldUnload;
private final AnimationState animationState = new AnimationState();
private final CommandProcessor commandProcessor = new CommandProcessor();
private final RobotActionProcessor actionProcessor = new RobotActionProcessor();
private final Terminal terminal = new Terminal();
private final RobotVirtualMachineState state;
private final RobotItemStackHandlers items = new RobotItemStackHandlers();
@@ -145,8 +147,8 @@ public final class RobotEntity extends Entity {
} else {
registerListeners();
RobotActions.initializeData(this);
if (commandProcessor.action != null) {
commandProcessor.action.initialize(this);
if (actionProcessor.action != null) {
actionProcessor.action.initialize(this);
}
}
}
@@ -157,7 +159,7 @@ public final class RobotEntity extends Entity {
state.tick();
}
commandProcessor.tick();
actionProcessor.tick();
}
@Override
@@ -235,7 +237,7 @@ public final class RobotEntity extends Entity {
protected void writeAdditional(final CompoundNBT tag) {
tag.put(STATE_TAG_NAME, state.serialize());
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
tag.put(COMMAND_PROCESSOR_TAG_NAME, commandProcessor.serialize());
tag.put(COMMAND_PROCESSOR_TAG_NAME, actionProcessor.serialize());
tag.put(BUS_ELEMENT_TAG_NAME, busElement.serialize());
tag.put(Constants.INVENTORY_TAG_NAME, items.serialize());
}
@@ -244,7 +246,7 @@ public final class RobotEntity extends Entity {
protected void readAdditional(final CompoundNBT tag) {
state.deserialize(tag.getCompound(STATE_TAG_NAME));
NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal);
commandProcessor.deserialize(tag.getCompound(COMMAND_PROCESSOR_TAG_NAME));
actionProcessor.deserialize(tag.getCompound(COMMAND_PROCESSOR_TAG_NAME));
busElement.deserialize(tag.getCompound(BUS_ELEMENT_TAG_NAME));
if (tag.contains(Constants.INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
@@ -365,7 +367,7 @@ public final class RobotEntity extends Entity {
public float topRenderHover = -(hashCode() & 0xFFFF); // init to "random" to avoid synchronous hovering
public void update(final float deltaTime, final Random random) {
if (getState().isRunning() || commandProcessor.hasQueuedActions()) {
if (getState().isRunning() || actionProcessor.hasQueuedActions()) {
topRenderHover = topRenderHover + deltaTime * HOVER_ANIMATION_SPEED;
final float topOffsetY = MathHelper.sin(topRenderHover) / 32f;
@@ -386,13 +388,49 @@ public final class RobotEntity extends Entity {
}
}
private final class CommandProcessor {
private static final class RobotActionProcessorResult {
private static final String ACTION_ID_TAG_NAME = "action_id";
private static final String RESULT_TAG_NAME = "result";
public int actionId;
public RobotActionResult result;
public RobotActionProcessorResult(final int actionId, final RobotActionResult result) {
this.actionId = actionId;
this.result = result;
}
public RobotActionProcessorResult(final CompoundNBT tag) {
deserialize(tag);
}
public CompoundNBT serialize() {
final CompoundNBT tag = new CompoundNBT();
tag.putInt(ACTION_ID_TAG_NAME, actionId);
NBTUtils.putEnum(tag, RESULT_TAG_NAME, result);
return tag;
}
public void deserialize(final CompoundNBT tag) {
actionId = tag.getInt(ACTION_ID_TAG_NAME);
result = NBTUtils.getEnum(tag, RESULT_TAG_NAME, RobotActionResult.class);
}
}
private final class RobotActionProcessor {
private static final String QUEUE_TAG_NAME = "queue";
private static final String ACTION_TAG_NAME = "action";
private static final String RESULTS_TAG_NAME = "results";
private static final String LAST_ACTION_ID_TAG_NAME = "last_action_id";
private final Queue<AbstractRobotAction> queue = new ArrayDeque<>(MAX_QUEUED_ACTIONS);
private final Queue<AbstractRobotAction> queue = new ArrayDeque<>(MAX_QUEUED_ACTIONS - 1);
@Nullable private AbstractRobotAction action;
private final Queue<RobotActionProcessorResult> results = new ArrayDeque<>(MAX_QUEUED_RESULTS);
private int lastActionId;
public boolean hasQueuedActions() {
return action != null || !queue.isEmpty();
}
@@ -414,7 +452,16 @@ public final class RobotEntity extends Entity {
RobotActions.performClient(RobotEntity.this);
} else {
if (action != null) {
if (action.perform(RobotEntity.this)) {
final RobotActionResult result = action.perform(RobotEntity.this);
if (result != RobotActionResult.INCOMPLETE) {
synchronized (results) {
if (results.size() == MAX_QUEUED_RESULTS) {
results.remove();
}
results.add(new RobotActionProcessorResult(action.getId(), result));
}
action = null;
}
}
@@ -429,6 +476,8 @@ public final class RobotEntity extends Entity {
public void clear() {
queue.clear();
results.clear();
lastActionId = 0;
}
public CompoundNBT serialize() {
@@ -444,24 +493,40 @@ public final class RobotEntity extends Entity {
tag.put(ACTION_TAG_NAME, RobotActions.serialize(action));
}
final ListNBT resultsTag = new ListNBT();
for (final RobotActionProcessorResult result : results) {
resultsTag.add(result.serialize());
}
tag.put(RESULTS_TAG_NAME, resultsTag);
tag.putInt(LAST_ACTION_ID_TAG_NAME, lastActionId);
return tag;
}
public void deserialize(final CompoundNBT tag) {
queue.clear();
action = null;
results.clear();
final ListNBT queueTag = tag.getList(QUEUE_TAG_NAME, NBTTagIds.TAG_COMPOUND);
for (int i = 0; i < queueTag.size(); i++) {
for (int i = 0; i < Math.min(queueTag.size(), MAX_QUEUED_ACTIONS - 1); i++) {
final AbstractRobotAction action = RobotActions.deserialize(queueTag.getCompound(i));
if (action != null) {
queue.add(action);
}
}
if (tag.contains(ACTION_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
action = RobotActions.deserialize(tag.getCompound(ACTION_TAG_NAME));
action = RobotActions.deserialize(tag.getCompound(ACTION_TAG_NAME));
final ListNBT resultsTag = tag.getList(RESULTS_TAG_NAME, NBTTagIds.TAG_COMPOUND);
for (int i = 0; i < Math.min(resultsTag.size(), MAX_QUEUED_RESULTS); i++) {
final RobotActionProcessorResult result = new RobotActionProcessorResult(resultsTag.getCompound(i));
if (result.actionId != 0) {
results.add(result);
}
}
lastActionId = tag.getInt(LAST_ACTION_ID_TAG_NAME);
}
private boolean addAction(final AbstractRobotAction action) {
@@ -473,8 +538,12 @@ public final class RobotEntity extends Entity {
return false;
}
if (queue.size() < MAX_QUEUED_ACTIONS) {
queue.add(action);
if (queue.size() < MAX_QUEUED_ACTIONS - 1) { // -1 for current action
lastActionId = (lastActionId + 1) & 0x7FFFFFFF; // only positive ids; unlikely to ever wrap, but eh.
action.setId(lastActionId);
synchronized (queue) {
queue.add(action);
}
return true;
} else {
return false;
@@ -589,7 +658,7 @@ public final class RobotEntity extends Entity {
public void stopRunnerAndReset() {
super.stopRunnerAndReset();
commandProcessor.clear();
actionProcessor.clear();
}
@Override
@@ -609,21 +678,51 @@ public final class RobotEntity extends Entity {
}
public final class RobotDevice {
@Callback
@Callback(synchronize = false)
public boolean move(@Parameter("direction") @Nullable final MovementDirection direction) {
if (direction == null) throw new IllegalArgumentException();
return commandProcessor.move(direction);
return actionProcessor.move(direction);
}
@Callback
@Callback(synchronize = false)
public boolean turn(@Parameter("direction") @Nullable final RotationDirection direction) {
if (direction == null) throw new IllegalArgumentException();
return commandProcessor.rotate(direction);
return actionProcessor.rotate(direction);
}
@Callback
@Callback(synchronize = false)
public int getLastActionId() {
return actionProcessor.lastActionId;
}
@Callback(synchronize = false)
public int getQueuedActionCount() {
return commandProcessor.getQueuedActionCount();
return actionProcessor.getQueuedActionCount();
}
@Nullable
@Callback(synchronize = false)
public RobotActionResult getActionResult(@Parameter("actionId") final int actionId) {
final AbstractRobotAction currentAction = actionProcessor.action;
if (currentAction != null && currentAction.getId() == actionId) {
return RobotActionResult.INCOMPLETE;
}
synchronized (actionProcessor.queue) {
for (final AbstractRobotAction action : actionProcessor.queue) {
if (action.getId() == actionId) {
return RobotActionResult.INCOMPLETE;
}
}
}
synchronized (actionProcessor.results) {
for (final RobotActionProcessorResult result : actionProcessor.results) {
if (result.actionId == actionId) {
return result.result;
}
}
}
return null;
}
private RobotDevice() {

View File

@@ -4,7 +4,12 @@ import li.cil.oc2.common.entity.RobotEntity;
import net.minecraft.nbt.CompoundNBT;
public abstract class AbstractRobotAction {
private static final String ID_TAG_NAME = "id";
///////////////////////////////////////////////////////////////////
private final AbstractRobotActionType type;
private int id;
///////////////////////////////////////////////////////////////////
@@ -23,17 +28,28 @@ public abstract class AbstractRobotAction {
return type;
}
public int getId() {
return id;
}
public void setId(final int value) {
id = value;
}
public void initialize(final RobotEntity robot) {
}
public abstract boolean perform(RobotEntity robot);
public abstract RobotActionResult perform(RobotEntity robot);
public CompoundNBT serialize() {
return new CompoundNBT();
final CompoundNBT tag = new CompoundNBT();
tag.putInt(ID_TAG_NAME, id);
return tag;
}
///////////////////////////////////////////////////////////////////
protected void deserialize(final CompoundNBT tag) {
public void deserialize(final CompoundNBT tag) {
id = tag.getInt(ID_TAG_NAME);
}
}

View File

@@ -0,0 +1,7 @@
package li.cil.oc2.common.entity.robot;
public enum RobotActionResult {
INCOMPLETE,
SUCCESS,
FAILURE
}

View File

@@ -12,6 +12,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import javax.annotation.Nullable;
import java.util.Objects;
public final class RobotMovementAction extends AbstractRobotAction {
public static final double TARGET_EPSILON = 0.0001;
@@ -21,14 +22,17 @@ public final class RobotMovementAction extends AbstractRobotAction {
private static final float MOVEMENT_SPEED = 1f / Constants.TICK_SECONDS; // In blocks per second.
private static final String DIRECTION_TAG_NAME = "direction";
private static final String ORIGIN_TAG_NAME = "origin";
private static final String START_TAG_NAME = "start";
private static final String TARGET_TAG_NAME = "start";
private static final String TARGET_TAG_NAME = "target";
///////////////////////////////////////////////////////////////////
private MovementDirection direction;
@Nullable private BlockPos origin;
@Nullable private BlockPos start;
@Nullable private Vector3d target;
@Nullable private BlockPos target;
@Nullable private Vector3d targetPos;
///////////////////////////////////////////////////////////////////
@@ -57,82 +61,94 @@ public final class RobotMovementAction extends AbstractRobotAction {
robot.move(MoverType.SELF, delta);
}
///////////////////////////////////////////////////////////////////
@Override
public void initialize(final RobotEntity robot) {
if (target == null) {
start = robot.getPosition();
BlockPos targetPosition = start;
if (origin == null || start == null || target == null) {
origin = robot.getPosition();
start = origin;
target = start;
switch (direction) {
case UP:
targetPosition = targetPosition.offset(Direction.UP);
target = target.offset(Direction.UP);
break;
case DOWN:
targetPosition = targetPosition.offset(Direction.DOWN);
target = target.offset(Direction.DOWN);
break;
case FORWARD:
targetPosition = targetPosition.offset(robot.getHorizontalFacing());
target = target.offset(robot.getHorizontalFacing());
break;
case BACKWARD:
targetPosition = targetPosition.offset(robot.getHorizontalFacing().getOpposite());
target = target.offset(robot.getHorizontalFacing().getOpposite());
break;
}
target = getTargetPositionInBlock(targetPosition);
}
robot.getDataManager().set(RobotEntity.TARGET_POSITION, new BlockPos(target));
targetPos = getTargetPositionInBlock(target);
robot.getDataManager().set(RobotEntity.TARGET_POSITION, target);
}
@Override
public boolean perform(final RobotEntity robot) {
if (target == null) {
return true;
public RobotActionResult perform(final RobotEntity robot) {
if (targetPos == null) {
throw new IllegalStateException();
}
moveTowards(robot, target);
moveTowards(robot, targetPos);
final boolean didCollide = robot.collidedHorizontally || robot.collidedVertically;
if (didCollide && !robot.getEntityWorld().isRemote()) {
if (start != null) {
target = getTargetPositionInBlock(start);
robot.getDataManager().set(RobotEntity.TARGET_POSITION, start);
final BlockPos newStart = target;
target = start;
start = newStart;
targetPos = getTargetPositionInBlock(target);
robot.getDataManager().set(RobotEntity.TARGET_POSITION, target);
}
start = null;
if (robot.getPositionVec().squareDistanceTo(targetPos) < TARGET_EPSILON) {
if (Objects.equals(target, origin)) {
return RobotActionResult.FAILURE; // Collided and returned.
} else {
// todo if it's a block, try to break it. or just drop ourselves?
return RobotActionResult.SUCCESS; // Made it to new location.
}
}
return robot.getPositionVec().squareDistanceTo(target) < TARGET_EPSILON;
return RobotActionResult.INCOMPLETE;
}
///////////////////////////////////////////////////////////////////
@Override
public CompoundNBT serialize() {
final CompoundNBT tag = super.serialize();
NBTUtils.putEnum(tag, DIRECTION_TAG_NAME, direction);
if (origin != null) {
NBTUtils.putBlockPos(tag, ORIGIN_TAG_NAME, origin);
}
if (start != null) {
NBTUtils.putBlockPos(tag, START_TAG_NAME, start);
}
if (target != null) {
NBTUtils.putVector3d(tag, TARGET_TAG_NAME, target);
NBTUtils.putBlockPos(tag, TARGET_TAG_NAME, target);
}
return tag;
}
@Override
protected void deserialize(final CompoundNBT tag) {
public void deserialize(final CompoundNBT tag) {
super.deserialize(tag);
direction = NBTUtils.getEnum(tag, DIRECTION_TAG_NAME, MovementDirection.class);
if (tag.contains(ORIGIN_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
origin = NBTUtils.getBlockPos(tag, ORIGIN_TAG_NAME);
}
if (tag.contains(START_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
start = NBTUtils.getBlockPos(tag, START_TAG_NAME);
}
if (tag.contains(TARGET_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
target = NBTUtils.getVector3d(tag, TARGET_TAG_NAME);
target = NBTUtils.getBlockPos(tag, TARGET_TAG_NAME);
targetPos = getTargetPositionInBlock(target);
}
}
}

View File

@@ -43,6 +43,8 @@ public final class RobotRotationAction extends AbstractRobotAction {
robot.rotationYaw = MathHelper.approachDegrees(robot.rotationYaw, targetRotation.getHorizontalAngle(), ROTATION_SPEED);
}
///////////////////////////////////////////////////////////////////
@Override
public void initialize(final RobotEntity robot) {
if (target == null) {
@@ -61,17 +63,19 @@ public final class RobotRotationAction extends AbstractRobotAction {
}
@Override
public boolean perform(final RobotEntity robot) {
public RobotActionResult perform(final RobotEntity robot) {
if (target == null) {
return true;
throw new IllegalStateException();
}
rotateTowards(robot, target);
return MathHelper.degreesDifferenceAbs(robot.rotationYaw, target.getHorizontalAngle()) < TARGET_EPSILON;
}
if (MathHelper.degreesDifferenceAbs(robot.rotationYaw, target.getHorizontalAngle()) < TARGET_EPSILON) {
return RobotActionResult.SUCCESS;
}
///////////////////////////////////////////////////////////////////
return RobotActionResult.INCOMPLETE;
}
@Override
public CompoundNBT serialize() {
@@ -86,7 +90,7 @@ public final class RobotRotationAction extends AbstractRobotAction {
}
@Override
protected void deserialize(final CompoundNBT tag) {
public void deserialize(final CompoundNBT tag) {
super.deserialize(tag);
direction = NBTUtils.getEnum(tag, DIRECTION_TAG_NAME, RotationDirection.class);