Keybinding API (#57)

This commit is contained in:
Adrian Siekierka 2019-01-06 13:28:09 +01:00
parent 1345c08cb8
commit c42667b035
7 changed files with 313 additions and 0 deletions

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.keybinding;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Identifier;
/**
* Expanded version of {@link KeyBinding} for use by Fabric mods.
*
* *ALL* instantiated FabricKeyBindings should be registered in
* {@link KeyBindingRegistry#register(FabricKeyBinding)}!
*/
public class FabricKeyBinding extends KeyBinding {
protected FabricKeyBinding(Identifier id, InputUtil.Type type, int code, String category) {
super("key." + id.toString().replace(':', '.'), type, code, category);
}
public static class Builder {
protected final FabricKeyBinding binding;
protected Builder(FabricKeyBinding binding) {
this.binding = binding;
}
public FabricKeyBinding build() {
return binding;
}
public static Builder create(Identifier id, InputUtil.Type type, int code, String category) {
return new Builder(new FabricKeyBinding(id, type, code, category));
}
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.keybinding;
import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
import net.minecraft.client.settings.KeyBinding;
/**
* Interface for registering key bindings.
*
* @see KeyBinding
*/
public interface KeyBindingRegistry {
static KeyBindingRegistry INSTANCE = KeyBindingRegistryImpl.INSTANCE;
/**
* Add a new key binding category.
* @param categoryName The key binding category name.
* @return True if a new category was added.
*/
boolean addCategory(String categoryName);
/**
* Register a new key binding.
* @param binding The key binding.
* @return True if a new key binding was registered.
*/
boolean register(FabricKeyBinding binding);
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.client.keybinding;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.settings.KeyBinding;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class KeyBindingRegistryImpl implements KeyBindingRegistry {
public static final KeyBindingRegistryImpl INSTANCE = new KeyBindingRegistryImpl();
private static final Logger LOGGER = LogManager.getLogger();
private Map<String, Integer> cachedCategoryMap;
private List<FabricKeyBinding> fabricKeyBindingList;
private KeyBindingRegistryImpl() {
fabricKeyBindingList = new ArrayList<>();
}
private Map<String, Integer> getCategoryMap() {
if (cachedCategoryMap == null) {
try {
//noinspection JavaReflectionMemberAccess
Method m = KeyBinding.class.getDeclaredMethod("fabric_getCategoryMap");
m.setAccessible(true);
//noinspection unchecked
cachedCategoryMap = (Map<String, Integer>) m.invoke(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (cachedCategoryMap == null) {
throw new RuntimeException("Cached key binding category map missing!");
}
}
return cachedCategoryMap;
}
private boolean hasCategory(String categoryName) {
return getCategoryMap().containsKey(categoryName);
}
@Override
public boolean addCategory(String categoryName) {
Map<String, Integer> map = getCategoryMap();
if (map.containsKey(categoryName)) {
return false;
}
Optional<Integer> largest = map.values().stream().max(Integer::compareTo);
int largestInt = largest.orElse(0);
map.put(categoryName, largestInt + 1);
return true;
}
@Override
public boolean register(FabricKeyBinding binding) {
for (KeyBinding exBinding : fabricKeyBindingList) {
if (exBinding == binding) {
return false;
} else if (exBinding.method_1431().equals(binding.method_1431())) {
throw new RuntimeException("Attempted to register two key bindings with equal ID: " + binding.method_1431() + "!");
}
}
if (!hasCategory(binding.method_1423())) {
LOGGER.warn("Tried to register key binding with unregistered category '" + binding.method_1423() + "' - please use addCategory to ensure intended category ordering!");
addCategory(binding.method_1423());
}
fabricKeyBindingList.add(binding);
return true;
}
public KeyBinding[] process(KeyBinding[] keysAll) {
List<KeyBinding> newKeysAll = new ArrayList<>();
for (KeyBinding binding : keysAll) {
if (!(binding instanceof FabricKeyBinding)) {
newKeysAll.add(binding);
}
}
newKeysAll.addAll(fabricKeyBindingList);
return newKeysAll.toArray(new KeyBinding[0]);
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.client.keybinding;
import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
import net.minecraft.client.settings.GameOptions;
import net.minecraft.client.settings.KeyBinding;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameOptions.class)
public class MixinGameOptions {
@Shadow
public KeyBinding[] keysAll;
@Inject(at = @At("HEAD"), method = "load()V")
public void loadHook(CallbackInfo info) {
keysAll = KeyBindingRegistryImpl.INSTANCE.process(keysAll);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.client.keybinding;
import net.minecraft.client.settings.KeyBinding;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Map;
@Mixin(KeyBinding.class)
public class MixinKeyBinding {
@Shadow
private static Map<String, Integer> field_1656;
private static Map<String, Integer> fabric_getCategoryMap() {
return field_1656;
}
}

View file

@ -7,6 +7,8 @@
"bugfix.MixinBiomeColors",
"client.itemgroup.MixinItemGroup",
"client.itemgroup.MixinCreativePlayerInventoryGui",
"client.keybinding.MixinGameOptions",
"client.keybinding.MixinKeyBinding",
"client.model.MixinModelLoader",
"client.render.MixinBlockColorMap",
"client.render.MixinBlockEntityRenderManager",

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.keybinding;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Identifier;
public class KeyBindingModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
KeyBindingRegistry.INSTANCE.addCategory("fabric.test");
KeyBindingRegistry.INSTANCE.register(
FabricKeyBinding.Builder.create(
new Identifier("fabric:test"),
InputUtil.Type.KEY_KEYBOARD,
37,
"fabric.test"
).build()
);
}
}