Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.ryanhcode.sable.mixin.entity.bee;

import dev.ryanhcode.sable.mixinhelpers.entity.bee.BeeSableHooks;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Position;
import net.minecraft.world.entity.animal.Bee;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeeEnterHiveGoal")
public abstract class BeeEnterHiveGoalMixin {
@Shadow
@Final
private Bee this$0;

@Redirect(method = "canBeeUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;closerToCenterThan(Lnet/minecraft/core/Position;D)Z"))
private boolean sable$canEnterHive(final BlockPos hivePos, final Position position, final double distance) {
return BeeSableHooks.closerThan(this.this$0, hivePos, distance);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.ryanhcode.sable.mixin.entity.bee;

import dev.ryanhcode.sable.mixinhelpers.entity.bee.BeeSableHooks;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.animal.Bee;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.List;

@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeeLocateHiveGoal")
public abstract class BeeLocateHiveGoalMixin {
@Shadow
@Final
private Bee this$0;

@Inject(method = "findNearbyHivesWithSpace", at = @At("HEAD"), cancellable = true)
private void sable$findNearbyHivesWithSpace(final CallbackInfoReturnable<List<BlockPos>> cir) {
cir.setReturnValue(BeeSableHooks.findNearbyHivesWithSpace(this.this$0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.ryanhcode.sable.mixin.entity.bee;

import dev.ryanhcode.sable.mixinhelpers.entity.bee.BeeSableHooks;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.animal.Bee;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(Bee.class)
public abstract class BeeMixin {
@Inject(method = "closerThan", at = @At("HEAD"), cancellable = true)
private void sable$closerThan(final BlockPos pos, final int distance, final CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(BeeSableHooks.closerThan((Bee) (Object) this, pos, distance));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.ryanhcode.sable.mixin.entity.bee;

import dev.ryanhcode.sable.mixinhelpers.entity.bee.BeeSableHooks;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.animal.Bee;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.Optional;
import java.util.function.Predicate;

@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeePollinateGoal")
public abstract class BeePollinateGoalMixin {
@Shadow
@Final
private Bee this$0;

@Shadow
@Final
private Predicate<BlockState> VALID_POLLINATION_BLOCKS;

@Inject(method = "findNearbyFlower", at = @At("HEAD"), cancellable = true)
private void sable$findNearbyFlower(final CallbackInfoReturnable<Optional<BlockPos>> cir) {
cir.setReturnValue(BeeSableHooks.findNearestFlower(this.this$0, this.VALID_POLLINATION_BLOCKS, 5.0));
}

@Inject(method = "canBeeContinueToUse", at = @At("HEAD"), cancellable = true)
private void sable$stopPollinatingAtNight(final CallbackInfoReturnable<Boolean> cir) {
if (this.this$0.level().isNight()) {
cir.setReturnValue(false);
}
}

@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;atBottomCenterOf(Lnet/minecraft/core/Vec3i;)Lnet/minecraft/world/phys/Vec3;"))
private Vec3 sable$projectFlowerHoverTarget(final Vec3i pos) {
return BeeSableHooks.projectHoverTarget(this.this$0, new BlockPos(pos.getX(), pos.getY(), pos.getZ()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package dev.ryanhcode.sable.mixinhelpers.entity.bee;

import dev.ryanhcode.sable.Sable;
import dev.ryanhcode.sable.companion.SubLevelAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Position;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.PoiTypeTags;
import net.minecraft.world.entity.animal.Bee;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.Vec3;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class BeeSableHooks {
private BeeSableHooks() {
}

public static boolean closerThan(final Bee bee, final BlockPos pos, final int distance) {
return closerThan(bee, pos, (double) distance);
}

public static boolean closerThan(final Bee bee, final BlockPos pos, final double distance) {
final double maxDistanceSquared = distance * distance;
return Sable.HELPER.distanceSquaredWithSubLevels(bee.level(), pos.getCenter(), bee.position()) < maxDistanceSquared;
}

public static List<BlockPos> findNearbyHivesWithSpace(final Bee bee) {
final Level level = bee.level();
if (!(level instanceof ServerLevel serverLevel)) {
return List.of();
}

final SubLevelAccess trackingSubLevel = Sable.HELPER.getTrackingOrVehicleSubLevel(bee);
final Position origin = getSearchOrigin(bee, trackingSubLevel);
final List<BlockPos> hives = Sable.HELPER.runIncludingSubLevels(level, origin, true, trackingSubLevel,
(subLevel, candidateOrigin) -> findNearbyHivesWithSpace(serverLevel, bee, candidateOrigin));
return hives == null ? List.of() : hives;
}

public static Optional<BlockPos> findNearestFlower(final Bee bee, final Predicate<BlockState> predicate, final double range) {
final SubLevelAccess trackingSubLevel = Sable.HELPER.getTrackingOrVehicleSubLevel(bee);
final Position origin = getSearchOrigin(bee, trackingSubLevel);
final Optional<BlockPos> flower = Sable.HELPER.runIncludingSubLevels(bee.level(), origin, true, trackingSubLevel,
(subLevel, candidateOrigin) -> findNearestBlock(bee, predicate, range, candidateOrigin));
return flower == null ? Optional.empty() : flower;
}

public static Vec3 projectHoverTarget(final Bee bee, final BlockPos flowerPos) {
return Sable.HELPER.projectOutOfSubLevel(bee.level(), Vec3.atBottomCenterOf(flowerPos));
}

private static Position getSearchOrigin(final Bee bee, final SubLevelAccess trackingSubLevel) {
if (trackingSubLevel == null) {
return bee.position();
}

return trackingSubLevel.logicalPose().transformPositionInverse(bee.position());
}

private static List<BlockPos> findNearbyHivesWithSpace(final ServerLevel level, final Bee bee, final BlockPos origin) {
final PoiManager poiManager = level.getPoiManager();
final Stream<PoiRecord> records = poiManager.getInRange(record -> record.is(PoiTypeTags.BEE_HOME), origin, 20, PoiManager.Occupancy.ANY);
final List<BlockPos> hives = records.map(PoiRecord::getPos)
.filter(pos -> hasHiveSpace(level, pos))
.sorted(Comparator.comparingDouble(pos -> Sable.HELPER.distanceSquaredWithSubLevels(bee.level(), pos.getCenter(), bee.position())))
.collect(Collectors.toList());
return hives.isEmpty() ? null : hives;
}

private static boolean hasHiveSpace(final Level level, final BlockPos pos) {
if (!level.getBlockState(pos).is(BlockTags.BEEHIVES)) {
return false;
}

final BlockEntity blockEntity = level.getBlockEntity(pos);
return blockEntity instanceof BeehiveBlockEntity beehive && !beehive.isFull();
}

private static Optional<BlockPos> findNearestBlock(final Bee bee, final Predicate<BlockState> predicate, final double range, final BlockPos origin) {
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();

for (int y = 0; (double) y <= range; y = y > 0 ? -y : 1 - y) {
for (int radius = 0; (double) radius < range; radius++) {
for (int x = 0; x <= radius; x = x > 0 ? -x : 1 - x) {
for (int z = x < radius && x > -radius ? radius : 0; z <= radius; z = z > 0 ? -z : 1 - z) {
mutablePos.setWithOffset(origin, x, y - 1, z);
if (isWithinSableAwareRange(bee.level(), bee.position(), mutablePos.getCenter(), range)
&& predicate.test(bee.level().getBlockState(mutablePos))) {
return Optional.of(mutablePos.immutable());
}
}
}
}
}

return Optional.empty();
}

private static boolean isWithinSableAwareRange(final Level level, final Position origin, final Position candidate, final double range) {
return Sable.HELPER.distanceSquaredWithSubLevels(level, origin, candidate) < range * range;
}
}
4 changes: 4 additions & 0 deletions common/src/main/resources/sable.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@
"death_message.EntityMixin",
"enchanting_table.EnchantingTableBlockEntityMixin",
"entity.arrows_hit_blocks.AbstractArrowMixin",
"entity.bee.BeeEnterHiveGoalMixin",
"entity.bee.BeeLocateHiveGoalMixin",
"entity.bee.BeeMixin",
"entity.bee.BeePollinateGoalMixin",
"entity.entities_in_blocks.EntityMixin",
"entity.entities_stick_sublevels.EntityMixin",
"entity.entities_stick_sublevels.LivingEntityMixin",
Expand Down