Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

full damage utils refactor #4083

Merged
merged 68 commits into from
Jan 30, 2024
Merged
Changes from 25 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
c5462c3
optimize safestep
RacoonDog Sep 13, 2023
27548ff
shrimplify
RacoonDog Sep 13, 2023
9639c8a
abstract away `Explosion`
RacoonDog Sep 13, 2023
6bdc075
just hashmap stuff
RacoonDog Sep 13, 2023
6fed411
small stuff
RacoonDog Sep 13, 2023
28d7f6c
rework attack damage
RacoonDog Sep 14, 2023
0b6fca8
optimize raycasting + unlerp
RacoonDog Sep 14, 2023
29e6481
docs
RacoonDog Sep 14, 2023
e1aac25
bring back the mutable vec >:3
RacoonDog Sep 14, 2023
ead2b47
deprecated method + deduplicate ops
RacoonDog Sep 14, 2023
1fbf9e7
docs
RacoonDog Sep 14, 2023
cb0d7c8
remove anchoraura allocations
RacoonDog Sep 14, 2023
148616a
safety
RacoonDog Sep 14, 2023
52382c0
deprecate `DamageEvent.amount`
RacoonDog Sep 14, 2023
d976652
fix damage calc target
RacoonDog Sep 14, 2023
707d433
fix inaccurate damage calculations
RacoonDog Sep 15, 2023
47fb83e
docs
RacoonDog Sep 15, 2023
049dbe8
add trident
RacoonDog Sep 17, 2023
8a04f42
correctly track entity attributes
RacoonDog Sep 17, 2023
cf77f56
allow hostile mobs as attackers
RacoonDog Sep 17, 2023
416d1ee
attribute manager
RacoonDog Sep 18, 2023
9474c23
Update EntityAttributeManager.java
RacoonDog Sep 18, 2023
ea306bb
romp
RacoonDog Sep 18, 2023
51b8f56
mob damage difficulty modifier
RacoonDog Sep 18, 2023
fdc10a1
mob specific enchant damage
RacoonDog Sep 18, 2023
a95473e
raycast use collision shape
RacoonDog Sep 19, 2023
59d2317
fix only getting damage attribute
RacoonDog Sep 22, 2023
f366517
special-case shulker armor
RacoonDog Sep 22, 2023
f0a9f91
remove deprecated methods & fields
RacoonDog Sep 22, 2023
1613230
docs
RacoonDog Sep 22, 2023
5102944
fix armor reductions
RacoonDog Sep 22, 2023
3a8c6d4
blockpos stuff
RacoonDog Sep 25, 2023
cbcb776
what did eclipse mean by this
RacoonDog Sep 26, 2023
94de41a
handle tracked attributes
RacoonDog Sep 26, 2023
a8a02f8
DO **NOT** TRUST THE GAME
RacoonDog Sep 27, 2023
375db78
properly handle tracked attributes
RacoonDog Sep 27, 2023
aa8d59b
compute status effects
RacoonDog Oct 2, 2023
21f6a7e
these methods are identical lole
RacoonDog Oct 2, 2023
1c4a11f
bring back damage source
RacoonDog Oct 2, 2023
805a021
use float instead of double
RacoonDog Oct 2, 2023
53f37ca
explosion refactor + fall damage
RacoonDog Oct 3, 2023
c0fc990
overshoot instead of undershoot
RacoonDog Oct 3, 2023
b9be976
surface check
RacoonDog Oct 3, 2023
aec2f4a
Update DamageUtils.java
RacoonDog Nov 2, 2023
bcd5144
Update CrystalAura.java
RacoonDog Nov 2, 2023
baa15b0
floatation device
RacoonDog Nov 2, 2023
d7f3ed1
Update DamageUtils.java
RacoonDog Nov 2, 2023
f6223e9
Update PlayerUtils.java
RacoonDog Nov 2, 2023
fc8d5bb
remove zStep
RacoonDog Nov 2, 2023
2990273
prevent crash
RacoonDog Nov 2, 2023
074e80d
simply fall damage
RacoonDog Dec 29, 2023
a7fbd49
move damageutils to utils.entity
RacoonDog Dec 29, 2023
232bc94
unused imports
RacoonDog Jan 15, 2024
e3c1c9e
use tracked data
RacoonDog Jan 15, 2024
1254ddc
beacon entries
RacoonDog Jan 15, 2024
e211ac7
i am cooking
RacoonDog Jan 15, 2024
9f1cc23
rename method
RacoonDog Jan 15, 2024
4fa6471
imports
RacoonDog Jan 15, 2024
7cc4e7f
Update StatusEffectBruteForce.java
RacoonDog Jan 15, 2024
c25354f
Merge branch 'master' into crystal-damage-calcs
RacoonDog Jan 18, 2024
dc1cd1d
update to 1.20.4
RacoonDog Jan 18, 2024
a94ba9c
rename method
RacoonDog Jan 18, 2024
cbbd5ff
fix clinit
RacoonDog Jan 18, 2024
bd08c9a
boingus
RacoonDog Jan 18, 2024
01f574a
yearg
RacoonDog Jan 18, 2024
0fb7edb
Update StatusEffectBruteForce max depth
Wide-Cat Jan 30, 2024
31bd513
the j
Wide-Cat Jan 30, 2024
ae80c02
must have misread
Wide-Cat Jan 30, 2024
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
@@ -13,12 +13,12 @@ public class DamageEvent {

public LivingEntity entity;
public DamageSource source;
public float amount;
@Deprecated(forRemoval = true)
public final float amount = 0;

public static DamageEvent get(LivingEntity entity, DamageSource source, float amount) {
public static DamageEvent get(LivingEntity entity, DamageSource source) {
INSTANCE.entity = entity;
INSTANCE.source = source;
INSTANCE.amount = amount;
return INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ private void onPushOutOfBlocks(double x, double d, CallbackInfo info) {

@Inject(method = "damage", at = @At("HEAD"))
private void onDamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) {
if (Utils.canUpdate() && getWorld().isClient && canTakeDamage()) MeteorClient.EVENT_BUS.post(DamageEvent.get(this, source, amount));
if (Utils.canUpdate() && getWorld().isClient && canTakeDamage()) MeteorClient.EVENT_BUS.post(DamageEvent.get(this, source));
}

@ModifyConstant(method = "canSprint", constant = @Constant(floatValue = 6.0f))
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ public LivingEntityMixin(EntityType<?> type, World world) {
@Inject(method = "damage", at = @At("HEAD"))
private void onDamageHead(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) {
if (Utils.canUpdate() && getWorld().isClient)
MeteorClient.EVENT_BUS.post(DamageEvent.get((LivingEntity) (Object) this, source, amount));
MeteorClient.EVENT_BUS.post(DamageEvent.get((LivingEntity) (Object) this, source));
}

@ModifyReturnValue(method = "canWalkOnFluid", at = @At("RETURN"))
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.Nullable;

public class AnchorAura extends Module {
private final SettingGroup sgGeneral = settings.getDefaultGroup();
@@ -225,6 +226,7 @@ public class AnchorAura extends Module {
private int placeDelayLeft;
private int breakDelayLeft;
private PlayerEntity target;
private final BlockPos.Mutable mutable = new BlockPos.Mutable();

public AnchorAura() {
super(Categories.Combat, "anchor-aura", "Automatically places and breaks Respawn Anchors to harm entities.");
@@ -264,7 +266,8 @@ private void onTick(TickEvent.Post event) {
breakDelayLeft = 0;

if (rotationMode.get() == RotationMode.Both || rotationMode.get() == RotationMode.Break) {
Rotations.rotate(Rotations.getYaw(breakPos), Rotations.getPitch(breakPos), 50, () -> breakAnchor(breakPos, anchor, glowStone));
BlockPos immutableBreakPos = breakPos.toImmutable();
Rotations.rotate(Rotations.getYaw(breakPos), Rotations.getPitch(breakPos), 50, () -> breakAnchor(immutableBreakPos, anchor, glowStone));
} else breakAnchor(breakPos, anchor, glowStone);
}
}
@@ -274,7 +277,7 @@ private void onTick(TickEvent.Post event) {

if (placePos != null) {
placeDelayLeft = 0;
BlockUtils.place(placePos, anchor, (rotationMode.get() == RotationMode.Place || rotationMode.get() == RotationMode.Both), 50);
BlockUtils.place(placePos.toImmutable(), anchor, (rotationMode.get() == RotationMode.Place || rotationMode.get() == RotationMode.Both), 50);
}
}

@@ -301,48 +304,50 @@ private void onRender(Render3DEvent event) {
}
}

@Nullable
private BlockPos findPlacePos(BlockPos targetPlacePos) {
switch (placePositions.get()) {
case All:
if (isValidPlace(targetPlacePos.down())) return targetPlacePos.down();
else if (isValidPlace(targetPlacePos.up(2))) return targetPlacePos.up(2);
else if (isValidPlace(targetPlacePos.add(1, 0, 0))) return targetPlacePos.add(1, 0, 0);
else if (isValidPlace(targetPlacePos.add(-1, 0, 0))) return targetPlacePos.add(-1, 0, 0);
else if (isValidPlace(targetPlacePos.add(0, 0, 1))) return targetPlacePos.add(0, 0, 1);
else if (isValidPlace(targetPlacePos.add(0, 0, -1))) return targetPlacePos.add(0, 0, -1);
else if (isValidPlace(targetPlacePos.add(1, 1, 0))) return targetPlacePos.add(1, 1, 0);
else if (isValidPlace(targetPlacePos.add(-1, -1, 0))) return targetPlacePos.add(-1, -1, 0);
else if (isValidPlace(targetPlacePos.add(0, 1, 1))) return targetPlacePos.add(0, 1, 1);
else if (isValidPlace(targetPlacePos.add(0, 0, -1))) return targetPlacePos.add(0, 0, -1);
break;
case Above:
if (isValidPlace(targetPlacePos.up(2))) return targetPlacePos.up(2);
break;
case AboveAndBelow:
if (isValidPlace(targetPlacePos.down())) return targetPlacePos.down();
else if (isValidPlace(targetPlacePos.up(2))) return targetPlacePos.up(2);
break;
case Around:
if (isValidPlace(targetPlacePos.north())) return targetPlacePos.north();
else if (isValidPlace(targetPlacePos.east())) return targetPlacePos.east();
else if (isValidPlace(targetPlacePos.west())) return targetPlacePos.west();
else if (isValidPlace(targetPlacePos.south())) return targetPlacePos.south();
break;
case All -> {
if (isValidPlace(BlockUtils.mutateDown(mutable, targetPlacePos))) return mutable;
else if (isValidPlace(BlockUtils.mutateUp(mutable, targetPlacePos, 2))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 1, 0, 0))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, -1, 0, 0))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 0, 0, 1))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 0, 0, -1))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 1, 1, 0))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, -1, -1, 0))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 0, 1, 1))) return mutable;
else if (isValidPlace(BlockUtils.mutateAround(mutable, targetPlacePos, 0, 0, -1))) return mutable;
}
case Above -> {
if (isValidPlace(BlockUtils.mutateUp(mutable, targetPlacePos, 2))) return mutable;
}
case AboveAndBelow -> {
if (isValidPlace(BlockUtils.mutateDown(mutable, targetPlacePos))) return mutable;
else if (isValidPlace(BlockUtils.mutateUp(mutable, targetPlacePos, 2))) return mutable;
}
case Around -> {
if (isValidPlace(BlockUtils.mutateNorth(mutable, targetPlacePos))) return mutable;
else if (isValidPlace(BlockUtils.mutateEast(mutable, targetPlacePos))) return mutable;
else if (isValidPlace(BlockUtils.mutateWest(mutable, targetPlacePos))) return mutable;
else if (isValidPlace(BlockUtils.mutateSouth(mutable, targetPlacePos))) return mutable;
}
}
return null;
}

@Nullable
private BlockPos findBreakPos(BlockPos targetPos) {
if (isValidBreak(targetPos.down())) return targetPos.down();
else if (isValidBreak(targetPos.up(2))) return targetPos.up(2);
else if (isValidBreak(targetPos.add(1, 0, 0))) return targetPos.add(1, 0, 0);
else if (isValidBreak(targetPos.add(-1, 0, 0))) return targetPos.add(-1, 0, 0);
else if (isValidBreak(targetPos.add(0, 0, 1))) return targetPos.add(0, 0, 1);
else if (isValidBreak(targetPos.add(0, 0, -1))) return targetPos.add(0, 0, -1);
else if (isValidBreak(targetPos.add(1, 1, 0))) return targetPos.add(1, 1, 0);
else if (isValidBreak(targetPos.add(-1, -1, 0))) return targetPos.add(-1, -1, 0);
else if (isValidBreak(targetPos.add(0, 1, 1))) return targetPos.add(0, 1, 1);
else if (isValidBreak(targetPos.add(0, 0, -1))) return targetPos.add(0, 0, -1);
if (isValidBreak(BlockUtils.mutateDown(mutable, targetPos))) return mutable;
else if (isValidBreak(BlockUtils.mutateUp(mutable, targetPos, 2))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 1, 0, 0))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, -1, 0, 0))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 0, 0, 1))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 0, 0, -1))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 1, 1, 0))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, -1, -1, 0))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 0, 1, 1))) return mutable;
else if (isValidBreak(BlockUtils.mutateAround(mutable, targetPos, 0, 0, -1))) return mutable;
return null;
}

Original file line number Diff line number Diff line change
@@ -113,13 +113,13 @@ private void onTick(TickEvent.Post event) {
}

for (Entity entity : mc.world.getEntities()) {
if (entity instanceof PlayerEntity && entity.getUuid() != mc.player.getUuid()) {
if (onlyTrusted.get() && entity != mc.player && !Friends.get().isFriend((PlayerEntity) entity)) {
if (entity instanceof PlayerEntity player && player != mc.player) {
if (onlyTrusted.get() && !Friends.get().isFriend(player)) {
mc.player.networkHandler.onDisconnect(new DisconnectS2CPacket(Text.literal("[AutoLog] A non-trusted player appeared in your render distance.")));
if (toggleOff.get()) this.toggle();
break;
}
if (PlayerUtils.isWithin(entity, 8) && instantDeath.get() && DamageUtils.getSwordDamage((PlayerEntity) entity, true)
if (PlayerUtils.isWithin(player, 8) && instantDeath.get() && DamageUtils.getAttackDamage(player, mc.player)
> playerHealth + mc.player.getAbsorptionAmount()) {
mc.player.networkHandler.onDisconnect(new DisconnectS2CPacket(Text.literal("[AutoLog] Anti-32k measures.")));
if (toggleOff.get()) this.toggle();
Original file line number Diff line number Diff line change
@@ -16,8 +16,7 @@
import net.minecraft.entity.Entity;
import net.minecraft.entity.decoration.EndCrystalEntity;

import java.util.Comparator;
import java.util.Optional;
import java.util.OptionalDouble;

public class Step extends Module {
private final SettingGroup sgGeneral = settings.getDefaultGroup();
@@ -91,13 +90,13 @@ private float getHealth(){
return mc.player.getHealth() + mc.player.getAbsorptionAmount();
}

private double getExplosionDamage(){
Optional<EndCrystalEntity> crystal = Streams.stream(mc.world.getEntities())
private double getExplosionDamage() {
OptionalDouble crystalDamage = Streams.stream(mc.world.getEntities())
.filter(entity -> entity instanceof EndCrystalEntity)
.filter(Entity::isAlive)
.max(Comparator.comparingDouble(o -> DamageUtils.crystalDamage(mc.player, o.getPos())))
.map(entity -> (EndCrystalEntity) entity);
return crystal.map(endCrystalEntity -> DamageUtils.crystalDamage(mc.player, endCrystalEntity.getPos())).orElse(0.0);
.mapToDouble(entity -> DamageUtils.crystalDamage(mc.player, entity.getPos()))
.max();
return crystalDamage.orElse(0.0);
}

public enum ActiveWhen {
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client).
* Copyright (c) Meteor Development.
*/

package meteordevelopment.meteorclient.utils.entity;

import com.google.common.collect.Multimap;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.*;
import net.minecraft.item.ItemStack;

import static meteordevelopment.meteorclient.MeteorClient.mc;

public class EntityAttributeManager {
/**
* @see LivingEntity#getAttributes()
*/
public static AttributeContainer getAttributes(LivingEntity entity) {
if (entity == mc.player) return entity.getAttributes();

@SuppressWarnings("unchecked")
AttributeContainer attributes = new AttributeContainer(DefaultAttributeRegistry.get((EntityType<? extends LivingEntity>) entity.getType()));

// Equipment
for (var equipmentSlot : EquipmentSlot.values()) {
ItemStack stack = entity.getEquippedStack(equipmentSlot);
attributes.addTemporaryModifiers(stack.getAttributeModifiers(equipmentSlot));
}

// Status effects
for (var statusEffect : entity.getStatusEffects()) {
statusEffect.getEffectType().onApplied(entity, attributes, statusEffect.getAmplifier());
}

return attributes;
}

/**
* @see LivingEntity#getAttributeInstance(EntityAttribute)
*/
public static EntityAttributeInstance getAttributeInstance(LivingEntity entity, EntityAttribute attribute) {
if (entity == mc.player) return entity.getAttributeInstance(attribute);

@SuppressWarnings("unchecked")
double baseValue = DefaultAttributeRegistry.get((EntityType<? extends LivingEntity>) entity.getType()).getBaseValue(attribute);
EntityAttributeInstance attributeInstance = new EntityAttributeInstance(attribute, o1 -> {});
attributeInstance.setBaseValue(baseValue);

// Equipment
for (var equipmentSlot : EquipmentSlot.values()) {
ItemStack stack = entity.getEquippedStack(equipmentSlot);
Multimap<EntityAttribute, EntityAttributeModifier> modifiers = stack.getAttributeModifiers(equipmentSlot);
for (var modifier : modifiers.get(attribute)) attributeInstance.addTemporaryModifier(modifier);
}

// Status effects
for (var statusEffect : entity.getStatusEffects()) {
EntityAttributeModifier modifier = statusEffect.getEffectType().getAttributeModifiers().get(EntityAttributes.GENERIC_ATTACK_DAMAGE);
if (modifier == null) continue;
attributeInstance.addTemporaryModifier(new EntityAttributeModifier(modifier.getId(), statusEffect.getTranslationKey() + " " + statusEffect.getAmplifier(), statusEffect.getEffectType().adjustModifierAmount(statusEffect.getAmplifier(), modifier), modifier.getOperation()));
}

return attributeInstance;
}

/**
* @see LivingEntity#getAttributeValue(EntityAttribute)
*/
public static double getAttributeValue(LivingEntity entity, EntityAttribute attribute) {
if (entity == mc.player) return entity.getAttributeValue(attribute);

return getAttributeInstance(entity, attribute).getValue();
}
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
package meteordevelopment.meteorclient.utils.entity.fakeplayer;

import com.mojang.authlib.GameProfile;
import meteordevelopment.meteorclient.utils.entity.EntityAttributeManager;
import net.minecraft.client.network.OtherClientPlayerEntity;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.entity.player.PlayerEntity;
@@ -33,7 +34,7 @@ public FakePlayerEntity(PlayerEntity player, String name, float health, boolean
Byte playerModel = player.getDataTracker().get(PlayerEntity.PLAYER_MODEL_PARTS);
dataTracker.set(PlayerEntity.PLAYER_MODEL_PARTS, playerModel);

getAttributes().setFrom(player.getAttributes());
getAttributes().setFrom(EntityAttributeManager.getAttributes(player));
setPose(player.getPose());

capeX = getX();

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -191,16 +191,14 @@ public static double possibleHealthReductions(boolean entities, boolean fall) {
if (entities) {
for (Entity entity : mc.world.getEntities()) {
// Check for end crystals
if (entity instanceof EndCrystalEntity && damageTaken < DamageUtils.crystalDamage(mc.player, entity.getPos())) {
damageTaken = DamageUtils.crystalDamage(mc.player, entity.getPos());
if (entity instanceof EndCrystalEntity) {
double crystalDamage = DamageUtils.crystalDamage(mc.player, entity.getPos());
if (crystalDamage > damageTaken) damageTaken = crystalDamage;
}
// Check for players holding swords
else if (entity instanceof PlayerEntity && damageTaken < DamageUtils.getSwordDamage((PlayerEntity) entity, true)) {
if (!Friends.get().isFriend((PlayerEntity) entity) && isWithin(entity, 5)) {
if (((PlayerEntity) entity).getActiveItem().getItem() instanceof SwordItem) {
damageTaken = DamageUtils.getSwordDamage((PlayerEntity) entity, true);
}
}
else if (entity instanceof PlayerEntity player && !Friends.get().isFriend(player) && isWithin(entity, 5)) {
double attackDamage = DamageUtils.getAttackDamage(player, mc.player);
if (attackDamage > damageTaken) damageTaken = attackDamage;
}
}

@@ -210,8 +208,9 @@ else if (entity instanceof PlayerEntity && damageTaken < DamageUtils.getSwordDam
BlockPos bp = blockEntity.getPos();
Vec3d pos = new Vec3d(bp.getX(), bp.getY(), bp.getZ());

if (blockEntity instanceof BedBlockEntity && damageTaken < DamageUtils.bedDamage(mc.player, pos)) {
damageTaken = DamageUtils.bedDamage(mc.player, pos);
if (blockEntity instanceof BedBlockEntity) {
double explosionDamage = DamageUtils.bedDamage(mc.player, pos);
if (explosionDamage > damageTaken) damageTaken = explosionDamage;
}
}
}
Original file line number Diff line number Diff line change
@@ -336,4 +336,42 @@ private static double getBlockBreakingSpeed(int slot, BlockState block) {

return speed;
}

/** {@link BlockPos.Mutable} mutations around an origin */

public static BlockPos.Mutable mutateAround(BlockPos.Mutable mutable, BlockPos origin, int xOffset, int yOffset, int zOffset) {
return mutable.set(origin.getX() + xOffset, origin.getY() + yOffset, origin.getZ() + zOffset);
}

public static BlockPos.Mutable mutateDown(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX(), origin.getY() - 1, origin.getZ());
}

public static BlockPos.Mutable mutateDown(BlockPos.Mutable mutable, BlockPos origin, int blocks) {
return mutable.set(origin.getX(), origin.getY() - blocks, origin.getZ());
}

public static BlockPos.Mutable mutateUp(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX(), origin.getY() + 1, origin.getZ());
}

public static BlockPos.Mutable mutateUp(BlockPos.Mutable mutable, BlockPos origin, int blocks) {
return mutable.set(origin.getX(), origin.getY() + blocks, origin.getZ());
}

public static BlockPos.Mutable mutateNorth(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX(), origin.getY(), origin.getZ() - 1);
}

public static BlockPos.Mutable mutateSouth(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX(), origin.getY(), origin.getZ() + 1);
}

public static BlockPos.Mutable mutateWest(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX() - 1, origin.getY(), origin.getZ());
}

public static BlockPos.Mutable mutateEast(BlockPos.Mutable mutable, BlockPos origin) {
return mutable.set(origin.getX() + 1, origin.getY(), origin.getZ());
}
}