Initial commit
Some checks failed
build / build (push) Has been cancelled

This commit is contained in:
2025-07-29 22:05:26 -04:00
commit 7a9e57f5cc
23 changed files with 1076 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package me.advait;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import net.fabricmc.loader.api.FabricLoader;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.file.Path;
import java.util.*;
public class PriorityConfig {
private static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve("priority_keys.json");
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static String activePreset = "default";
public static Map<String, List<Integer>> presets = new HashMap<>();
public static List<Integer> priorityOrder = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5, 6, 7, 8)); // fallback
public static void load() {
if (!CONFIG_PATH.toFile().exists()) {
save(); // create default config if missing
return;
}
try (Reader reader = new FileReader(CONFIG_PATH.toFile())) {
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = GSON.fromJson(reader, type);
// Load active preset
if (data.containsKey("active_preset")) {
activePreset = (String) data.get("active_preset");
}
// Load presets
Object rawPresets = data.get("presets");
if (rawPresets instanceof Map<?, ?> rawMap) {
for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
String key = entry.getKey().toString();
List<Integer> converted = new ArrayList<>();
for (Object o : (List<?>) entry.getValue()) {
converted.add(((Number) o).intValue());
}
presets.put(key, converted);
}
}
// Apply active preset
if (!applyPreset(activePreset)) {
System.err.println("Invalid active preset: " + activePreset);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void save() {
Map<String, Object> data = new HashMap<>();
data.put("active_preset", activePreset);
data.put("presets", presets);
try (Writer writer = new FileWriter(CONFIG_PATH.toFile())) {
GSON.toJson(data, writer);
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean applyPreset(String name) {
List<Integer> preset = presets.get(name);
if (preset == null) return false;
priorityOrder = new ArrayList<>(preset);
activePreset = name;
return true;
}
}

View File

@@ -0,0 +1,59 @@
package me.advait;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.text.Text;
import net.minecraft.client.util.InputUtil;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import org.lwjgl.glfw.GLFW;
import java.util.*;
public class PriorityKeysClient implements ClientModInitializer {
private static final Set<Integer> keysPressedThisTick = new HashSet<>();
private static KeyBinding cyclePresetKey;
@Override
public void onInitializeClient() {
PriorityConfig.load();
cyclePresetKey = KeyBindingHelper.registerKeyBinding(
new KeyBinding("Cycle Priority Key Preset", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_SEMICOLON, "Priority Keys")
);
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player == null || client.options == null) return;
// Handle keybind to cycle preset
while (cyclePresetKey.wasPressed()) {
List<String> presetNames = new ArrayList<>(PriorityConfig.presets.keySet());
int index = presetNames.indexOf(PriorityConfig.activePreset);
index = (index + 1) % presetNames.size();
String next = presetNames.get(index);
PriorityConfig.applyPreset(next);
PriorityConfig.save();
client.player.sendMessage(Text.of("§aSwitched to priority preset: §f" + next), true);
}
KeyBinding[] currentHotbarKeys = client.options.hotbarKeys;
keysPressedThisTick.clear();
for (int i = 0; i < currentHotbarKeys.length; i++) {
if (currentHotbarKeys[i].isPressed()) {
keysPressedThisTick.add(i);
}
}
if (keysPressedThisTick.size() > 1) {
for (int slot : PriorityConfig.priorityOrder) {
if (keysPressedThisTick.contains(slot)) {
client.player.getInventory().setSelectedSlot(slot);
break;
}
}
}
});
}
}

View File

@@ -0,0 +1,30 @@
package me.advait.mixin.client;
import me.advait.screen.PriorityPresetScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
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(ControlsOptionsScreen.class)
public abstract class ControlsOptionsScreenMixin {
@Inject(method = "addOptions", at = @At("TAIL"))
private void addPriorityButton(CallbackInfo ci) {
ControlsOptionsScreen screen = (ControlsOptionsScreen) (Object) this;
screen.body.addWidgetEntry(
ButtonWidget.builder(
Text.literal("Priority Keys..."),
b -> MinecraftClient.getInstance().setScreen(new PriorityPresetScreen(screen))
).build(),
null
);
}
}

View File

@@ -0,0 +1,14 @@
package me.advait.mixin.client;
import net.minecraft.client.gui.screen.option.GameOptionsScreen;
import net.minecraft.client.gui.widget.OptionListWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(GameOptionsScreen.class)
public interface GameOptionsScreenAccessor {
@Accessor
OptionListWidget getBody();
}

View File

@@ -0,0 +1,109 @@
package me.advait.screen;
import me.advait.PriorityConfig;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.world.CreateWorldScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.*;
public class PriorityPresetScreen extends Screen {
private final Screen parent;
private TextFieldWidget inputName;
private TextFieldWidget inputLayout;
public PriorityPresetScreen(Screen parent) {
super(Text.literal("Priority Key Presets"));
this.parent = parent;
}
@Override
protected void init() {
super.init();
this.clearChildren(); // ✅ Fix layout duplication after re-init
PriorityConfig.load();
int y = 30;
for (String name : PriorityConfig.presets.keySet()) {
boolean isActive = name.equals(PriorityConfig.activePreset);
this.addDrawableChild(ButtonWidget.builder(
Text.literal((isActive ? "§a✔ Selected Profile " : "") + name),
btn -> {
PriorityConfig.applyPreset(name);
PriorityConfig.save();
this.init();
}
).dimensions(width / 2 - 85, y, 170, 20).build());
y += 25;
}
y += 20; // extra spacing before add section
// // Add section title
// this.addDrawable(ButtonWidget.builder(Text.literal("Add Profile"), btn -> {}).dimensions(width / 2 - 85, y, 170, 20).build()).active = false;
// y += 25;
inputName = new TextFieldWidget(textRenderer, width / 2 - 85, y, 170, 20, Text.literal("Preset name"));
inputName.setPlaceholder(Text.literal("Preset Name").copy().formatted(Formatting.GRAY));
this.addDrawableChild(inputName);
inputLayout = new TextFieldWidget(textRenderer, width / 2 - 85, y + 25, 170, 20, Text.literal("123456789"));
inputLayout.setPlaceholder(Text.literal("Key Priority (e.g. 123456789)").copy().formatted(Formatting.GRAY));
this.addDrawableChild(inputLayout);
this.addDrawableChild(ButtonWidget.builder(Text.literal("Add Preset"), btn -> {
String name = inputName.getText().trim();
String layout = inputLayout.getText().trim();
if (!isValidLayout(layout)) {
sendToast(Text.of("§cInvalid layout. Must be 123456789 with no repeats."));
return;
}
List<Integer> slots = layout.chars().map(c -> c - '1').boxed().toList();
PriorityConfig.presets.put(name, slots);
PriorityConfig.applyPreset(name);
PriorityConfig.save();
this.init();
}).dimensions(width / 2 - 85, y + 55, 170, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.literal("Remove Selected Profile"), btn -> {
if (PriorityConfig.presets.size() <= 1) {
sendToast(Text.of("§cCan't remove last preset."));
return;
}
PriorityConfig.presets.remove(PriorityConfig.activePreset);
PriorityConfig.activePreset = PriorityConfig.presets.keySet().iterator().next();
PriorityConfig.applyPreset(PriorityConfig.activePreset);
PriorityConfig.save();
this.init();
}).dimensions(width / 2 - 85, y + 85, 170, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.literal("Back"), btn -> client.setScreen(parent))
.dimensions(width / 2 - 85, height - 30, 170, 20).build());
}
private boolean isValidLayout(String layout) {
if (layout.length() != 9 || !layout.matches("[1-9]+")) return false;
return layout.chars().distinct().count() == 9;
}
private void sendToast(Text message) {
MinecraftClient client = MinecraftClient.getInstance();
if (client != null && client.getToastManager() != null) {
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION, Text.literal("Priority Keys"), message));
}
}
@Override
public void render(net.minecraft.client.gui.DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 10, 0xFFFFFF);
context.drawTextWithShadow(this.textRenderer, Text.literal("Add Profile").formatted(Formatting.BOLD), this.width / 2 - 85, inputName.getY() - 20, 0xFFFFFF);
}
}

View File

@@ -0,0 +1,12 @@
{
"required": true,
"package": "me.advait.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"ControlsOptionsScreenMixin",
"GameOptionsScreenAccessor"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -0,0 +1,24 @@
package me.advait;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PriorityKeys implements ModInitializer {
public static final String MOD_ID = "prioritykeys";
// This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
LOGGER.info("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,15 @@
package me.advait.mixin;
import net.minecraft.server.MinecraftServer;
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(MinecraftServer.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "loadWorld")
private void init(CallbackInfo info) {
// This code is injected into the start of MinecraftServer.loadWorld()V
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,42 @@
{
"schemaVersion": 1,
"id": "prioritykeys",
"version": "${version}",
"name": "PriorityKeys",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Advait"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/prioritykeys/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"me.advait.PriorityKeys"
],
"client": [
"me.advait.PriorityKeysClient"
]
},
"mixins": [
"prioritykeys.mixins.json",
{
"config": "prioritykeys.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.16.14",
"minecraft": "~1.21.7",
"java": ">=21",
"fabric-api": "*"
},
"suggests": {
"another-mod": "*"
},
"accessWidener": "prioritykeys.accesswidener"
}

View File

@@ -0,0 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/client/gui/screen/option/GameOptionsScreen body Lnet/minecraft/client/gui/widget/OptionListWidget;

View File

@@ -0,0 +1,14 @@
{
"required": true,
"package": "me.advait.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"requireAnnotations": true
}
}