Skip to content

Commit

Permalink
Improve & generalise keybind code (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
hashalite authored Jan 20, 2024
2 parents 6050e13 + 00a5b73 commit dd758ea
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 107 deletions.
72 changes: 25 additions & 47 deletions common/src/main/java/net/xolt/freecam/Freecam.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package net.xolt.freecam;

import me.shedaniel.autoconfig.AutoConfig;
import net.minecraft.client.CameraType;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.KeyboardInput;
import net.minecraft.client.renderer.texture.Tickable;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.xolt.freecam.config.ModBindings;
import net.xolt.freecam.config.ModConfig;
import net.xolt.freecam.tripod.TripodRegistry;
import net.xolt.freecam.tripod.TripodSlot;
Expand All @@ -17,20 +18,15 @@
import net.xolt.freecam.variant.api.BuildVariant;
import org.jetbrains.annotations.Nullable;

import static net.xolt.freecam.config.ModBindings.*;

public class Freecam {

public static final Minecraft MC = Minecraft.getInstance();
public static final String MOD_ID = "freecam";
private static final long TOGGLE_KEY_MAX_TICKS = 10;

private static boolean freecamEnabled = false;
private static boolean tripodEnabled = false;
private static boolean playerControlEnabled = false;
private static boolean disableNextTick = false;
private static boolean toggleKeyUsedWhileHeld = false;
private static long toggleKeyHeldTicks = 0;
private static final TripodRegistry tripods = new TripodRegistry();
private static TripodSlot activeTripod = TripodSlot.NONE;
private static FreeCamera freeCamera;
Expand All @@ -56,47 +52,7 @@ public static void preTick(Minecraft mc) {
}

public static void postTick(Minecraft mc) {
if (KEY_TOGGLE.isDown()) {
// Count held ticks, so we can toggle on release
toggleKeyHeldTicks++;
KEY_TOGGLE.reset();

// Handle <toggle_key>+<hotbar_key> combos
for (KeyMapping combo : mc.options.keyHotbarSlots) {
while (combo.consumeClick()) {
toggleTripod(TripodSlot.ofKeyCode(combo.getDefaultKey().getValue()));
toggleKeyUsedWhileHeld = true;
}
}
}
// Check if toggle was pressed, and is now released
else if (KEY_TOGGLE.consumeClick() || toggleKeyHeldTicks > 0) {
// Only toggle if the key wasn't used (or held too long)
if (!toggleKeyUsedWhileHeld && toggleKeyHeldTicks < TOGGLE_KEY_MAX_TICKS) {
toggle();
}
// Reset state
KEY_TOGGLE.reset();
toggleKeyHeldTicks = 0;
toggleKeyUsedWhileHeld = false;
}

// Handle <reset_key>+<hotbar_key> combos
if (KEY_TRIPOD_RESET.isDown()) {
for (KeyMapping key : mc.options.keyHotbarSlots) {
while (key.consumeClick()) {
resetCamera(TripodSlot.ofKeyCode(key.getDefaultKey().getValue()));
}
}
}

while (KEY_PLAYER_CONTROL.consumeClick()) {
switchControls();
}

while (KEY_CONFIG_GUI.consumeClick()) {
mc.setScreen(AutoConfig.getConfigScreen(ModConfig.class, mc.screen).get());
}
ModBindings.forEach(Tickable::tick);
}

public static void onDisconnect() {
Expand All @@ -106,6 +62,28 @@ public static void onDisconnect() {
tripods.clear();
}

public static boolean activateTripodHandler() {
boolean activated = false;
for (KeyMapping combo : MC.options.keyHotbarSlots) {
while (combo.consumeClick()) {
toggleTripod(TripodSlot.ofKeyCode(combo.getDefaultKey().getValue()));
activated = true;
}
}
return activated;
}

public static boolean resetTripodHandler() {
boolean reset = false;
for (KeyMapping key : MC.options.keyHotbarSlots) {
while (key.consumeClick()) {
resetCamera(TripodSlot.ofKeyCode(key.getDefaultKey().getValue()));
reset = true;
}
}
return reset;
}

public static void toggle() {
if (tripodEnabled) {
toggleTripod(activeTripod);
Expand Down
90 changes: 31 additions & 59 deletions common/src/main/java/net/xolt/freecam/config/ModBindings.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
import me.shedaniel.autoconfig.AutoConfig;
import net.xolt.freecam.Freecam;
import net.xolt.freecam.config.keys.FreecamKeyMapping;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
Expand All @@ -12,89 +13,60 @@
import java.util.Spliterator;
import java.util.function.Consumer;

import static net.xolt.freecam.Freecam.MC;
import static net.xolt.freecam.config.keys.FreecamKeyMappingBuilder.builder;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_F4;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_UNKNOWN;

public enum ModBindings {

KEY_TOGGLE("toggle", GLFW_KEY_F4),
KEY_PLAYER_CONTROL("playerControl"),
KEY_TRIPOD_RESET("tripodReset"),
KEY_CONFIG_GUI("configGui");

private final Supplier<KeyMapping> lazyMapping;

ModBindings(String translationKey) {
this(translationKey, InputConstants.Type.KEYSYM, GLFW_KEY_UNKNOWN);
}

ModBindings(String translationKey, int code) {
this(translationKey, InputConstants.Type.KEYSYM, code);
}

ModBindings(String translationKey, InputConstants.Type type) {
this(translationKey, type, GLFW_KEY_UNKNOWN);
}

ModBindings(String translationKey, InputConstants.Type type, int code) {
this.lazyMapping = Suppliers.memoize(() ->
new KeyMapping("key.freecam." + translationKey, type, code, "category.freecam.freecam"));
}

/**
* @return the result of calling {@link KeyMapping#isDown()} on the represented {@link KeyMapping}.
* @see KeyMapping#isDown()
*/
public boolean isDown() {
return get().isDown();
}

/**
* @return the result of calling {@link KeyMapping#consumeClick()} on the represented {@link KeyMapping}.
* @see KeyMapping#consumeClick()
*/
public boolean consumeClick() {
return get().consumeClick();
}

/**
* Reset whether the key was pressed.
* <p>
* Note: Cannot use {@link KeyMapping#release()} because it doesn't work as expected.
*/
@SuppressWarnings("StatementWithEmptyBody")
public void reset() {
final KeyMapping key = get();
while (key.consumeClick()) {}
KEY_TOGGLE(() -> builder("toggle")
.action(Freecam::toggle)
.holdAction(Freecam::activateTripodHandler)
.defaultKey(GLFW_KEY_F4)
.build()),
KEY_PLAYER_CONTROL(() -> builder("playerControl")
.action(Freecam::switchControls)
.build()),
KEY_TRIPOD_RESET(() -> builder("tripodReset")
.holdAction(Freecam::resetTripodHandler)
.build()),
KEY_CONFIG_GUI(() -> builder("configGui")
.action(() -> MC.setScreen(AutoConfig.getConfigScreen(ModConfig.class, MC.screen).get()))
.build());

private final Supplier<FreecamKeyMapping> lazyMapping;

ModBindings(Supplier<FreecamKeyMapping> mappingSupplier) {
lazyMapping = Suppliers.memoize(mappingSupplier);
}

/**
* Lazily get the actual {@link KeyMapping} represented by this enum value.
* Lazily get the actual {@link FreecamKeyMapping} represented by this enum value.
* <p>
* Values are constructed if they haven't been already.
*
* @return the actual {@link KeyMapping}.
* @return the actual {@link FreecamKeyMapping}.
*/
public KeyMapping get() {
public FreecamKeyMapping get() {
return lazyMapping.get();
}

/**
* Calls {@code action} using each {@link KeyMapping} owned by this enum.
* Calls {@code action} using each {@link FreecamKeyMapping} owned by this enum.
* <p>
* Values are constructed if they haven't been already.
* <p>
* Static implementation of {@link Iterable#forEach(Consumer)}.
*/
public static void forEach(@NotNull Consumer<KeyMapping> action) {
public static void forEach(@NotNull Consumer<FreecamKeyMapping> action) {
Objects.requireNonNull(action);
iterator().forEachRemaining(action);
}

/**
* Static implementation of {@link Iterable#iterator()}.
*/
public static @NotNull Iterator<KeyMapping> iterator() {
public static @NotNull Iterator<FreecamKeyMapping> iterator() {
return Arrays.stream(values())
.map(ModBindings::get)
.iterator();
Expand All @@ -103,7 +75,7 @@ public static void forEach(@NotNull Consumer<KeyMapping> action) {
/**
* Static implementation of {@link Iterable#spliterator()}.
*/
public static @NotNull Spliterator<KeyMapping> spliterator() {
public static @NotNull Spliterator<FreecamKeyMapping> spliterator() {
return Arrays.stream(values())
.map(ModBindings::get)
.spliterator();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package net.xolt.freecam.config.keys;

import com.mojang.blaze3d.platform.InputConstants;


class FreecamComboKeyMapping extends FreecamKeyMapping {

private final Runnable action;
private final HoldAction holdAction;
private final long maxHoldTicks;

private boolean usedWhileHeld = false;
private long ticksHeld = 0;

FreecamComboKeyMapping(String translationKey, InputConstants.Type type, int code, Runnable action, HoldAction holdAction, long maxHoldTicks) {
super(translationKey, type, code);
this.action = action;
this.holdAction = holdAction;
this.maxHoldTicks = maxHoldTicks;
}

@Override
public void tick() {
if (isDown()) {
// Count held ticks, so we can run action on release
ticksHeld++;
reset();

// Handle combo actions
if (holdAction.run()) {
usedWhileHeld = true;
}
}
// Check if pressed, but now released
else if (consumeClick() || ticksHeld > 0) {
// Only run action if the key wasn't used (or held too long)
if (!usedWhileHeld && ticksHeld < maxHoldTicks) {
action.run();
}
// Reset state
reset();
ticksHeld = 0;
usedWhileHeld = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package net.xolt.freecam.config.keys;

import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.renderer.texture.Tickable;

import java.util.function.Consumer;

public class FreecamKeyMapping extends KeyMapping implements Tickable {

private final Consumer<FreecamKeyMapping> onTick;

/**
* @apiNote should only be used if overriding {@link #tick()}
*/
protected FreecamKeyMapping(String translationKey, InputConstants.Type type, int code) {
this(translationKey, type, code, null);
}

FreecamKeyMapping(String translationKey, InputConstants.Type type, int code, Consumer<FreecamKeyMapping> onTick) {
super("key.freecam." + translationKey, type, code, "category.freecam.freecam");
this.onTick = onTick;
}

@Override
public void tick() {
onTick.accept(this);
}

/**
* Reset whether the key was pressed.
* <p>
* @implNote Cannot use {@link KeyMapping#release()} because it doesn't work as expected.
*/
@SuppressWarnings("StatementWithEmptyBody")
public void reset() {
while (consumeClick()) {}
}
}
Loading

0 comments on commit dd758ea

Please sign in to comment.