Skip to content

Instantly share code, notes, and snippets.

@RecursiveG
Created December 9, 2021 05:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RecursiveG/9879277abb1448d476f20e7858d52b74 to your computer and use it in GitHub Desktop.
Save RecursiveG/9879277abb1448d476f20e7858d52b74 to your computer and use it in GitHub Desktop.
/* SPDX-License-Identifier: MIT */
package me.recursiveg.automation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.MultiPlayerGameMode;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.LogicalSide;
import java.util.Map;
public class AutoPlacement implements IAutomation {
private boolean enabled = false;
private int toolSlot = 0;
private Block used_material = null;
private BlockPos player_block_pos = null;
private int blockY;
private double blockOffsetY; // 0-1
@Override
public void enable() {
LocalPlayer p = Minecraft.getInstance().player;
if (p == null) return;
double offY = p.getY() % 1.0;
if (offY < 0.9 && offY > 0.1) {
// Standing on not complete block
blockY = (int) Math.floor(p.getY());
blockOffsetY = 0.3;
} else {
// Standing on complete block
blockY = (int) p.getY() - 1;
blockOffsetY = 0.7;
}
toolSlot = p.getInventory().selected;
Item item = p.getInventory().getSelected().getItem();
if (item instanceof BlockItem bi) {
used_material = bi.getBlock();
player_block_pos = p.blockPosition();
enabled = true;
AutomationMod.msg("AutoPlacement enabled: %s, blockY: %d, blockOffsetY: %f", enabled, blockY, blockOffsetY);
}
}
@Override
public void disable() {
enabled = false;
AutomationMod.msg("AutoPlacement enabled: %s", enabled);
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public Map<String, String> getOptions() {
return Map.of("enabled", "ro bool:" + enabled,
"blockY", "ro int:" + blockY,
"blockOffsetY", "ro double:" + blockOffsetY);
}
@Override
public void setOption(String op, String val) {
AutomationMod.msg("AutoPlacement has no options");
}
@SubscribeEvent
public void onTick(TickEvent.PlayerTickEvent e) {
if (enabled && e.side == LogicalSide.CLIENT && e.player != null && e.player == Minecraft.getInstance().player) {
if (e.player.getInventory().selected != toolSlot) {
enabled = false;
AutomationMod.msg("Tool switched. AutoPlacement disabled");
return;
}
if (e.player.blockPosition().equals(player_block_pos)) return;
player_block_pos = e.player.blockPosition();
if (e.player.getInventory().getSelected().isEmpty()) {
boolean success = tryFillItemInHand(Minecraft.getInstance().player);
if (!success) {
enabled = false;
AutomationMod.msg("Item used up! AutoPlacement disabled");
}
}
if (!tryPlacement(Minecraft.getInstance().player)) {
enabled = false;
AutomationMod.msg("Place Fail! AutoPlacement disabled");
}
}
}
public static void moveInventoryItem(int srcIdx, int dstIdx) {
LocalPlayer p = Minecraft.getInstance().player;
if (p == null) return;
NonNullList<ItemStack> a = p.getInventory().items;
MultiPlayerGameMode gm = Minecraft.getInstance().gameMode;
if (gm == null) return;
if (a.get(srcIdx).isEmpty()) return;
if (a.get(dstIdx).isEmpty()) {
gm.handleInventoryMouseClick(
/*containerId=*/0,
/*slotNum=*/srcIdx < 9 ? srcIdx + 36 : srcIdx,
/*buttonNum=*/0,
ClickType.PICKUP,
p);
gm.handleInventoryMouseClick(
/*containerId=*/0,
/*slotNum=*/dstIdx < 9 ? dstIdx + 36 : dstIdx,
/*buttonNum=*/0,
ClickType.PICKUP,
p);
} else {
gm.handleInventoryMouseClick(
/*containerId=*/0,
/*slotNum=*/srcIdx < 9 ? srcIdx + 36 : srcIdx,
/*buttonNum=*/0,
ClickType.PICKUP,
p);
gm.handleInventoryMouseClick(
/*containerId=*/0,
/*slotNum=*/dstIdx < 9 ? dstIdx + 36 : dstIdx,
/*buttonNum=*/0,
ClickType.PICKUP,
p);
gm.handleInventoryMouseClick(
/*containerId=*/0,
/*slotNum=*/srcIdx < 9 ? srcIdx + 36 : srcIdx,
/*buttonNum=*/0,
ClickType.PICKUP,
p);
}
}
// return true if success
private boolean tryFillItemInHand(LocalPlayer p) {
ItemStack itemStack = p.getItemInHand(InteractionHand.MAIN_HAND);
if (!itemStack.isEmpty()) return true;
if (used_material == null) return false;
ItemStack expectingItem = new ItemStack(used_material);
int supplmentIdx = -1;
NonNullList<ItemStack> inv = p.getInventory().items;
for (int idx = 0; idx < 36; ++idx) {
ItemStack s = inv.get(idx);
if (s.getItem() == expectingItem.getItem() &&
s.getDamageValue() == expectingItem.getDamageValue() &&
!s.hasTag()) {
supplmentIdx = idx;
break;
}
}
if (supplmentIdx < 0) return false;
moveInventoryItem(supplmentIdx, p.getInventory().selected);
return true;
}
static BlockHitResult getHitResult(BlockPos block, Direction face, double blockOffsetY) {
// New block will be placed on `face` of `block`.
Vec3 hit = switch (face) {
case UP -> new Vec3(block.getX() + 0.5, block.getY() + 1, block.getZ() + 0.5);
case DOWN -> new Vec3(block.getX() + 0.5, block.getY(), block.getZ() + 0.5);
case EAST -> new Vec3(block.getX() + 1, block.getY() + blockOffsetY, block.getZ() + 0.5);
case SOUTH -> new Vec3(block.getX() + 0.5, block.getY() + blockOffsetY, block.getZ() + 1);
case WEST -> new Vec3(block.getX(), block.getY() + blockOffsetY, block.getZ() + 0.5);
case NORTH -> new Vec3(block.getX() + 0.5, block.getY() + blockOffsetY, block.getZ());
};
return new BlockHitResult(hit, face, block, false);
}
public boolean tryPlacement(LocalPlayer p) {
BlockPos blockStandingOn = player_block_pos.atY(blockY);
Direction d = Direction.fromYRot(p.getYRot());
BlockPos blockWillPlaceOn = blockStandingOn.relative(d);
BlockState blockToBeReplaced = p.level.getBlockState(blockWillPlaceOn);
if (blockToBeReplaced.isAir() || blockToBeReplaced.getBlock() == Blocks.WATER ||
blockToBeReplaced.getBlock() == Blocks.LAVA) {
Minecraft.getInstance().gameMode.useItemOn(p, p.clientLevel, InteractionHand.MAIN_HAND,
getHitResult(blockStandingOn, d, blockOffsetY));
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment