Skip to content

Commit

Permalink
Rewrite subscription system to reduce code complexity
Browse files Browse the repository at this point in the history
This is 1:1 the same behaviour as before, but split up into separate subscription records, so it's a lot easier to read.
  • Loading branch information
AlexProgrammerDE committed Nov 28, 2024
1 parent d15990c commit 8e8cea5
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 385 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public BlockBreakAction(MovementMiningCost movementMiningCost) {
@Override
public boolean isCompleted(BotConnection connection) {
var level = connection.dataManager().currentLevel();
var blockType = level.getBlockState(blockPosition).blockType();

return SFBlockHelpers.isEmptyBlock(level.getBlockState(blockPosition).blockType());
return SFBlockHelpers.isEmptyBlock(blockType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,43 +58,39 @@ public static void registerDownMovements(
callback.accept(registerDownMovement(blockSubscribers, new DownMovement()));
}

public static DownMovement registerDownMovement(
private static DownMovement registerDownMovement(
BiConsumer<SFVec3i, MinecraftGraph.MovementSubscription<?>> blockSubscribers,
DownMovement movement) {
{
for (var safetyBlock : movement.listSafetyCheckBlocks()) {
blockSubscribers
.accept(safetyBlock, new DownMovementBlockSubscription(DownMovementBlockSubscription.SubscriptionType.DOWN_SAFETY_CHECK));
.accept(safetyBlock, new DownSafetyCheckSubscription());
}
}

{
for (var obstructingBlock : movement.listObstructFallCheckBlocks()) {
blockSubscribers
.accept(obstructingBlock, new DownMovementBlockSubscription(DownMovementBlockSubscription.SubscriptionType.MOVEMENT_OBSTRUCTING_FALL_CHECK));
.accept(obstructingBlock, new ObstructingFallCheckSubscription());
}
}

{
var freeBlock = movement.blockToBreak();
blockSubscribers
.accept(freeBlock.key(), new DownMovementBlockSubscription(DownMovementBlockSubscription.SubscriptionType.MOVEMENT_FREE, 0, freeBlock.value()));
.accept(freeBlock.key(), new MovementFreeSubscription(freeBlock.value()));
}

{
var safeBlocks = movement.listCheckSafeMineBlocks();
for (var i = 0; i < safeBlocks.length; i++) {
var savedBlock = safeBlocks[i];
for (var savedBlock : safeBlocks) {
if (savedBlock == null) {
continue;
}

for (var block : savedBlock) {
blockSubscribers
.accept(block.position(), new DownMovementBlockSubscription(
DownMovementBlockSubscription.SubscriptionType.MOVEMENT_BREAK_SAFETY_CHECK,
i,
block.type()));
.accept(block.position(), new MovementBreakSafetyCheckSubscription(block.type()));
}
}
}
Expand Down Expand Up @@ -197,107 +193,94 @@ public DownMovement clone() {
}
}

record DownMovementBlockSubscription(
SubscriptionType type,
int blockArrayIndex,
BlockFace blockBreakSideHint,
BlockSafetyData.BlockSafetyType safetyType) implements MinecraftGraph.MovementSubscription<DownMovement> {
DownMovementBlockSubscription(SubscriptionType type) {
this(type, -1, null, null);
interface DownMovementSubscription extends MinecraftGraph.MovementSubscription<DownMovement> {
@Override
default DownMovement castAction(GraphAction action) {
return (DownMovement) action;
}
}

DownMovementBlockSubscription(SubscriptionType type, int blockArrayIndex, BlockFace blockBreakSideHint) {
this(type, blockArrayIndex, blockBreakSideHint, null);
}
record MovementFreeSubscription(BlockFace blockBreakSideHint) implements DownMovementSubscription {
@Override
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, DownMovement downMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
if (graph.disallowedToBreakBlock(absoluteKey)
|| graph.disallowedToBreakBlockType(blockState.blockType())) {
// No way to break this block
return MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}

DownMovementBlockSubscription(
SubscriptionType subscriptionType,
int i,
BlockSafetyData.BlockSafetyType type) {
this(subscriptionType, i, null, type);
var cacheableMiningCost = graph.inventory().getMiningCosts(graph.tagsState(), blockState);
// We can mine this block, lets add costs and continue
downMovement.breakCost = new MovementMiningCost(
absoluteKey,
cacheableMiningCost.miningCost(),
cacheableMiningCost.willDropUsableBlockItem(),
blockBreakSideHint);
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
}

record DownSafetyCheckSubscription() implements DownMovementSubscription {
@Override
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, DownMovement downMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
return switch (type) {
case MOVEMENT_FREE -> {
if (graph.disallowedToBreakBlock(absoluteKey)
|| graph.disallowedToBreakBlockType(blockState.blockType())) {
// No way to break this block
yield MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}

var cacheableMiningCost = graph.inventory().getMiningCosts(graph.tagsState(), blockState);
// We can mine this block, lets add costs and continue
downMovement.breakCost = new MovementMiningCost(
absoluteKey,
cacheableMiningCost.miningCost(),
cacheableMiningCost.willDropUsableBlockItem(),
blockBreakSideHint);
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
case DOWN_SAFETY_CHECK -> {
var yLevel = key.y;
var yLevel = key.y;

if (yLevel < downMovement.closestBlockToFallOn) {
// We already found a block to fall on, above this one
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
if (yLevel < downMovement.closestBlockToFallOn) {
// We already found a block to fall on, above this one
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
// We found a block to fall on
downMovement.closestBlockToFallOn = yLevel;
}
if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
// We found a block to fall on
downMovement.closestBlockToFallOn = yLevel;
}

yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
case MOVEMENT_BREAK_SAFETY_CHECK -> {
var unsafe = safetyType.isUnsafeBlock(blockState);
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
}

record MovementBreakSafetyCheckSubscription(BlockSafetyData.BlockSafetyType safetyType) implements DownMovementSubscription {
@Override
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, DownMovement downMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
var unsafe = safetyType.isUnsafeBlock(blockState);

if (unsafe) {
// We know already WE MUST dig the block below for this action
// So if one block around the block below is unsafe, we can't do this action
yield MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}
if (unsafe) {
// We know already WE MUST dig the block below for this action
// So if one block around the block below is unsafe, we can't do this action
return MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}

// All good, we can continue
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
case MOVEMENT_OBSTRUCTING_FALL_CHECK -> {
var yLevel = key.y;

if (yLevel < downMovement.closestObstructingBlock) {
// We already found a higher obstructing block
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

if (yLevel < downMovement.closestBlockToFallOn) {
// We only search blocks above the closest block to fall on
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

if (isFree.get()) {
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

// We found a block that obstructs our fall
downMovement.closestObstructingBlock = yLevel;
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
};
// All good, we can continue
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
}

record ObstructingFallCheckSubscription() implements DownMovementSubscription {
@Override
public DownMovement castAction(GraphAction action) {
return (DownMovement) action;
}
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, DownMovement downMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
var yLevel = key.y;

if (yLevel < downMovement.closestObstructingBlock) {
// We already found a higher obstructing block
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

if (yLevel < downMovement.closestBlockToFallOn) {
// We only search blocks above the closest block to fall on
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

if (isFree.get()) {
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

enum SubscriptionType {
MOVEMENT_FREE,
DOWN_SAFETY_CHECK,
MOVEMENT_BREAK_SAFETY_CHECK,
MOVEMENT_OBSTRUCTING_FALL_CHECK
// We found a block that obstructs our fall
downMovement.closestObstructingBlock = yLevel;
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
import com.soulfiremc.server.pathfinding.graph.BlockFace;
import com.soulfiremc.server.pathfinding.graph.GraphInstructions;
import com.soulfiremc.server.pathfinding.graph.MinecraftGraph;
import com.soulfiremc.server.pathfinding.graph.actions.movement.BlockSafetyData;
import com.soulfiremc.server.pathfinding.graph.actions.movement.ParkourDirection;
import com.soulfiremc.server.protocol.bot.BotActionManager;
import com.soulfiremc.server.util.SFBlockHelpers;
import com.soulfiremc.server.util.structs.LazyBoolean;
import it.unimi.dsi.fastutil.Pair;
Expand Down Expand Up @@ -62,21 +60,20 @@ private static ParkourMovement registerParkourMovement(
BiConsumer<SFVec3i, MinecraftGraph.MovementSubscription<?>> blockSubscribers,
ParkourMovement movement) {
{
var blockId = 0;
for (var freeBlock : movement.listRequiredFreeBlocks()) {
blockSubscribers
.accept(freeBlock.key(), new ParkourMovementBlockSubscription(ParkourMovementBlockSubscription.SubscriptionType.MOVEMENT_FREE, blockId++, freeBlock.value()));
.accept(freeBlock.key(), new MovementFreeSubscription());
}
}

{
blockSubscribers
.accept(movement.requiredUnsafeBlock(), new ParkourMovementBlockSubscription(ParkourMovementBlockSubscription.SubscriptionType.PARKOUR_UNSAFE_TO_STAND_ON));
.accept(movement.requiredUnsafeBlock(), new ParkourUnsafeToStandOnSubscription());
}

{
blockSubscribers
.accept(movement.requiredSolidBlock(), new ParkourMovementBlockSubscription(ParkourMovementBlockSubscription.SubscriptionType.MOVEMENT_SOLID));
.accept(movement.requiredSolidBlock(), new MovementSolidSubscription());
}

return movement;
Expand Down Expand Up @@ -140,62 +137,50 @@ public ParkourMovement clone() {
}
}

record ParkourMovementBlockSubscription(
SubscriptionType type,
int blockArrayIndex,
BlockFace blockBreakSideHint,
BotActionManager.BlockPlaceAgainstData blockToPlaceAgainst,
BlockSafetyData.BlockSafetyType safetyType) implements MinecraftGraph.MovementSubscription<ParkourMovement> {
ParkourMovementBlockSubscription(SubscriptionType type) {
this(type, -1, null, null, null);
interface ParkourMovementSubscription extends MinecraftGraph.MovementSubscription<ParkourMovement> {
@Override
default ParkourMovement castAction(GraphAction action) {
return (ParkourMovement) action;
}
}

record MovementFreeSubscription() implements ParkourMovementSubscription {
@Override
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, ParkourMovement parkourMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
if (isFree.get()) {
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

ParkourMovementBlockSubscription(SubscriptionType type, int blockArrayIndex, BlockFace blockBreakSideHint) {
this(type, blockArrayIndex, blockBreakSideHint, null, null);
return MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}
}

record ParkourUnsafeToStandOnSubscription() implements ParkourMovementSubscription {
@Override
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, ParkourMovement parkourMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
// We only want to jump over dangerous blocks/gaps
// So either a non-full-block like water or lava or magma
// since it hurts to stand on.
if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
return MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}

return switch (type) {
case MOVEMENT_FREE -> {
if (isFree.get()) {
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

yield MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}
case PARKOUR_UNSAFE_TO_STAND_ON -> {
// We only want to jump over dangerous blocks/gaps
// So either a non-full-block like water or lava or magma
// since it hurts to stand on.
if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
yield MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}

yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
case MOVEMENT_SOLID -> {
// Block is safe to walk on, no need to check for more
if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
yield MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

yield MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}
};
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}
}

record MovementSolidSubscription() implements ParkourMovementSubscription {
@Override
public ParkourMovement castAction(GraphAction action) {
return (ParkourMovement) action;
}
public MinecraftGraph.SubscriptionSingleResult processBlock(MinecraftGraph graph, SFVec3i key, ParkourMovement parkourMovement, LazyBoolean isFree,
BlockState blockState, SFVec3i absoluteKey) {
// Block is safe to walk on, no need to check for more
if (SFBlockHelpers.isSafeBlockToStandOn(blockState)) {
return MinecraftGraph.SubscriptionSingleResult.CONTINUE;
}

enum SubscriptionType {
MOVEMENT_FREE,
PARKOUR_UNSAFE_TO_STAND_ON,
MOVEMENT_SOLID
return MinecraftGraph.SubscriptionSingleResult.IMPOSSIBLE;
}
}
}
Loading

0 comments on commit 8e8cea5

Please sign in to comment.