/** * Copyright (c) Lambda Innovation, 2013-2015 * 本作品版权由Lambda Innovation所有。 * http://www.li-dev.cn/ * * This project is open-source, and it is distributed under * the terms of GNU General Public License. You can modify * and distribute freely as long as you follow the license. * 本项目是一个开源项目,且遵循GNU通用公共授权协议。 * 在遵照该协议的情况下,您可以自由传播和修改。 * http://www.gnu.org/licenses/gpl.html */ package cn.liutils.util.client; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import cn.annoreg.core.Registrant; import cn.annoreg.mc.RegEventHandler; import cn.annoreg.mc.RegEventHandler.Bus; import cn.annoreg.mc.RegInit; import cn.liutils.core.LIUtils; import cn.liutils.util.generic.RegistryUtils; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.settings.KeyBinding; import net.minecraft.util.IntHashMap; /** * This class overrides(i.e.Disables) vanilla minecraft's control on a certain key. * It is currently intented to be used DURING gameplay and will unlock all overrides when any GUI is present. * There are two ways to use ControlOverrider: activate/deactivate a key manually, or add key event filters into it. * @author WeAthFolD */ @SideOnly(Side.CLIENT) @Registrant @RegInit @RegEventHandler(Bus.Forge) public class ControlOverrider { private static IntHashMap kbMap; private static Field pressedField; private static Field kbMapField; private static Map<Integer, Override> activeOverrides = new HashMap(); private static boolean completeOverriding; public static void init() { try { kbMapField = RegistryUtils.getObfField(KeyBinding.class, "hash", "field_74514_b"); kbMap = getOriginalKbMap(); pressedField = RegistryUtils.getObfField(KeyBinding.class, "pressed", "field_74513_e"); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(kbMapField, kbMapField.getModifiers() & (~Modifier.FINAL)); } catch(Exception e) { error("init", e); e.printStackTrace(); } } private static IntHashMap createCopy(IntHashMap from, IntHashMap to) { if(to == null) to = new IntHashMap(); // Awkward, but who knows if this is faster than reflection? for(int i = -100; i <= 250; ++i) { if(from.containsItem(i)) to.addKey(i, from.lookup(i)); } return to; } private static IntHashMap getOriginalKbMap() { try { return (IntHashMap) kbMapField.get(null); } catch (Exception e) { error("getOriginalKbMap", e); e.printStackTrace(); return null; } } // SUPERHACKTECH Starts /** * A complete override stops ALL minecraft keys to function. Currently you can open up complete override globally only once. */ public static void startCompleteOverride() { if(!completeOverriding) { completeOverriding = true; kbMap = createCopy(kbMap, null); getOriginalKbMap().clearMap(); } } public static void endCompleteOverride() { if(completeOverriding) { completeOverriding = false; createCopy(kbMap, getOriginalKbMap()); kbMap = getOriginalKbMap(); } else { error("Try to stop complete override while not overriding at all", new RuntimeException()); } } // SUPERHACKTECH Ends public static void override(int keyID) { if(activeOverrides.containsKey(keyID)) { activeOverrides.get(keyID).count++; if(activeOverrides.get(keyID).count > 100) LIUtils.log.warn("Over 100 override locks for " + keyID + ". Might be a programming error?"); log("Override increment " + "[" + keyID + "]" + activeOverrides.get(keyID).count); return; } KeyBinding kb = (KeyBinding) kbMap.removeObject(keyID); if(kb != null) { try { pressedField.set(kb, false); } catch (Exception e) { e.printStackTrace(); } //kb.setKeyCode(-1); activeOverrides.put(keyID, new Override(kb)); log("Override new [" + keyID + "]"); } else { log("Override ignored [" + keyID + "]"); } } public static void removeOverride(int keyID) { Override ovr = activeOverrides.get(keyID); if(ovr == null) return; if(ovr.count > 1) { ovr.count--; log("Override decrement [" + keyID + "]" + ovr.count); } else { activeOverrides.remove(keyID); ovr.kb.setKeyCode(keyID); kbMap.addKey(keyID, ovr.kb); log("Override remove [" + keyID + "]"); } } private static void releaseLocks() { for(Map.Entry<Integer, Override> ao: activeOverrides.entrySet()) { kbMap.addKey(ao.getKey(), ao.getValue().kb); } } private static void restoreLocks() { for(Map.Entry<Integer, Override> ao: activeOverrides.entrySet()) { try { pressedField.set(ao.getValue().kb, false); } catch (Exception e) { e.printStackTrace(); } kbMap.removeObject(ao.getKey()); } } GuiScreen lastTickGui; @SubscribeEvent public void onClientTick(ClientTickEvent cte) { GuiScreen cgs = Minecraft.getMinecraft().currentScreen; if(lastTickGui == null && cgs != null) { releaseLocks(); } if(lastTickGui != null && cgs == null) { restoreLocks(); } lastTickGui = cgs; } private static void log(String s) { if(LIUtils.DEBUG) LIUtils.log.info(s); } private static void error(String s, Exception e) { LIUtils.log.error("ControlOverrider error: " + s, e); } private static class Override { final KeyBinding kb; int count; public Override(KeyBinding _kb) { kb = _kb; count = 1; } } }