Initial Commit

This commit is contained in:
Cameron Reed 2022-12-04 00:27:22 -07:00
parent 8749a28cdc
commit 1f2c68181e
23 changed files with 1266 additions and 0 deletions

119
.gitignore vendored Normal file
View File

@ -0,0 +1,119 @@
# User-specific stuff
.idea/
remappedSrc/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Cache of project
.gradletasknamecache
**/build/
# Common working directory
run/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022 CameronReed
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

86
build.gradle Normal file
View File

@ -0,0 +1,86 @@
plugins {
id 'fabric-loom' version '1.0-SNAPSHOT'
id 'maven-publish'
}
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
maven {
name = 'TerraformersMC'
url = 'https://maven.terraformersmc.com/releases'
}
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modCompileOnly "com.terraformersmc:modmenu:4.1.1"
}
processResources {
inputs.property "version", project.version
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
def targetJavaVersion = 17
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
it.options.release = targetJavaVersion
}
}
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
archivesBaseName = project.archives_base_name
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}" }
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

14
gradle.properties Normal file
View File

@ -0,0 +1,14 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.19.2
yarn_mappings=1.19.2+build.28
loader_version=0.14.10
# Mod Properties
mod_version=1.0.0
maven_group=cmods
archives_base_name=haxxor
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.67.1+1.19.2

View File

@ -0,0 +1 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip

9
settings.gradle Normal file
View File

@ -0,0 +1,9 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
}
}

View File

@ -0,0 +1,322 @@
package cmods.haxxor.client;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.block.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import java.util.ArrayList;
import java.util.Objects;
public class AutoFarmer { // Really just a namespace and needed to separate things into their own files
public static final AutoFarmerCropType[] crops = {
new AutoFarmerCropType(Blocks.WHEAT, Items.WHEAT_SEEDS, GROWTH_PATTERN.BASIC, Blocks.FARMLAND),
new AutoFarmerCropType(Blocks.BEETROOTS, Items.BEETROOT_SEEDS, GROWTH_PATTERN.BASIC, Blocks.FARMLAND),
new AutoFarmerCropType(Blocks.POTATOES, Items.POTATO, GROWTH_PATTERN.BASIC, Blocks.FARMLAND),
new AutoFarmerCropType(Blocks.CARROTS, Items.CARROT, GROWTH_PATTERN.BASIC, Blocks.FARMLAND),
new AutoFarmerCropType(Blocks.NETHER_WART, Items.NETHER_WART, GROWTH_PATTERN.BASIC, Blocks.SOUL_SAND),
new AutoFarmerCropType(Blocks.SUGAR_CANE, null, GROWTH_PATTERN.LEAVE_BASE, null)
};
private enum ACTION_TYPE {
NONE,
BREAK,
PLACE,
PLACE_MAIN_HAND,
PLACE_OFF_HAND
}
private enum GROWTH_PATTERN {
BASIC,
LEAVE_BASE
}
private static final ArrayList<FarmerAction> potentialActions = new ArrayList<>();
private static final ArrayList<FarmerAction> queuedActions = new ArrayList<>();
private static final ArrayList<BlockPos> queuedStopPackets = new ArrayList<>();
private static HaxxorOptions.AutoFarmerOptions options;
private static final int offHandSlot = 45;
public static void init() {
options = HaxxorOptions.getInstance().autoFarmer;
}
public static boolean canFarm() {
return queuedActions.size() > 0;
}
public static void tick(MinecraftClient client) {
if (client.player == null)
return;
if (queuedStopPackets.size() > 0)
sendStopPackets(client);
scanBlocks(client);
if (options.move_seeds && options.enabled)
adjustInventory(client);
findActions(client);
if (options.enabled)
run(client);
}
private static void run(MinecraftClient client) {
if (client.world == null || client.player == null)
return;
for (FarmerAction action : queuedActions) {
switch (action.type) {
case BREAK -> breakCrop(client, action.pos);
case PLACE_MAIN_HAND -> plantSeeds(client, action.pos, Hand.MAIN_HAND);
case PLACE_OFF_HAND -> plantSeeds(client, action.pos, Hand.OFF_HAND);
}
}
}
public static void adjustInventory(MinecraftClient client) {
if (client.player == null || client.world == null)
return;
boolean item_in_main = false;
boolean item_in_off = false;
PlayerInventory inventory = client.player.getInventory();
for (FarmerAction action: potentialActions) {
if (action.type != ACTION_TYPE.PLACE)
continue;
BlockState block = client.world.getBlockState(action.pos);
for (int i = 0; i < crops.length; i++) {
int slot = inventory.getSlotWithStack(new ItemStack(crops[i].seed_type));
if (crops[i].seed_type != null && crops[i].planted_on != null && block.isOf(crops[i].planted_on) &&
options.crops_enabled[i] && slot != -1) {
if (!item_in_main) {
if (PlayerInventory.isValidHotbarIndex(slot)) {
selectSlot(client, slot);
} else if (slot < 36) {
moveToHotBar(client, inventory, inventory.getSwappableHotbarSlot(), slot);
}
item_in_main = true;
} else if (!item_in_off && slot < 36 && slot > 8) {
moveToOffHand(client, inventory, slot);
}
}
}
}
}
private static void moveToOffHand(MinecraftClient client, PlayerInventory inventory, int slot) {
if (client.player == null)
return;
int[] slots = {slot, offHandSlot};
ItemStack[] stacks = {inventory.getStack(offHandSlot), inventory.getStack(slot)};
Int2ObjectMap<ItemStack> modifiedItems = new Int2ObjectArrayMap<>(slots, stacks);
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(
new ClickSlotC2SPacket(0, client.player.playerScreenHandler.getRevision(), slot, 40,
SlotActionType.SWAP, ItemStack.EMPTY, modifiedItems));
}
private static void moveToHotBar(MinecraftClient client, PlayerInventory inventory, int hotBarSlot, int slot) {
if (client.player == null)
return;
int[] slots = {slot, offHandSlot};
ItemStack[] stacks = {inventory.getStack(offHandSlot), inventory.getStack(slot)};
Int2ObjectMap<ItemStack> modifiedItems = new Int2ObjectArrayMap<>(slots, stacks);
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(
new ClickSlotC2SPacket(0, client.player.playerScreenHandler.getRevision(), slot, hotBarSlot,
SlotActionType.SWAP, ItemStack.EMPTY, modifiedItems));
selectSlot(client, hotBarSlot);
}
private static void selectSlot(MinecraftClient client, int slot) {
if (PlayerInventory.isValidHotbarIndex(slot) && client.player != null) {
client.player.getInventory().selectedSlot = slot;
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(new UpdateSelectedSlotC2SPacket(slot));
}
}
private static void scanBlocks(MinecraftClient client) {
if (client.world == null || client.player == null)
return;
potentialActions.clear();
BlockPos playerPos = client.player.getBlockPos();
for (int y = options.min_y; y <= options.max_y; y++) {
for (int x = -options.horizontal_reach; x <= options.horizontal_reach; x++) {
for (int z = -options.horizontal_reach; z <= options.horizontal_reach; z++) {
BlockPos pos = playerPos.add(x, y, z);
ACTION_TYPE action = checkBlock(client, pos);
if (action != ACTION_TYPE.NONE) {
potentialActions.add(new FarmerAction(pos, action));
}
}
}
}
}
private static ACTION_TYPE checkBlock(MinecraftClient client, BlockPos pos) {
if (client.world == null || client.player == null)
return ACTION_TYPE.NONE;
BlockState block = client.world.getBlockState(pos);
for (int i = 0; i < crops.length; i++) {
AutoFarmerCropType cropType = crops[i];
if (cropType.growth_pattern == GROWTH_PATTERN.BASIC && block.isOf(cropType.crop_type) &&
options.crops_enabled[i]) {
return isMature(block, cropType.crop_type.getClass()) ? ACTION_TYPE.BREAK : ACTION_TYPE.NONE;
}
if (cropType.growth_pattern == GROWTH_PATTERN.LEAVE_BASE && block.isOf(cropType.crop_type) &&
options.crops_enabled[i]) {
return isSecondBlock(client.world, pos, cropType.crop_type) ? ACTION_TYPE.BREAK : ACTION_TYPE.NONE;
}
if (cropType.seed_type != null && cropType.planted_on != null && block.isOf(cropType.planted_on) &&
client.world.getBlockState(pos.up()).isAir() && options.seeds_enabled[i]) {
return ACTION_TYPE.PLACE;
}
}
return ACTION_TYPE.NONE;
}
private static void findActions(MinecraftClient client) {
if (client.world == null || client.player == null)
return;
queuedActions.clear();
for (FarmerAction action: potentialActions) {
if (action.type != ACTION_TYPE.PLACE) {
queuedActions.add(action);
} else {
ACTION_TYPE action_type = holingSeeds(client, action.pos);
if (action_type != ACTION_TYPE.NONE) {
queuedActions.add(new FarmerAction(action.pos, action_type));
}
}
if (queuedActions.size() >= options.max_actions_per_tick)
return;
}
}
private static ACTION_TYPE holingSeeds(MinecraftClient client, BlockPos pos) {
if (client.world == null || client.player == null)
return ACTION_TYPE.NONE;
ItemStack mainHandItem = client.player.getStackInHand(Hand.MAIN_HAND);
ItemStack offHandItem = client.player.getStackInHand(Hand.OFF_HAND);
BlockState block = client.world.getBlockState(pos);
for (int i = 0; i < crops.length; i++) {
AutoFarmerCropType cropType = crops[i];
if (cropType.seed_type != null && cropType.planted_on != null && block.isOf(cropType.planted_on) &&
client.world.getBlockState(pos.up()).isAir() && options.seeds_enabled[i]) {
if (mainHandItem.isOf(cropType.seed_type)) {
return ACTION_TYPE.PLACE_MAIN_HAND;
} else if (offHandItem.isOf(cropType.seed_type)) {
return ACTION_TYPE.PLACE_OFF_HAND;
}
}
}
return ACTION_TYPE.NONE;
}
private static boolean isMature(BlockState block, Class<?> blockType) {
if (block.getBlock() instanceof CropBlock)
return ((CropBlock) block.getBlock()).isMature(block);
if (blockType == NetherWartBlock.class)
return block.get(NetherWartBlock.AGE) >= NetherWartBlock.field_31199;
return false;
}
private static boolean isSecondBlock(ClientWorld world, BlockPos pos, Block crop_type) {
return world.getBlockState(pos).isOf(crop_type) && world.getBlockState(pos.down()).isOf(crop_type) &&
!world.getBlockState(pos.down().down()).isOf(crop_type);
}
private static void sendStopPackets(MinecraftClient client) {
if (client.world == null || client.player == null)
return;
for (BlockPos pos: queuedStopPackets) {
PlayerActionC2SPacket packet =
new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, pos, Direction.UP, 0);
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet);
}
queuedStopPackets.clear();
}
private static void breakCrop(MinecraftClient client, BlockPos pos) {
if (client == null || client.player == null)
return;
PlayerActionC2SPacket packet =
new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, Direction.UP, 0);
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet);
queuedStopPackets.add(pos);
}
private static void plantSeeds(MinecraftClient client, BlockPos pos, Hand hand) {
if (client == null || client.player == null)
return;
PlayerInteractBlockC2SPacket packet = new PlayerInteractBlockC2SPacket(hand,
new BlockHitResult(new Vec3d(pos.getX(), pos.getY(), pos.getZ()), Direction.UP, pos, false), 0);
Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet);
}
public static class AutoFarmerCropType {
public Block crop_type;
public Item seed_type;
public GROWTH_PATTERN growth_pattern;
public Block planted_on;
public AutoFarmerCropType(Block crop_type, Item seed_type, GROWTH_PATTERN growth_pattern, Block planted_on) {
this.crop_type = crop_type;
this.seed_type = seed_type;
this.growth_pattern = growth_pattern;
this.planted_on = planted_on;
}
}
private record FarmerAction(BlockPos pos, ACTION_TYPE type) { }
}

View File

@ -0,0 +1,53 @@
package cmods.haxxor.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.StickyKeyBinding;
import org.lwjgl.glfw.GLFW;
import java.util.Optional;
@Environment(EnvType.CLIENT)
public class HaxxorClient implements ClientModInitializer {
public static String MOD_ID = "haxxor";
public static String version = "Unknown";
private static StickyKeyBinding auto_farm_key;
private static final HaxxorOptions options = HaxxorOptions.getInstance();
@Override
public void onInitializeClient() {
Optional<ModContainer> modContainer = FabricLoader.getInstance().getModContainer(MOD_ID);
modContainer.ifPresentOrElse(container -> version = container.getMetadata().getVersion().getFriendlyString(),
() -> System.out.println("Haxxor: Could not get mod version"));
auto_farm_key = (StickyKeyBinding) KeyBindingHelper.registerKeyBinding(new StickyKeyBinding(
"key.haxxor.auto_farm",
GLFW.GLFW_KEY_Z,
"category.haxxor.options",
() -> true
));
AutoFarmer.init();
ClientTickEvents.END_CLIENT_TICK.register(this::tick);
}
public static boolean toggleAutoFarmer() {
auto_farm_key.setPressed(true);
return options.autoFarmer.enabled = auto_farm_key.isPressed();
}
private void tick(MinecraftClient client) {
options.autoFarmer.enabled = auto_farm_key.isPressed();
AutoFarmer.tick(client);
}
}

View File

@ -0,0 +1,90 @@
package cmods.haxxor.client;
import java.util.prefs.Preferences;
public final class HaxxorOptions {
private static HaxxorOptions instance = null;
public final AutoFarmerOptions autoFarmer;
public static HaxxorOptions getInstance() {
if (instance == null)
instance = new HaxxorOptions();
return instance;
}
private HaxxorOptions() {
autoFarmer = new AutoFarmerOptions();
load();
}
public void load() {
autoFarmer.load();
}
public void save() {
autoFarmer.save();
}
public static class AutoFarmerOptions {
private final Preferences prefs = Preferences.userNodeForPackage(AutoFarmerOptions.class);
public boolean enabled;
public boolean move_seeds;
public int min_y = -2;
public int max_y = 1;
public int horizontal_reach = 4;
public int max_actions_per_tick = 30;
public final boolean[] seeds_enabled = new boolean[AutoFarmer.crops.length];
public final boolean[] crops_enabled = new boolean[AutoFarmer.crops.length];
public AutoFarmerOptions() { }
private void load() {
move_seeds = prefs.getBoolean("move_seeds", move_seeds);
min_y = prefs.getInt("min_y", min_y);
max_y = prefs.getInt("max_y", max_y);
horizontal_reach = prefs.getInt("horizontal_reach", horizontal_reach);
max_actions_per_tick = prefs.getInt("max_actions_per_tick", max_actions_per_tick);
for (int i = 0; i < AutoFarmer.crops.length; i++) {
if (AutoFarmer.crops[i].seed_type != null) {
seeds_enabled[i] = prefs.getBoolean("seed_" + AutoFarmer.crops[i].seed_type.getName().getString(),
seeds_enabled[i]);
}
if (AutoFarmer.crops[i].crop_type != null) {
crops_enabled[i] = prefs.getBoolean("crop_" + AutoFarmer.crops[i].crop_type.getName().getString(),
crops_enabled[i]);
}
}
}
private void save() {
prefs.putBoolean("move_seeds", move_seeds);
prefs.putInt("min_y", min_y);
prefs.putInt("max_y", max_y);
prefs.putInt("horizontal_reach", horizontal_reach);
prefs.putInt("max_actions_per_tick", max_actions_per_tick);
for (int i = 0; i < AutoFarmer.crops.length; i++) {
if (AutoFarmer.crops[i].seed_type != null) {
prefs.putBoolean("seed_" + AutoFarmer.crops[i].seed_type.getName().getString(), seeds_enabled[i]);
}
if (AutoFarmer.crops[i].crop_type != null) {
prefs.putBoolean("crop_" + AutoFarmer.crops[i].crop_type.getName().getString(), crops_enabled[i]);
}
}
}
}
}

View File

@ -0,0 +1,13 @@
package cmods.haxxor.client;
import cmods.haxxor.client.ui.HaxxorOptionsScreen;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import net.minecraft.client.gui.screen.Screen;
public class ModMenuConfig implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return (ConfigScreenFactory<Screen>) HaxxorOptionsScreen::new;
}
}

View File

@ -0,0 +1,14 @@
package cmods.haxxor.client.ui;
public class Constants {
public static final int buttonWidth = 150;
public static final int buttonHeight = 20;
public static final int column1_offset = -5 - buttonWidth;
public static final int column2_offset = 5;
public static final float startHeight_multiplier = 1.0f / 6.0f;
public static final int rowIncrement = buttonHeight + 5;
public static final int doneButtonWidth = buttonWidth + buttonWidth / 3;
public static final int doneButtonX_offset = -(doneButtonWidth / 2);
public static final int doneButtonRowIncrement = rowIncrement + 4;
}

View File

@ -0,0 +1,80 @@
package cmods.haxxor.client.ui;
import cmods.haxxor.client.AutoFarmer;
import cmods.haxxor.client.HaxxorOptions;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import static cmods.haxxor.client.ui.Constants.*;
public class CropSelectScreen extends Screen {
private final HaxxorOptions options;
private final Screen parent;
private final int heightOffset = 10;
protected CropSelectScreen(Screen parent) {
super(Text.translatable("haxxor.options.farmer.crop_select.title"));
this.parent = parent;
options = HaxxorOptions.getInstance();
}
protected void init() {
if (client == null)
return;
final int column1 = this.width / 2 + column1_offset;
final int column2 = this.width / 2 + column2_offset;
final int startHeight = (int) Math.floor(this.height * startHeight_multiplier) + heightOffset;
final int doneButtonX = this.width / 2 + doneButtonX_offset;
int y = startHeight;
for (int i = 0; i < AutoFarmer.crops.length; i++) {
int finalI = i;
if (AutoFarmer.crops[i].seed_type != null) {
addDrawableChild(new ToggleEnableButtonWidget(column1, y, buttonWidth, buttonHeight,
AutoFarmer.crops[i].seed_type.getName(), options.autoFarmer.seeds_enabled[i],
(button, enable) -> options.autoFarmer.seeds_enabled[finalI] = enable));
}
if (AutoFarmer.crops[i].crop_type != null) {
addDrawableChild(new ToggleEnableButtonWidget(column2, y, buttonWidth, buttonHeight,
AutoFarmer.crops[i].crop_type.getName(), options.autoFarmer.crops_enabled[i],
(button, enable) -> options.autoFarmer.crops_enabled[finalI] = enable));
}
y += rowIncrement;
}
addDrawableChild(new ButtonWidget(doneButtonX, y + doneButtonRowIncrement, doneButtonWidth, buttonHeight,
ScreenTexts.DONE, button -> {
options.save();
client.setScreen(parent);
}));
}
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
final int column1 = this.width / 2 + column1_offset;
final int column2 = this.width / 2 + column2_offset;
final int startHeight = (int) Math.floor(this.height * startHeight_multiplier) + heightOffset;
this.renderBackground(matrices);
drawCenteredText(matrices, this.textRenderer, Text.translatable("haxxor.options.farmer.crop_select.seeds"),
column1 + buttonWidth / 2, startHeight - rowIncrement, 0xffffff);
drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 0xffffff);
drawCenteredText(matrices, this.textRenderer, Text.translatable("haxxor.options.farmer.crop_select.blocks"),
column2 + buttonWidth / 2, startHeight - rowIncrement, 0xffffff);
super.render(matrices, mouseX, mouseY, delta);
}
public void removed() {
options.save();
}
}

View File

@ -0,0 +1,41 @@
package cmods.haxxor.client.ui;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
public class CycleButtonWidget extends ButtonWidget {
private final Text[] options;
private final CycleAction onCycle;
private int selectedIndex;
public CycleButtonWidget(int x, int y, int width, int height, Text[] options, int startIndex, CycleAction onCycle) {
this(x, y, width, height, options, startIndex, onCycle, EMPTY);
}
public CycleButtonWidget(int x, int y, int width, int height, Text[] options, int startIndex, CycleAction onCycle, TooltipSupplier tooltipSupplier) {
super(x, y, width, height, options[startIndex], onCycle, tooltipSupplier);
this.options = options;
this.onCycle = onCycle;
this.selectedIndex = startIndex;
}
@Override
public void onPress() {
selectedIndex++;
if (selectedIndex >= options.length) {
selectedIndex = 0;
}
this.setMessage(options[selectedIndex]);
this.onCycle.onCycle(this, selectedIndex);
}
public interface CycleAction extends PressAction {
void onCycle(ButtonWidget button, int newIndex);
@Override
default void onPress(ButtonWidget button) { }
}
}

View File

@ -0,0 +1,115 @@
package cmods.haxxor.client.ui;
import cmods.haxxor.client.HaxxorClient;
import cmods.haxxor.client.HaxxorOptions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
import static cmods.haxxor.client.ui.Constants.*;
public class FarmerOptionsScreen extends Screen {
private final Screen parent;
private final HaxxorOptions options = HaxxorOptions.getInstance();
protected FarmerOptionsScreen(Screen parent) {
super(Text.translatable("haxxor.options.farmer.title"));
this.parent = parent;
}
protected void init() {
if (client == null)
return;
final Text enabledText = Text.translatable("haxxor.state.enabled");
final Text disabledText = Text.translatable("haxxor.state.disabled");
final int column1 = this.width / 2 + column1_offset;
final int column2 = this.width / 2 + column2_offset;
final int startHeight = (int) Math.floor(this.height * startHeight_multiplier);
final int doneButtonX = this.width / 2 + doneButtonX_offset;
int row = 0;
addDrawableChild(new ButtonWidget(column1, startHeight, buttonWidth, buttonHeight,
options.autoFarmer.enabled ? enabledText : disabledText,
button -> button.setMessage(HaxxorClient.toggleAutoFarmer() ? enabledText : disabledText)));
addDrawableChild(new ButtonWidget(column2, startHeight, buttonWidth, buttonHeight,
Text.translatable("haxxor.options.farmer.edit_actions"),
button -> client.setScreen(new CropSelectScreen(this))));
row++;
addDrawableChild(new ToggleEnableButtonWidget(column1, startHeight + (rowIncrement * row), buttonWidth,
buttonHeight, Text.literal("Move Seeds"), options.autoFarmer.move_seeds,
(button, isEnabled) -> options.autoFarmer.move_seeds = isEnabled));
row++;
addVariableAdjust(column1, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.min_y,
Text.translatable("haxxor.options.farmer.min_y"),
() -> --options.autoFarmer.min_y,
() -> ++options.autoFarmer.min_y);
addVariableAdjust(column2, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.max_y,
Text.translatable("haxxor.options.farmer.max_y"),
() -> --options.autoFarmer.max_y,
() -> ++options.autoFarmer.max_y);
row++;
addVariableAdjust(column1, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.horizontal_reach,
Text.translatable("haxxor.options.farmer.reach"),
() -> --options.autoFarmer.horizontal_reach,
() -> ++options.autoFarmer.horizontal_reach);
addVariableAdjust(column2, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.max_actions_per_tick,
Text.translatable("haxxor.options.farmer.actions_per_tick"),
() -> options.autoFarmer.max_actions_per_tick -= 5,
() -> options.autoFarmer.max_actions_per_tick += 5);
row++;
this.addDrawableChild(new ButtonWidget(doneButtonX,
startHeight + (rowIncrement * row) + doneButtonRowIncrement, doneButtonWidth, buttonHeight,
ScreenTexts.DONE, button -> client.setScreen(parent)));
}
@SuppressWarnings("SameParameterValue")
private void addVariableAdjust(int x, int y, int width, int height, int initialValue, @NotNull Text label,
UpdateTextAction minus, UpdateTextAction add) {
final int button_width = height + height / 4;
final String labelStr = label.getString() + ": ";
ButtonWidget middle = new ButtonWidget(x + button_width - 1, y, width - (button_width * 2) + 2, height,
Text.literal(labelStr + initialValue), button -> {});
this.addDrawableChild(middle);
this.addDrawableChild(new ButtonWidget(x, y, button_width, height, Text.literal("-"),
button -> middle.setMessage(Text.literal(labelStr + minus.onUpdate()))));
this.addDrawableChild(new ButtonWidget(x + width - button_width + 1, y, button_width, height, Text.literal("+"),
button -> middle.setMessage(Text.literal(labelStr + add.onUpdate()))));
}
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 0xffffff);
super.render(matrices, mouseX, mouseY, delta);
}
public void removed() {
options.save();
}
@Environment(EnvType.CLIENT)
private interface UpdateTextAction {
int onUpdate();
}
}

View File

@ -0,0 +1,46 @@
package cmods.haxxor.client.ui;
import cmods.haxxor.client.HaxxorClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import static cmods.haxxor.client.ui.Constants.*;
public class HaxxorOptionsScreen extends Screen {
private final Screen parent;
public HaxxorOptionsScreen(Screen parent) {
super(Text.translatable("haxxor.options.title"));
this.parent = parent;
}
protected void init() {
if (client == null)
return;
final int column1 = this.width / 2 + column1_offset;
final int startHeight = (int) Math.floor(this.height * startHeight_multiplier);
final int doneButtonX = this.width / 2 + doneButtonX_offset;
this.addDrawableChild(new ButtonWidget(column1, startHeight, buttonWidth, buttonHeight,
Text.translatable("haxxor.options.farmer"),
button -> this.client.setScreen(new FarmerOptionsScreen(this))));
this.addDrawableChild(new ButtonWidget(doneButtonX, startHeight + rowIncrement + doneButtonRowIncrement,
doneButtonWidth, buttonHeight, ScreenTexts.DONE, button -> this.client.setScreen(this.parent)));
}
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
renderBackground(matrices);
Text versionText = Text.literal("v" + HaxxorClient.version);
drawCenteredText(matrices, textRenderer, title, this.width / 2, 15, 0xffffff);
drawTextWithShadow(matrices, textRenderer, versionText, this.width - textRenderer.getWidth(versionText) - 2,
this.height - textRenderer.fontHeight - 2, 0xffffff);
super.render(matrices, mouseX, mouseY, delta);
}
}

View File

@ -0,0 +1,51 @@
package cmods.haxxor.client.ui;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
public class ToggleEnableButtonWidget extends ButtonWidget {
private final String name;
private boolean enabled;
private final ToggleAction onToggle;
private final String enabledText = Text.translatable("haxxor.state.enabled").getString();
private final String disabledText = Text.translatable("haxxor.state.disabled").getString();
public ToggleEnableButtonWidget(int x, int y, int width, int height, Text name, boolean startEnabled,
ToggleAction onToggle) {
this(x, y, width, height, name, startEnabled, onToggle, EMPTY);
}
public ToggleEnableButtonWidget(int x, int y, int width, int height, Text name, boolean startEnabled,
ToggleAction onToggle, TooltipSupplier tooltipSupplier) {
super(x, y, width, height, name, onToggle, tooltipSupplier);
this.name = name.getString() + ": ";
this.enabled = startEnabled;
this.onToggle = onToggle;
}
private void updateText() {
setMessage(Text.literal(name + (enabled ? enabledText : disabledText)));
}
@Override
public void onPress() {
onToggle.onToggle(this, enabled = !enabled);
}
@Override
public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) {
updateText();
super.renderButton(matrices, mouseX, mouseY, delta);
}
public interface ToggleAction extends PressAction {
void onToggle(ButtonWidget button, boolean isEnabled);
@Override
default void onPress(ButtonWidget button) { }
}
}

View File

@ -0,0 +1,60 @@
package cmods.haxxor.mixin;
import cmods.haxxor.client.AutoFarmer;
import cmods.haxxor.client.HaxxorOptions;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
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.CallbackInfo;
import java.util.ArrayList;
@Mixin(InGameHud.class)
public abstract class HudMixin extends DrawableHelper {
@Shadow @Final private MinecraftClient client;
@Shadow public abstract TextRenderer getTextRenderer();
@Inject(at = @At("TAIL"), method = "render")
private void render(MatrixStack matrices, float tickDelta, CallbackInfo callback) {
if (this.client.options.debugEnabled || client.player == null)
return;
HaxxorOptions options = HaxxorOptions.getInstance();
TextRenderer textRenderer = this.getTextRenderer();
ArrayList<Pair<Text, Integer>> lines = new ArrayList<>();
int x = 5;
int y = 5;
int red = 0xff0000;
int green = 0x00ff00;
if (options.autoFarmer.enabled) {
lines.add(new Pair<>(Text.translatable("haxxor.state.on"), green));
} else {
lines.add(new Pair<>(Text.translatable("haxxor.state.off"), red));
}
if (AutoFarmer.canFarm()) {
lines.add(new Pair<>(Text.translatable("haxxor.actions.available"), green));
}
for (Pair<Text, Integer> line: lines) {
DrawableHelper.drawTextWithShadow(matrices, textRenderer, line.getFirst(), x, y, line.getSecond());
y += 10;
}
}
}

View File

@ -0,0 +1,29 @@
package cmods.haxxor.mixin;
import cmods.haxxor.client.ui.HaxxorOptionsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.option.OptionsScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
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.CallbackInfo;
@Mixin(OptionsScreen.class)
public class OptionsMixin extends Screen {
protected OptionsMixin(Text title) {
super(title);
}
@Inject(at = @At("TAIL"), method = "init")
private void init(CallbackInfo ci) {
if (client == null)
return;
this.addDrawableChild(new ButtonWidget(20, 20, 100, 20,
Text.translatable("haxxor.options"), (button) ->
client.setScreen(new HaxxorOptionsScreen(this))));
}
}

View File

@ -0,0 +1,30 @@
package cmods.haxxor.mixin;
import cmods.haxxor.client.ui.HaxxorOptionsScreen;
import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
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.CallbackInfo;
@Mixin(GameMenuScreen.class)
public class PauseMixin extends Screen {
protected PauseMixin(Text title) {
super(title);
}
@Inject(at = @At("TAIL"), method = "init")
public void init(CallbackInfo ci) {
if (client == null)
return;
this.addDrawableChild(new ButtonWidget(20, 20, 100, 20,
Text.translatable("haxxor.options"), (button) ->
client.setScreen(new HaxxorOptionsScreen(this))));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,26 @@
{
"haxxor.state.on": "On",
"haxxor.state.off": "Off",
"haxxor.state.enabled": "Enabled",
"haxxor.state.disabled": "Disabled",
"haxxor.actions.available": "Actions available",
"haxxor.actions.unavailable": "Actions unavailable",
"haxxor.options": "Haxxor",
"haxxor.options.title": "Haxxor Options",
"haxxor.options.farmer": "Auto Farm",
"haxxor.options.farmer.title": "Auto Farm Options",
"haxxor.options.farmer.max_y": "Max Y Offset",
"haxxor.options.farmer.min_y": "Min Y Offset",
"haxxor.options.farmer.reach": "Reach",
"haxxor.options.farmer.edit_actions": "Configure Actions",
"haxxor.options.farmer.actions_per_tick": "Max Per Tick",
"haxxor.options.farmer.crop_select.title": "Toggle Actions",
"haxxor.options.farmer.crop_select.seeds": "Seeds to Plant",
"haxxor.options.farmer.crop_select.blocks": "Crops to Break",
"category.haxxor.options": "Haxxor",
"key.haxxor.auto_farm": "Auto Farm Toggle",
"key.haxxor.swap_items": "Swap Items"
}

View File

@ -0,0 +1,30 @@
{
"schemaVersion": 1,
"id": "haxxor",
"version": "${version}",
"name": "Haxxor",
"description": "A collection of hacks because yes",
"authors": [
"Cameron Reed"
],
"contact": {
"sources": "https://gitea.cam123.dev/CameronReed/cmods-haxxor"
},
"license": "MIT",
"icon": "assets/haxxor/icon.png",
"environment": "client",
"entrypoints": {
"client": [
"cmods.haxxor.client.HaxxorClient"
],
"modmenu": ["cmods.haxxor.client.ModMenuConfig"]
},
"mixins": [
"haxxor.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.10",
"fabric": "*",
"minecraft": "1.19.2"
}
}

View File

@ -0,0 +1,16 @@
{
"required": true,
"minVersion": "0.8",
"package": "cmods.haxxor.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
],
"client": [
"HudMixin",
"OptionsMixin",
"PauseMixin"
],
"injectors": {
"defaultRequire": 1
}
}