← Назад к ресурсам

Combind

Expand vanilla key binds with combo support

Modrinth game-mechanics utility

Описание

This mod expands vanilla keybinds with combo support, letting you bind any action to a chord or a key sequence instead of a single key. It is **client-side only** and requires no server-side installation. ![Key Binds Screen 26.3](https://cdn.modrinth.com/data/cached_images/046b67d514ea3755e16146028da642222a3106ba.jpeg) ## Features ### Chord Combos - Bind actions to key combinations such as **Left Shift + E**, **Left Control + Left Button**, or any other held key + trigger pair - Any key or mouse button can be the trigger or a held key in a combo. The last key pressed during recording becomes the trigger, and all previously held keys complete the combination - No limits! Hold as many keys as you want for as wild a combination as you need - Can be followed by a sequence combo ### Sequence Combos - Bind actions to repeated key presses such as **double-tap Q** or **triple-tap Space** - A configurable time window determines how quickly the taps must occur - No limits! Bind to as many taps as you want ### Controls Screen Integration - Combo recording is embedded directly in the vanilla **Controls > Key Binds** screen, meaning no separate UI - Press **Escape** to unbind a key ### Configuration - **Allow Conflicts**: When disabled, pressing a combo will not also trigger bindings whose keys are fully contained in it. For example, binding both **E** and **Left Shift + E** means pressing **Left Shift + E** would normally fire both. Disabling this ensures only **Left Shift + E** fires. The same applies to sequences: if a single press and a multi-tap are bound to the same key (e.g. **Q** and **Q Q**), pressing that key will only trigger the single press binding. Two bindings like **Left Control + E** and **Left Shift + E** are not considered conflicting, since pressing one can never accidentally fire the other. They only both trigger if you deliberately hold **Left Control + Left Shift + E**. This setting only applies to bindings that share the same trigger key (default: on) - **Sequence Window**: How quickly you must press the key repeatedly in-game for a sequence to register (default: 400 ms) - **Sequence Recording Window**: How long the recorder waits between taps when capturing a sequence in the Controls screen (default: 200 ms) - Settings are saved to `config/combind.json` ## Use Cases This mod shines in modpacks where keys are scarce and every binding needs to be deliberate, but works just as well in vanilla for extra flexibility. ### A Practical Example **Double-tap Q (Q Q)** (or even **triple-tap Q (Q Q Q)**!) to drop items instead of a single press. No more accidental drops. ## Compatibility This mod has no required dependencies beyond Fabric API. It works with: - **[ModMenu](https://modrinth.com/mod/modmenu)** _(recommended)_ — adds a config button to the mod list as an alternative to manually editing `config/combind.json` - **[Controlling](https://modrinth.com/mod/controlling)** and **[Searchables](https://modrinth.com/mod/searchables)** — search for bindings by their combo using syntax like `key:"Left Shift + E"` or `key:"Q Q"`. Unsupported as of `combind-v1.0.0+26.3`, until both mods release a version compatible with 26.3 --- _**Note**: Some niche vanilla interactions are tied to a specific input type and may not work when rebound. For example, a mouse button bound to "Open Inventory" will not close it the way a key would, and a key bound to "Pick Block" will not support block duplication dragging inside containers the way a mouse button would._ _**Note**: If you encounter any compatibility issues, please report them on the [GitHub Issues page](https://github.com/Pemiridosa/combind/issues)._ --- ## For Mod Developers Using the Combind API Combind exposes three API classes under `net.pemiridosa.combind.api` that let your mod register key bindings with full combo support. ### Dependency Setup Add Combind as a compile-time dependency via the Modrinth Maven repository: ```groovy // build.gradle repositories { maven { url "https://api.modrinth.com/maven" } } dependencies { compileOnly "maven.modrinth:combind:VERSION" localRuntime "maven.modrinth:combind:VERSION" // optional: for dev runs } ``` Declare the dependency in `fabric.mod.json`. Use `depends` if your mod requires Combind, or `suggests` if it is optional: ```json "depends": { "combind": "*" } ``` --- ### API Classes | Class | Purpose | |---|---| | `CombindKeyBinding` | Wraps a vanilla `KeyMapping` with combo support — the main entry point | | `KeyCombo` | Describes a combo: single key, chord, sequence, or chord + sequence | | `InputKey` | Represents one physical input — `InputKey.keyboard(scancode)` or `InputKey.mouse(button)` | --- ### Examples #### Chord — held modifier(s) + trigger key Bind an action to **Left Shift + E**: ```java KeyMapping.Category category = KeyMapping.Category.register( Identifier.fromNamespaceAndPath("mymod", "mymod") ); KeyMapping someActionMapping = KeyMappingHelper.registerKeyMapping( new KeyMapping("key.mymod.someaction", InputConstants.KEY_E, category) ); // Left Shift + E CombindKeyBinding someActionBinding = CombindKeyBinding.of(someActionMapping, new KeyCombo( InputKey.keyboard(InputConstants.KEY_E), new InputKey[]{ InputKey.keyboard(InputConstants.KEY_LSHIFT) } ) ); someActionBinding .onPress(ctx -> LOGGER.info("Some action PRESSED — combo: {}", ctx.combo().getDisplayName())) .onRelease(ctx -> LOGGER.info("Some action RELEASED — combo: {}", ctx.combo().getDisplayName())); ``` #### Chord with multiple modifiers Bind an action to **Left Control + Left Shift + E**: ```java KeyMapping someActionMapping = KeyMappingHelper.registerKeyMapping( new KeyMapping("key.mymod.someaction", InputConstants.KEY_E, category) ); // Left Control + Left Shift + E CombindKeyBinding someActionBinding = CombindKeyBinding.of(someActionMapping, new KeyCombo( InputKey.keyboard(InputConstants.KEY_E), new InputKey[]{ InputKey.keyboard(InputConstants.KEY_LCONTROL), InputKey.keyboard(InputConstants.KEY_LSHIFT) } ) ); someActionBinding .onPress(ctx -> LOGGER.info("Some action PRESSED — combo: {}", ctx.combo().getDisplayName())) .onRelease(ctx -> LOGGER.info("Some action RELEASED — combo: {}", ctx.combo().getDisplayName())); ``` #### Sequence — tap the same key multiple times Bind an action to **M M** (double-tap) or **M M M** (triple-tap): ```java KeyMapping someActionMapping = KeyMappingHelper.registerKeyMapping( new KeyMapping("key.mymod.someaction", InputConstants.KEY_M, category) ); // Double-tap M (M M) CombindKeyBinding someActionBinding = CombindKeyBinding.of(someActionMapping, KeyCombo.sequence(InputKey.keyboard(InputConstants.KEY_M), 2) ); // Triple-tap M (M M M) — even safer CombindKeyBinding someActionBinding = CombindKeyBinding.of(someActionMapping, KeyCombo.sequence(InputKey.keyboard(InputConstants.KEY_M), 3) ); someActionBinding .onPress(ctx -> LOGGER.info("Some action PRESSED — combo: {}", ctx.combo().getDisplayName())) .onRelease(ctx -> LOGGER.info("Some action RELEASED — combo: {}", ctx.combo().getDisplayName())); ``` #### Chord + sequence — hold modifier(s) while multi-tapping Bind an action to **Left Shift + Q Q** (double-tap Q while holding Shift): ```java // Left Shift + Q Q CombindKeyBinding binding = CombindKeyBinding.of(mapping, new KeyCombo( InputKey.keyboard(InputConstants.KEY_Q), new InputKey[]{ InputKey.keyboard(InputConstants.KEY_LSHIFT) }, 2 ) ); ``` #### Mouse button as trigger Bind an action to **Left Control + Left Button**: ```java // Left Control + Left Mouse Button CombindKeyBinding binding = CombindKeyBinding.of(mapping, new KeyCombo( InputKey.mouse(InputConstants.MOUSE_BUTTON_LEFT), new InputKey[]{ InputKey.keyboard(InputConstants.KEY_LCONTROL) } ) ); ``` #### Single key (no explicit combo needed) `CombindKeyBinding.of(mapping)` infers the initial combo from the `KeyMapping`'s default key: ```java CombindKeyBinding binding = CombindKeyBinding.of(mapping); // defaults to the KeyMapping's key ``` #### Unbound — no key assigned by default ```java // Player must configure this in the Controls > Key Binds screen CombindKeyBinding binding = CombindKeyBinding.of(mapping, KeyCombo.unbound()); ``` --- ### Tick-Based Polling (Vanilla Style) If you prefer the standard tick-polling pattern instead of callbacks, it works unchanged via the vanilla `KeyMapping`: ```java ClientTickEvents.END_CLIENT_TICK.register(client -> { while (someActionMapping.consumeClick()) { // handle each queued click } if (someActionMapping.isDown()) { // binding is currently held } }); ``` For more details, see the full implementation at [src/client/java/net/pemiridosa/combind/api](https://github.com/Pemiridosa/combind/tree/26.3/src/client/java/net/pemiridosa/combind/api).