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

Fixed search field in controls loosing the text when window is resized #44

Open
wants to merge 13 commits into
base: 1.17
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Moved keybindings for extra functionality into amecs
  • Loading branch information
Klotzi111 committed Sep 16, 2021
commit 3b152559751e2aa1e3998309456fe7d165440c48
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ yarn_mappings = 10
loader_version = 0.11.6

#Mod properties
mod_version = 1.3.4
mod_version = 1.4.0
maven_group = de.siphalor.amecs
archives_base_name = amecs

Expand Down
86 changes: 75 additions & 11 deletions src/main/java/de/siphalor/amecs/Amecs.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
package de.siphalor.amecs;

import java.util.Arrays;
import java.util.Locale;
import java.util.*;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;

import de.siphalor.amecs.api.KeyBindingUtils;
import de.siphalor.amecs.api.KeyModifiers;
import de.siphalor.amecs.impl.AmecsAPI;
import de.siphalor.amecs.api.input.InputEventHandler;
import de.siphalor.amecs.api.input.InputHandlerManager;
import de.siphalor.amecs.impl.duck.IKeyBindingEntry;
import de.siphalor.amecs.keybinding.DropEntireStackKeyBinding;
import de.siphalor.amecs.keybinding.HotbarScrollKeyBinding;
import de.siphalor.amecs.keybinding.SkinLayerKeyBinding;
import de.siphalor.amecs.keybinding.ToggleAutoJumpKeyBinding;
import de.siphalor.amecs.mixin.ControlsListWidgetKeyBindingEntryAccessor;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.api.Version;
import net.minecraft.client.gui.screen.option.ControlsListWidget;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.entity.PlayerModelPart;
Expand All @@ -37,38 +45,94 @@ public class Amecs implements ClientModInitializer {
/**
* The mod id of Amecs
*/
@SuppressWarnings("WeakerAccess")
public static final String MOD_ID = "amecs";
public static final String MOD_NAME_SHORT = "Amecs";

private static final String LOGGER_PREFIX = "[" + MOD_NAME_SHORT + "] ";
private static final Logger LOGGER = LogManager.getLogger();

public static Version MINECRAFT_VERSION = null;
public static SemanticVersion SEMANTIC_MINECRAFT_VERSION = null;

private static final String SKIN_LAYER_CATEGORY = MOD_ID + ".key.categories.skin_layers";
private static final String MOVEMENT_CATEGORY = "key.categories.movement";
private static final String INVENTORY_CATEGORY = "key.categories.inventory";

// keybindings
/**
* this list is that we can (but not actually doing right now) remove/unregister or re-register them at a later point in time
*/
public static List<KeyBinding> ALL_KEYBINDINGS = new ArrayList<>();

public static DropEntireStackKeyBinding KEYBINDING_DROP_STACK;
// -keybindings

private static String makeKeyID(String keyName) {
return "key." + MOD_ID + "." + keyName;
}

private static void getMinecraftVersion() {
Optional<ModContainer> minecraftModContainer = FabricLoader.getInstance().getModContainer("minecraft");
if (!minecraftModContainer.isPresent()) {
throw new IllegalStateException("Minecraft not available?!?");
}
MINECRAFT_VERSION = minecraftModContainer.get().getMetadata().getVersion();
if (MINECRAFT_VERSION instanceof SemanticVersion) {
SEMANTIC_MINECRAFT_VERSION = (SemanticVersion) MINECRAFT_VERSION;
} else {
log(Level.WARN, "Minecraft version is no SemVer. This will cause problems!");
}
}

@Override
public void onInitializeClient() {
KeyBindingHelper.registerKeyBinding(new ToggleAutoJumpKeyBinding(makeKeyID("toggle_auto_jump"), InputUtil.Type.KEYSYM, 66, MOVEMENT_CATEGORY, new KeyModifiers()));
getMinecraftVersion();

Arrays.stream(PlayerModelPart.values())
.map(playerModelPart -> new SkinLayerKeyBinding(makeKeyID("toggle_" + playerModelPart.getName().toLowerCase(Locale.ENGLISH)), InputUtil.Type.KEYSYM, -1, SKIN_LAYER_CATEGORY, playerModelPart))
.forEach(KeyBindingHelper::registerKeyBinding);
VersionedLogicMethodHelper.initLogicMethodsForClasses(Arrays.asList(HotbarScrollKeyBinding.class, DropEntireStackKeyBinding.class));

KeyBindingHelper.registerKeyBinding(AmecsAPI.KEYBINDING_SCROLL_UP);
KeyBindingHelper.registerKeyBinding(AmecsAPI.KEYBINDING_SCROLL_DOWN);
createKeyBindings();
}

// keybindings
private static boolean registerKeyBinding(KeyBinding keyBinding) {
if (KeyBindingHelper.registerKeyBinding(keyBinding) == keyBinding) {
ALL_KEYBINDINGS.add(keyBinding);
return true;
}
return false;
}

KeyBindingHelper.registerKeyBinding(AmecsAPI.KEYBINDING_DROP_STACK);
private static <K extends KeyBinding & InputEventHandler> void registerKeyBindingWithHandler(K keyBinding) {
if (registerKeyBinding(keyBinding)) {
InputHandlerManager.registerInputEventHandler(keyBinding);
}
}

private static void createKeyBindings() {
// auto jump
registerKeyBinding(new ToggleAutoJumpKeyBinding(makeKeyID("toggle_auto_jump"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_B, MOVEMENT_CATEGORY, new KeyModifiers()));

// skin layers
Arrays.stream(PlayerModelPart.values())
.map(playerModelPart -> new SkinLayerKeyBinding(makeKeyID("toggle_" + playerModelPart.getName().toLowerCase(Locale.ENGLISH)), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, SKIN_LAYER_CATEGORY,
playerModelPart))
.forEach(Amecs::registerKeyBinding);

// hotbar scroll
registerKeyBindingWithHandler(new HotbarScrollKeyBinding(makeKeyID("hotbar.scroll.up"), InputUtil.Type.MOUSE, KeyBindingUtils.MOUSE_SCROLL_UP, INVENTORY_CATEGORY, new KeyModifiers(), true));
registerKeyBindingWithHandler(new HotbarScrollKeyBinding(makeKeyID("hotbar.scroll.down"), InputUtil.Type.MOUSE, KeyBindingUtils.MOUSE_SCROLL_DOWN, INVENTORY_CATEGORY, new KeyModifiers(), false));

// drop entire stack
// we intentionally do not register the drop stack keybinding for input handling because it is called from MixinMinecraftClient
KEYBINDING_DROP_STACK = new DropEntireStackKeyBinding(makeKeyID("drop.stack"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_Q, INVENTORY_CATEGORY, new KeyModifiers().setControl(true));
registerKeyBinding(KEYBINDING_DROP_STACK);
}

public static void sendToggleMessage(PlayerEntity playerEntity, boolean value, Text option) {
playerEntity.sendMessage(new TranslatableText("amecs.toggled." + (value ? "on" : "off"), option), true);
}

// controls gui search
public static boolean entryKeyMatches(ControlsListWidget.KeyBindingEntry entry, String keyFilter) {
if (keyFilter == null) {
return true;
Expand Down
132 changes: 132 additions & 0 deletions src/main/java/de/siphalor/amecs/VersionedLogicMethodHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package de.siphalor.amecs;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.logging.log4j.Level;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.api.VersionParsingException;

@Environment(EnvType.CLIENT)
public class VersionedLogicMethodHelper {
Comment on lines +19 to +20
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this branch able to run on all the supported Minecraft versions (1.14-1.17)?

If not then this class should come in a separate pull request.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not test it with versions other than 1.17.1. But I do not expect the logic for item dropping and mouse scrolling to change in versions down to 1.8. But that should be checked to be sure

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just discovered that drop logic changed a little bit from mc 1.14 to 1.15 and is now bytecode incompatible with the newer versions. But I will fix it. It make 1.17 the base/compile version and call the 1.14 version's variant via reflection

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the 1.14 logic and call the method via reflection. But I did not test it with a mc 1.14 version yet. You could test it when backporting to 1.14


public static void initLogicMethodsForClasses(List<Class<?>> classes) {
for (Class<?> clazz : classes) {
List<MethodFieldAndName> fieldAndNames = getMethodFieldAndNamesForClass(clazz);
for (MethodFieldAndName fn : fieldAndNames) {
initLogicMethod(clazz, fn);
}
}
}

public static class MethodFieldAndName {
public final Field methodField;
public final String logicMethodNamePrefix;

public MethodFieldAndName(Field methodField, String logicMethodNamePrefix) {
this.methodField = methodField;
this.logicMethodNamePrefix = logicMethodNamePrefix;
}
}

public static final String LOGIC_METHOD_SEARCH_FIELD_NAME_PREFIX = "Method_";
public static final String LOGIC_METHOD_SEARCH_FIELD_TARGET_NAME_PREFIX_SUFFIX = "_PREFIX";

public static List<MethodFieldAndName> getMethodFieldAndNamesForClass(Class<?> clazz) {
List<MethodFieldAndName> ret = new ArrayList<>();
for (Field f : clazz.getDeclaredFields()) {
int modifiers = f.getModifiers();
if (!Modifier.isStatic(modifiers)) {
continue;
}
String fName = f.getName();
if (!fName.startsWith(LOGIC_METHOD_SEARCH_FIELD_NAME_PREFIX)) {
continue;
}
if (!ReflectionExceptionProxiedMethod.class.isAssignableFrom(f.getType())) {
continue;
}

// found method field
// now search for its prefix
try {
Field prefixField = clazz.getDeclaredField(fName + LOGIC_METHOD_SEARCH_FIELD_TARGET_NAME_PREFIX_SUFFIX);
prefixField.setAccessible(true);
String logicMethodPrefix = (String) prefixField.get(null);
ret.add(new MethodFieldAndName(f, logicMethodPrefix));
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
Amecs.log(Level.WARN, "Found logic method search method in class \"" + clazz.getName() + "\" but no associated logic method name prefix");
e.printStackTrace();
continue;
}
}
return ret;
}

public static Method getLogicMethod(Class<?> clazz, String methodPrefix) {
TreeMap<SemanticVersion, Method> methodAndVersions = new TreeMap<>();
for (Method m : clazz.getDeclaredMethods()) {
String methodName = m.getName();
if (methodName.startsWith(methodPrefix)) {
String versionString = methodName.substring(methodPrefix.length()).replace('_', '.');
try {
SemanticVersion version = SemanticVersion.parse(versionString);
methodAndVersions.put(version, m);
} catch (VersionParsingException e) {
Amecs.log(Level.ERROR, "Could not parse semantic version for logic method: " + methodName);
}
}
}
if (Amecs.SEMANTIC_MINECRAFT_VERSION == null) {
return methodAndVersions.firstEntry().getValue();
}
Entry<SemanticVersion, Method> suitable = methodAndVersions.floorEntry(Amecs.SEMANTIC_MINECRAFT_VERSION);
if (suitable != null) {
return suitable.getValue();
}
return null;
}

public static void initLogicMethod(Class<?> clazz, MethodFieldAndName fieldAndName) {
Method logicMethod = getLogicMethod(clazz, fieldAndName.logicMethodNamePrefix);
if (logicMethod == null) {
throw new IllegalStateException("No \"" + fieldAndName.logicMethodNamePrefix + "\" method available for minecraft Version: " + Amecs.SEMANTIC_MINECRAFT_VERSION.getFriendlyString());
}
ReflectionExceptionProxiedMethod proxiedMethod = new ReflectionExceptionProxiedMethod(logicMethod);
try {
fieldAndName.methodField.setAccessible(true);
fieldAndName.methodField.set(null, proxiedMethod);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException("Failed to set the logic method for class: \"" + clazz.getName() + "\"");
}
}

public static class ReflectionExceptionProxiedMethod {
public final Method method;

public ReflectionExceptionProxiedMethod(Method method) {
method.setAccessible(true);
this.method = method;
}

public Object invoke(Object instance, Object... args) {
try {
return method.invoke(instance, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
Amecs.log(Level.ERROR, "Error while executing: \"" + method.getName() + "\" in class: \"" + method.getDeclaringClass().getName() + "\"");
e.printStackTrace();
}
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package de.siphalor.amecs.keybinding;

import de.siphalor.amecs.VersionedLogicMethodHelper.ReflectionExceptionProxiedMethod;
import de.siphalor.amecs.api.AmecsKeyBinding;
import de.siphalor.amecs.api.KeyModifiers;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Hand;

@Environment(EnvType.CLIENT)
public class DropEntireStackKeyBinding extends AmecsKeyBinding implements DropItemStackTriggerListener {
@SuppressWarnings("unused") // used via reflection
private static final String Method_dropEntireStackLogic_PREFIX = "dropEntireStackLogic$";
private static ReflectionExceptionProxiedMethod Method_dropEntireStackLogic;

public DropEntireStackKeyBinding(String id, InputUtil.Type type, int code, String category, KeyModifiers defaultModifiers) {
super(id, type, code, category, defaultModifiers);
}

// TODO: check if it is really equal for all versions between 1.8 - 1.17.1
// from minecraft code: MinecraftClient with feedback addition
@SuppressWarnings("unused") // used via reflection
private boolean dropEntireStackLogic$1_8(MinecraftClient client) {
ClientPlayerEntity player = client.player;

// true to always drop an entire stack
if (!player.isSpectator() && player.dropSelectedItem(true)) {
player.swingHand(Hand.MAIN_HAND);
return true;
}
return false;
}

// TODO: copy the byteCode from MinecraftClient in order to remove this version check
private boolean dropEntireStackLogic_currentVersion(MinecraftClient client) {
return (boolean) Method_dropEntireStackLogic.invoke(this, client);
}

// this method is NOT called by the InputHandlerManger (because we do not register it there) it is called from MixinMinecraftClient
@Override
public boolean handleDropItemStackEvent(MinecraftClient client) {
boolean dropped = false;
while (wasPressed()) {
dropped |= dropEntireStackLogic_currentVersion(client);
}
return dropped;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.siphalor.amecs.keybinding;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;

@Environment(EnvType.CLIENT)
public interface DropItemStackTriggerListener {

public boolean handleDropItemStackEvent(MinecraftClient client);

}
Loading