Hmm. I'm not really sure why this is happening. I'll look into it later.
I am sorry.
It was because I have put in a package that were specified in the TransformerExclusions annotation.
Was working when moved to a different package.
I am sorry.
It was because I have put in a package that were specified in the TransformerExclusions annotation.
Was working when moved to a different package.
~snip~
Caused by: java.lang.LinkageError: loader (instance of net/minecraft/launchwrapper/LaunchClassLoader): attempted duplicate class definition for name: "net/minecraft/client/settings/KeyBinding"
From the above, looks like you may have imported KeyBinding twice?
@SoBiohazardous:
Awesome TUT - worked great! I have a few questions, though:
- Checking for keyPressed onUpdate will send a constant stream of signals even if we set 'repeat' to false, as it just checks each tick if keyPressed is true. Do you happen to know how to process a keystroke only once for each downpress, even if held?
- Is there a way to check if Chat is open, because my keybindings still do their functions no matter what menu or chat is open.
Never mind, here's how to do it (just the first check in the 'if' statement):
From the above, looks like you may have imported KeyBinding twice?
@SoBiohazardous:
Awesome TUT - worked great! I have a few questions, though:
- Checking for keyPressed onUpdate will send a constant stream of signals even if we set 'repeat' to false, as it just checks each tick if keyPressed is true. Do you happen to know how to process a keystroke only once for each downpress, even if held?
- Is there a way to check if Chat is open, because my keybindings still do their functions no matter what menu or chat is open.
Never mind, here's how to do it (just the first check in the 'if' statement):
For your first question, yes i'm aware of that. I am still tinkering with logic trying to figure that out. You can try if you like. Try and use a system of booleans and numbers. When I figure it out, i'll let everyone know.
I actually found a problem with the keybinding, and I'm not sure if it's Bio's code or just a problem on my end, but whenever I have the chat open and use the keybinding it will run the methods.
I don't want it to run the methods while the chat or any other GUI is open lol
For your first question, yes i'm aware of that. I am still tinkering with logic trying to figure that out. You can try if you like. Try and use a system of booleans and numbers. When I figure it out, i'll let everyone know.
I'll let you know if I find out as well. I tried this in my GUI:
KEY_G is the key I bound. Strangely, ESC (1) and Inventory Key (E) both work normally, but KEY_G will freak out opening and closing the GUI. There must be something in the vanilla code... I will find it!
I actually found a problem with the keybinding, and I'm not sure if it's Bio's code or just a problem on my end, but whenever I have the chat open and use the keybinding it will run the methods.
I don't want it to run the methods while the chat or any other GUI is open lol
Any suggestions?
I have no idea why that would be. Post your code maybe?
KEY_G is the key I bound. Strangely, ESC (1) and Inventory Key (E) both work normally, but KEY_G will freak out opening and closing the GUI. There must be something in the vanilla code... I will find it!
By freak out, I just mean it will open and close the GUI very quickly continuously, as KEY_G is set to both open and close... heh. Just has to do with the keyPressed being set to true. Whenever I get around to it, I'll see about searching out a solution.
you should put a note about importing the wrong Key Binding
Just a heads up
With the standard libraries included when you start modding you may even import the wrong Block.java class
There are many of the standards imports in minecraft which has the same name as something in another lib,
one must always be careful about which import one chooses.
The error was that a Keyboard was not registered, and therefore it could not use that key. This works on single player. So I looked up my error and found this tutorial. If I put your KeyRegistry part in the main mod class or the client proxy (and use the code below), it works for single player but crashes the server before it starts.
How do you make this work on a server? Keybinding is not supposed to be called by the server, so it doesn't know what to do... I know I have to use packets somehow, but I'm at a loss.
Any ideas? Help would be greatly appreciated.
Thanks,
LittleBreadLoaf
Well if you only have one it would be 0, unless you are using multiple then you should handle them in your GuiHandler. This isn't part of my tutorial thought, its for the gui tutorial.
I want a GUI to open when I press a key, similar to inventory, but it wont open the GUI at all. I have tested with some system.out.println's that the button is being pressed, I use to have the GUI attached to a block so I know the GUI works.
public static void playerTick(EntityPlayer player) {
if (KeyBind.keyHasBeenPressed) {
System.out.println("key has been tapped");
int x = (int)player.posX;
int y = (int)player.posY;
int z = (int)player.posZ;
World world = MinecraftServer.getServer().worldServers[0];
System.out.println(x + " " + y + " " + z + " " + world);
if(!world.isRemote) {
player.openGui(ModEventHandler.instance, 0, world, x, y, z);
}
}
}
I want a GUI to open when I press a key, similar to inventory, but it wont open the GUI at all. I have tested with some system.out.println's that the button is being pressed, I use to have the GUI attached to a block so I know the GUI works.
public static void playerTick(EntityPlayer player) {
if (KeyBind.keyHasBeenPressed) {
System.out.println("key has been tapped");
int x = (int)player.posX;
int y = (int)player.posY;
int z = (int)player.posZ;
World world = MinecraftServer.getServer().worldServers[0];
System.out.println(x + " " + y + " " + z + " " + world);
if(!world.isRemote) {
player.openGui(ModEventHandler.instance, 0, world, x, y, z);
}
}
}
Try removing 'if (!world.isRemote)' - that tells it to only open the Gui on the server, but Gui is client side (and sometimes also server side, for instance, if accessing a tile entity's inventory).
@Mazetar: Don't know if you're still looking, but here's the cleanest I got my keybinding registering to look:
public class KeyBindSGT
{
public static final int NUM_KEYS_BOUND = 10;
public static final int PLUS_X = 0, MINUS_X = 1, PLUS_Z = 2, MINUS_Z = 3, OFFSET_Y = 4,
INVERT_Y = 5, RESET_OFFSET = 6, ROTATE_90 = 7, NEXT_STRUCT = 8, PREV_STRUCT = 9;
/** Maps Keyboard values to SGT KeyBinding values */
public static final Map<Integer, Integer> SGTKeyMap = new HashMap<Integer, Integer>();
public static void init()
{
KeyBinding[] key = {
new KeyBinding("PlusX", Keyboard.KEY_UP),
new KeyBinding("MinusX", Keyboard.KEY_DOWN),
new KeyBinding("PlusZ", Keyboard.KEY_RIGHT),
new KeyBinding("MinusZ", Keyboard.KEY_LEFT),
new KeyBinding("OffsetY", Keyboard.KEY_Y),
new KeyBinding("InvertY", Keyboard.KEY_I),
new KeyBinding("Reset", Keyboard.KEY_U),
new KeyBinding("Rotate", Keyboard.KEY_O),
new KeyBinding("NextStruct", Keyboard.KEY_RBRACKET),
new KeyBinding("PrevStruct", Keyboard.KEY_LBRACKET)
};
boolean[] repeat = {false, false, false, false, false, false, false, false, false, false};
KeyBindingRegistry.registerKeyBinding(new KeyHandlerSGT(key, repeat));
// add all our custom keys to the map so the real Keyboard keyCode returns our custom value
for (int i = 0; i < NUM_KEYS_BOUND; ++i) {
SGTKeyMap.put(key[i].keyCode, i);
}
}
}
Since KeyBinding is an array [ ], you can initialize as many as you want within it
Then in your KeyHandler keyDown and keyUp methods you can use the Map we made earlier to have a single line do all the work no matter how many keys you bind:
!world.isRemote isn't the problem, even when I took it out it didn't help. I tried using the server sided world as such
World world = MinecraftServer.getServer().worldServers[0];
And I also tried using the client sided world as such
World world = Minecraft.getMinecraft().theWorld;
Neither of these worked for me. I'm not sure how to check for what is causing it to not open.
I think I might have the reason why, but I'm not sure how to fix it. It's trying to getBlockTileEntity from world, but I'm not sure how to get it from the player I guess. Not sure how this works when you just want it to open on a button instead of from an item or block.
@Override
public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) {
TileEntity entity = world.getBlockTileEntity(x, y, z);
switch(id) {
case 0:
if (entity != null && entity instanceof TileEntityDeployer) {
return new ContainerDeployer(player.inventory, (TileEntityDeployer) entity);
} else {
return null;
}
default:
return null;
}
}
@Override
public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) {
TileEntity entity = world.getBlockTileEntity(x, y, z);
switch(id) {
case 0:
if(entity != null && entity instanceof TileEntityDeployer) {
return new GuiDeployer(player.inventory, (TileEntityDeployer) entity);
} else {
return null;
}
default:
return null;
}
}
Does your Gui work if you open it on block activation? If so, then you are getting the incorrect coordinates for the tile entity when you use your button to open the gui instead of a block.
Found a problem with using my original method: the key map doesn't get updated when key bindings are changed using the in-game menu. It's better to use the menu than a config to handle user preferences, for obvious reasons, so here is how I do it now while still trying to keep the code simple:
@SideOnly(Side.CLIENT)
public class MyKeyHandler extends KeyHandler
{
/** Key index for easy handling and retrieval of keys and key descriptions */
public static final byte KEY_CUSTOM_1 = 0, KEY_CUSTOM_2 = 1, etc.;
/** Key descriptions - this is what the player sees when changing key bindings in-game */
public static final String[] desc = {"Description 1","Description 2", etc.};
/** Default key values */
private static final int[] keyValues = {Keyboard.KEY_RBRACKET, Keyboard.KEY_LBRACKET, etc.};
/** This stores all the keybindings, but they must first be initialized */
public static final KeyBinding[] keys = new KeyBinding[desc.length];
/** Initializes keybindings and registers a new KeyHandler instance */
public static final void init()
{
// this way avoids declaring every key binding as its own 'static final' field
boolean[] repeat = new boolean[desc.length];
for (int i = 0; i < desc.length; ++i) {
keys[i] = new KeyBinding(desc[i], keyValues[i]);
repeat[i] = false;
}
KeyBindingRegistry.registerKeyBinding(new MyKeyHandler(keys, repeat));
}
public MyKeyHandler(KeyBinding[] keys, boolean[] repeat) { super(keys, repeat); }
}
Then you can handle key presses like so in the keyDown method:
public void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat)
{
if (tickEnd)
{
Minecraft mc = Minecraft.getMinecraft();
// prevents key press firing while gui screen or chat open, if that's what you want
// if you want your key to be able to close the gui screen, handle it outside this if statement
if (mc.currentScreen == null)
{
if (kb.keyCode == keys[KEY_CUSTOM_1].keyCode) {
// do something like send a packet to server or change client side data
} else if (kb.keyCode == keys[KEY_CUSTOM_2].keyCode) {
// do something else
}
}
}
}
Just remember that keys are handled client side only, so if your Gui has a server-side component (such as a TileEntity inventory) or you need to change any real data, you'll need to send packets to the server.
Old information:
When using multiple keybindings, I find it cleanest to define their values at the top of the file and use a HashMap to store the true key code for my custom indices.
This method is also very compatible with configuration files, so users will be able to change your key settings to their liking.
The KeyBinding class (not to be confused with KeyHandler)
public class KeyBindSGT
{
// don't really need this anymore: use desc.length instead
public static final int NUM_KEYS_BOUND = 11;
/** Key index for easy handling */
public static final int PLUS_X = 0, MINUS_X = 1, PLUS_Z = 2, MINUS_Z = 3, OFFSET_Y = 4,
INVERT_Y = 5, RESET_OFFSET = 6, ROTATE_90 = 7, NEXT_STRUCT = 8, PREV_STRUCT = 9,
TOGGLE_REMOVE = 10;
/** Key descriptions */ // You could of course use this without a config as well
private static final String[] desc = {"PlusX", "MinusX", "PlusZ", "MinusZ", "OffsetY","InvertY",
"Reset", "Rotate", "NextStruct", "PrevStruct", "ToggleRemove"
};
/** Default key values */ // You could also use this without a config
private static final int[] keyValues = {Keyboard.KEY_UP, Keyboard.KEY_DOWN, Keyboard.KEY_RIGHT,
Keyboard.KEY_LEFT, Keyboard.KEY_Y, Keyboard.KEY_I, Keyboard.KEY_U, Keyboard.KEY_O,
Keyboard.KEY_RBRACKET, Keyboard.KEY_LBRACKET, Keyboard.KEY_V
};
/** Maps Keyboard values to SGT KeyBinding values */
public static final Map<Integer, Integer> SGTKeyMap = new HashMap<Integer, Integer>();
public static void init(Configuration config)
{
KeyBinding[] key = new KeyBinding[desc.length];
boolean[] repeat = new boolean[desc.length];
for (int i = 0; i < desc.length; ++i) {
key[i] = new KeyBinding(desc[i], config.get(KeyHandlerSGT.label, desc[i], keyValues[i]).getInt());
repeat[i] = false;
SGTKeyMap.put(key[i].keyCode, i);
}
KeyBindingRegistry.registerKeyBinding(new KeyHandlerSGT(key, repeat));
}
}
If using a config file, pass it in from your main mod during pre-Init:
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
LogHelper.init();
Configuration config = new Configuration(new File(event.getModConfigurationDirectory().getAbsolutePath() + "/StructureGenMod.cfg"));
config.load();
modItemIndex = config.getItem("modItemIndex", MOD_ITEM_INDEX_DEFAULT).getInt() - 256;
KeyBindSGT.init(config); // <--- this line here
config.save();
}
Now your KeyHandler class can also be very simple, due to the HashMap from above. For your keyDown method, you'll basically only need one line:
The same goes for keyUp, but setting keyPressed to false.
One idiosyncracy of this method is that it will send a constant stream of signals while the key is pressed. This may be desired, such as for movement, or not, such as for changing current spells (trust me, it's annoying when you tap the key and you change 2 or 3 spell slots at a time).
A solution to 'keyPressed' sending a constant signal stream is to handle all of your key bindings from within your extended KeyHandler class instead, as the 'keyDown' method is only called once when the key is first pressed. There, however, you'd need to use FMLClientHandler to retrieve the World and Player objects, as well as send packets to the server with any information that needs updating server-side.
Here's an example from my code that works great - only one signal per key press:
@Override
public void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat)
{
// NOTE that we no longer need 'keyPressed' variable or 'keyReleased'
if (tickEnd && KeyBindSGT.SGTKeyMap.containsKey(kb.keyCode) && FMLCommonHandler.instance().getEffectiveSide() == Side.CLIENT && FMLClientHandler.instance().getClient().inGameHasFocus) // so you don't trigger keys during chat, etc.
{
EntityClientPlayerMP player = FMLClientHandler.instance().getClient().thePlayer;
if (player.getHeldItem() != null && player.getHeldItem().getItem() instanceof ItemStructureSpawner)
{
ItemStructureSpawner spawner = (ItemStructureSpawner) player.getHeldItem().getItem();
switch(KeyBindSGT.SGTKeyMap.get(kb.keyCode)) {
case KeyBindSGT.PLUS_X:
player.addChatMessage("[STRUCTURE GEN] Incremented x offset: " + spawner.incrementOffset(ItemStructureSpawner.Offset.OFFSET_X));
break;
case KeyBindSGT.MINUS_X:
player.addChatMessage("[STRUCTURE GEN] Decremented x offset: " + spawner.decrementOffset(ItemStructureSpawner.Offset.OFFSET_X));
break;
case KeyBindSGT.PLUS_Z:
player.addChatMessage("[STRUCTURE GEN] Incremented z offset: " + spawner.incrementOffset(ItemStructureSpawner.Offset.OFFSET_Z));
break;
case KeyBindSGT.MINUS_Z:
player.addChatMessage("[STRUCTURE GEN] Decremented z offset: " + spawner.decrementOffset(ItemStructureSpawner.Offset.OFFSET_Z));
break;
case KeyBindSGT.OFFSET_Y:
if (spawner.isInverted())
player.addChatMessage("[STRUCTURE GEN] Decremented y offset: " + spawner.decrementOffset(ItemStructureSpawner.Offset.OFFSET_Y));
else
player.addChatMessage("[STRUCTURE GEN] Incremented y offset: " + spawner.incrementOffset(ItemStructureSpawner.Offset.OFFSET_Y));
break;
case KeyBindSGT.INVERT_Y:
player.addChatMessage("[STRUCTURE GEN] y offset will now " + (spawner.invertY() ? "decrement." : "increment."));
break;
case KeyBindSGT.RESET_OFFSET:
spawner.resetOffset();
player.addChatMessage("[STRUCTURE GEN] Offsets x/y/z reset to 0.");
break;
case KeyBindSGT.ROTATE:
player.addChatMessage("[STRUCTURE GEN] Structure orientation rotated by " + (spawner.rotate() * 90) + " degrees.");
break;
case KeyBindSGT.PREV_STRUCT:
player.addChatMessage("[STRUCTURE GEN] Selected structure " + spawner.getStructureName(spawner.prevStructure()) + " at index " + (spawner.getCurrentStructure() + 1));
break;
case KeyBindSGT.NEXT_STRUCT:
player.addChatMessage("[STRUCTURE GEN] Selected structure " + spawner.getStructureName(spawner.nextStructure()) + " at index " + (spawner.getCurrentStructure() + 1));
break;
case KeyBindSGT.TOGGLE_REMOVE:
player.addChatMessage("[STRUCTURE GEN] Structure will " + (spawner.toggleRemove() ? "be removed" : "generate") + " on right click.");
break;
}
}
}
}
One problem that may arise is that now your keys are only handled client side, vs. if you check in the onUpdate method of an item that has access to both client and server. So if your key press opens a Gui, for example, that involves an inventory from a TileEntity, then you need to send a packet to the server telling it to open the server side Gui element as well or you won't be able to interact with or even see the inventory.
Also, in the example above, the client is setting NBT data, which is a big no no. Instead, you should only send the key pressed data to the server and let the server handle all the NBT data.
This is what the KeyHandler should look like:
@Override
public void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat)
{
if (tickEnd && SGTKeyBindings.SGTKeyMap.containsKey(kb.keyCode))
{
EntityClientPlayerMP player = FMLClientHandler.instance().getClient().thePlayer;
if (player.getHeldItem().getItem() instanceof ItemStructureSpawner)
{
// a custom packet with the key code data
player.sendQueue.addToSendQueue(SGTPacketKeyPress.getPacket((byte) SGTKeyBindings.SGTKeyMap.get(kb.keyCode)));
}
}
}
The switch from earlier we simply move to the packet handler:
public class SGTPacketHandler implements IPacketHandler
{
/** Packet IDs */
public static final byte PACKET_KEY_PRESS = 1;
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player)
{
LogHelper.log(Level.INFO, "[SERVER] Received client packet.");
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
byte packetType;
try {
packetType = inputStream.readByte();
} catch (IOException e) {
e.printStackTrace();
return;
}
switch (packetType) {
case PACKET_KEY_PRESS: handlePacketKeyPress(packet, (EntityPlayer) player, inputStream); break;
default: LogHelper.log(Level.SEVERE, "Unhandled packet exception for packet id " + packetType);
}
}
private void handlePacketKeyPress(Packet250CustomPayload packet, EntityPlayer player, DataInputStream inputStream)
{
byte key;
try {
key = inputStream.readByte();
} catch (IOException e) {
e.printStackTrace();
return;
}
// This check shouldn't be necessary, as we checked already client side, but just in case...
if (!(player.getHeldItem().getItem() instanceof ItemStructureSpawner)) {
LogHelper.log(Level.SEVERE, "Held item is not an instance of ItemStructureSpawner - unable to process key press packet");
return;
}
ItemStructureSpawner spawner = (ItemStructureSpawner) player.getHeldItem().getItem();
switch (key) {
case SGTKeyBindings.PLUS_X: player.addChatMessage("[STRUCTURE GEN] Incremented x offset: " + spawner.incrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_X)); break;
case SGTKeyBindings.MINUS_X: player.addChatMessage("[STRUCTURE GEN] Decremented x offset: " + spawner.decrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_X)); break;
case SGTKeyBindings.PLUS_Z: player.addChatMessage("[STRUCTURE GEN] Incremented z offset: " + spawner.incrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_Z)); break;
case SGTKeyBindings.MINUS_Z: player.addChatMessage("[STRUCTURE GEN] Decremented z offset: " + spawner.decrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_Z)); break;
case SGTKeyBindings.OFFSET_Y:
if (spawner.isInverted(player.getHeldItem()))
player.addChatMessage("[STRUCTURE GEN] Decremented y offset: " + spawner.decrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_Y));
else
player.addChatMessage("[STRUCTURE GEN] Incremented y offset: " + spawner.incrementOffset(player.getHeldItem(), ItemStructureSpawner.Offset.OFFSET_Y));
break;
case SGTKeyBindings.INVERT_Y: player.addChatMessage("[STRUCTURE GEN] y offset will now " + (spawner.invertY(player.getHeldItem()) ? "decrement." : "increment.")); break;
case SGTKeyBindings.RESET_OFFSET:
spawner.resetOffset(player.getHeldItem());
player.addChatMessage("[STRUCTURE GEN] Offsets x/y/z reset to 0.");
break;
case SGTKeyBindings.ROTATE: player.addChatMessage("[STRUCTURE GEN] Structure orientation rotated by " + (spawner.rotate(player.getHeldItem()) * 90) + " degrees."); break;
case SGTKeyBindings.PREV_STRUCT: player.addChatMessage("[STRUCTURE GEN] Selected structure: " + spawner.getStructureName(spawner.prevStructure(player.getHeldItem())) + " at index " + (spawner.getCurrentStructureIndex(player.getHeldItem()) + 1)); break;
case SGTKeyBindings.NEXT_STRUCT: player.addChatMessage("[STRUCTURE GEN] Selected structure: " + spawner.getStructureName(spawner.nextStructure(player.getHeldItem())) + " at index " + (spawner.getCurrentStructureIndex(player.getHeldItem()) + 1)); break;
case SGTKeyBindings.TOGGLE_REMOVE: player.addChatMessage("[STRUCTURE GEN] Structure will " + (spawner.toggleRemove() ? "be removed" : "generate") + " on right click."); break;
}
}
}
The custom key packet I wrote:
public class SGTPacketKeyPress
{
public static Packet250CustomPayload getPacket(byte key)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(2);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
outputStream.writeByte(SGTPacketHandler.PACKET_KEY_PRESS);
outputStream.writeByte(key);
} catch (Exception ex) {
ex.printStackTrace();
}
return new Packet250CustomPayload(ModInfo.CHANNEL, bos.toByteArray());
}
}
You can do exactly the same for opening a gui, but send the gui id to open. In this case, you don't even have to open the gui client-side, as it will happen automatically when you call player.openGui from the server.
Does your Gui work if you open it on block activation? If so, then you are getting the incorrect coordinates for the tile entity when you use your button to open the gui instead of a block.
The problem is this code:
TileEntity entity = world.getBlockTileEntity(x, y, z);
is trying to get a block tile entity at the coordinates that the player is (so it comes back as null as there isn't supposed to be a block), but I don't want it to get a block entity I just want to press a button and it open the gui without an item or block. Just like when you press e and it opens the inventory, without the use of a block or an item. So I'm not sure what I have to do to get it to work like that. I have been trying to look for the java code for vanilla inventories and see what they do, but I haven't found them yet.
I am sorry.
It was because I have put in a package that were specified in the TransformerExclusions annotation.
Was working when moved to a different package.
Ah, ok.
From the above, looks like you may have imported KeyBinding twice?
@SoBiohazardous:
Awesome TUT - worked great! I have a few questions, though:
- Checking for keyPressed onUpdate will send a constant stream of signals even if we set 'repeat' to false, as it just checks each tick if keyPressed is true. Do you happen to know how to process a keystroke only once for each downpress, even if held?
-
Is there a way to check if Chat is open, because my keybindings still do their functions no matter what menu or chat is open.Never mind, here's how to do it (just the first check in the 'if' statement):
For your first question, yes i'm aware of that. I am still tinkering with logic trying to figure that out. You can try if you like. Try and use a system of booleans and numbers. When I figure it out, i'll let everyone know.
I don't want it to run the methods while the chat or any other GUI is open lol
Any suggestions?
Follow me on twitter! @dreinsteinium
I'll let you know if I find out as well. I tried this in my GUI:
KEY_G is the key I bound. Strangely, ESC (1) and Inventory Key (E) both work normally, but KEY_G will freak out opening and closing the GUI. There must be something in the vanilla code... I will find it!
I have no idea why that would be. Post your code maybe?
Ok, cool...I don't know why it would freak out.
you should put a note about importing the wrong Key Binding
Just a heads up
With the standard libraries included when you start modding you may even import the wrong Block.java class
There are many of the standards imports in minecraft which has the same name as something in another lib,
one must always be careful about which import one chooses.
Thanks man, I'll put it in the main thread with credit
I had a crash where iff on a server the server would crash because of the PlayerTickHandler. I found out that it had to do with a line of code,
The error was that a Keyboard was not registered, and therefore it could not use that key. This works on single player. So I looked up my error and found this tutorial. If I put your KeyRegistry part in the main mod class or the client proxy (and use the code below), it works for single player but crashes the server before it starts.
How do you make this work on a server? Keybinding is not supposed to be called by the server, so it doesn't know what to do... I know I have to use packets somehow, but I'm at a loss.
Any ideas? Help would be greatly appreciated.
Thanks,
LittleBreadLoaf
Bleach Mod
Well if you only have one it would be 0, unless you are using multiple then you should handle them in your GuiHandler. This isn't part of my tutorial thought, its for the gui tutorial.
Try removing 'if (!world.isRemote)' - that tells it to only open the Gui on the server, but Gui is client side (and sometimes also server side, for instance, if accessing a tile entity's inventory).
Since KeyBinding is an array [ ], you can initialize as many as you want within it
Then in your KeyHandler keyDown and keyUp methods you can use the Map we made earlier to have a single line do all the work no matter how many keys you bind:
Same goes for keyUp, but set to false, obviously.
And I also tried using the client sided world as such
Neither of these worked for me. I'm not sure how to check for what is causing it to not open.
I think I might have the reason why, but I'm not sure how to fix it. It's trying to getBlockTileEntity from world, but I'm not sure how to get it from the player I guess. Not sure how this works when you just want it to open on a button instead of from an item or block.
Then you can handle key presses like so in the keyDown method:
Just remember that keys are handled client side only, so if your Gui has a server-side component (such as a TileEntity inventory) or you need to change any real data, you'll need to send packets to the server.
Old information:
When using multiple keybindings, I find it cleanest to define their values at the top of the file and use a HashMap to store the true key code for my custom indices.
This method is also very compatible with configuration files, so users will be able to change your key settings to their liking.
The KeyBinding class (not to be confused with KeyHandler)
Now your KeyHandler class can also be very simple, due to the HashMap from above. For your keyDown method, you'll basically only need one line:
The same goes for keyUp, but setting keyPressed to false.
One idiosyncracy of this method is that it will send a constant stream of signals while the key is pressed. This may be desired, such as for movement, or not, such as for changing current spells (trust me, it's annoying when you tap the key and you change 2 or 3 spell slots at a time).
A solution to 'keyPressed' sending a constant signal stream is to handle all of your key bindings from within your extended KeyHandler class instead, as the 'keyDown' method is only called once when the key is first pressed. There, however, you'd need to use FMLClientHandler to retrieve the World and Player objects, as well as send packets to the server with any information that needs updating server-side.
Here's an example from my code that works great - only one signal per key press:
One problem that may arise is that now your keys are only handled client side, vs. if you check in the onUpdate method of an item that has access to both client and server. So if your key press opens a Gui, for example, that involves an inventory from a TileEntity, then you need to send a packet to the server telling it to open the server side Gui element as well or you won't be able to interact with or even see the inventory.
Also, in the example above, the client is setting NBT data, which is a big no no. Instead, you should only send the key pressed data to the server and let the server handle all the NBT data.
This is what the KeyHandler should look like:
The switch from earlier we simply move to the packet handler:
The custom key packet I wrote:
The problem is this code:
TileEntity entity = world.getBlockTileEntity(x, y, z);
is trying to get a block tile entity at the coordinates that the player is (so it comes back as null as there isn't supposed to be a block), but I don't want it to get a block entity I just want to press a button and it open the gui without an item or block. Just like when you press e and it opens the inventory, without the use of a block or an item. So I'm not sure what I have to do to get it to work like that. I have been trying to look for the java code for vanilla inventories and see what they do, but I haven't found them yet.