This commit is contained in:
80
src/client/java/me/advait/PriorityConfig.java
Normal file
80
src/client/java/me/advait/PriorityConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
59
src/client/java/me/advait/PriorityKeysClient.java
Normal file
59
src/client/java/me/advait/PriorityKeysClient.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
109
src/client/java/me/advait/screen/PriorityPresetScreen.java
Normal file
109
src/client/java/me/advait/screen/PriorityPresetScreen.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
12
src/client/resources/prioritykeys.client.mixins.json
Normal file
12
src/client/resources/prioritykeys.client.mixins.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "me.advait.mixin.client",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"ControlsOptionsScreenMixin",
|
||||
"GameOptionsScreenAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
24
src/main/java/me/advait/PriorityKeys.java
Normal file
24
src/main/java/me/advait/PriorityKeys.java
Normal 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!");
|
||||
}
|
||||
}
|
||||
15
src/main/java/me/advait/mixin/ExampleMixin.java
Normal file
15
src/main/java/me/advait/mixin/ExampleMixin.java
Normal 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
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/assets/prioritykeys/icon.png
Normal file
BIN
src/main/resources/assets/prioritykeys/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
42
src/main/resources/fabric.mod.json
Normal file
42
src/main/resources/fabric.mod.json
Normal 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"
|
||||
}
|
||||
3
src/main/resources/prioritykeys.accesswidener
Normal file
3
src/main/resources/prioritykeys.accesswidener
Normal file
@@ -0,0 +1,3 @@
|
||||
accessWidener v2 named
|
||||
|
||||
accessible field net/minecraft/client/gui/screen/option/GameOptionsScreen body Lnet/minecraft/client/gui/widget/OptionListWidget;
|
||||
14
src/main/resources/prioritykeys.mixins.json
Normal file
14
src/main/resources/prioritykeys.mixins.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "me.advait.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"ExampleMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"overwrites": {
|
||||
"requireAnnotations": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user