Jump to content

[1.7.10] Keyboards and keybinds.


Robo51

Recommended Posts

I've been working through and learning how to use events and I've run into a problem.

 

This is the code in question:

 

 

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

 

if(Keyboard.isKeyDown(43)) {

event.entity.motionY = 4.0D;

}

}

 

 

 

This works fine in single player but throws and error in multiplayer:

 

 

[23:06:17] [server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.living.LivingEvent$LivingUpdateEvent@2216b29c:

java.lang.IllegalStateException: Keyboard must be created before you can query key state

at org.lwjgl.input.Keyboard.isKeyDown(Keyboard.java:406) ~[lwjgl-2.9.1.jar:?]

at robo51.newt.handlers.EventsHandler.onLivingUpdateEvent(EventsHandler.java:48) ~[EventsHandler.class:?]

at cpw.mods.fml.common.eventhandler.ASMEventHandler_6_EventsHandler_onLivingUpdateEvent_LivingUpdateEvent.invoke(.dynamic) ~[?:?]

at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]

at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:138) [EventBus.class:?]

at net.minecraftforge.common.ForgeHooks.onLivingUpdate(ForgeHooks.java:282) [ForgeHooks.class:?]

at net.minecraft.entity.EntityLivingBase.onUpdate(EntityLivingBase.java:1763) [EntityLivingBase.class:?]

at net.minecraft.entity.player.EntityPlayer.onUpdate(EntityPlayer.java:327) [EntityPlayer.class:?]

at net.minecraft.entity.player.EntityPlayerMP.onUpdateEntity(EntityPlayerMP.java:330) [EntityPlayerMP.class:?]

at net.minecraft.network.NetHandlerPlayServer.processPlayer(NetHandlerPlayServer.java:329) [NetHandlerPlayServer.class:?]

at net.minecraft.network.play.client.C03PacketPlayer.processPacket(C03PacketPlayer.java:37) [C03PacketPlayer.class:?]

at net.minecraft.network.play.client.C03PacketPlayer$C06PacketPlayerPosLook.processPacket(C03PacketPlayer.java:271) [C03PacketPlayer$C06PacketPlayerPosLook.class:?]

at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:241) [NetworkManager.class:?]

at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) [NetworkSystem.class:?]

at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) [MinecraftServer.class:?]

at net.minecraft.server.dedicated.DedicatedServer.updateTimeLightAndEntities(DedicatedServer.java:349) [DedicatedServer.class:?]

at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) [MinecraftServer.class:?]

at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]

at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]

 

 

 

Now after doing a little looking around I've figured out that I could  register a key binder to help fix this.

But I'm wondering it there is an easier way since I'm just trying to check for the space-bar (or jump key) press.

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Link to comment
Share on other sites

So after a few busy days I had a chance to sit down and try and work this out, and I feel like I have hit a wall with my face. :/

 

I ended up using Ablaze's How to use SimpleNetworkWrapper, IMessage and IMessageHandler tutorial.

 

I have these two classes from that:

 

NewtMessageHandler

 

 

package robo51.newt.handlers;

 

import cpw.mods.fml.common.network.simpleimpl.IMessage;

import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;

import cpw.mods.fml.common.network.simpleimpl.MessageContext;

 

public class NewtMessageHandler implements IMessageHandler<NewtMessage, IMessage> {

 

@Override

public IMessage onMessage(NewtMessage message, MessageContext ctx) {

// TODO Auto-generated method stub

return null;

}

 

}

 

 

 

 

NewtMessage

 

 

package robo51.newt.handlers;

 

import io.netty.buffer.ByteBuf;

import cpw.mods.fml.common.network.simpleimpl.IMessage;

 

public class NewtMessage implements IMessage {

 

@Override

public void fromBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

@Override

public void toBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

}

 

 

 

 

But I'm confused as to what I am supposed to put in these.

I think I need to send a packet from my event handler using these to the server?

 

But what do I need to send? Do I send the Keyboard.isKeyDown?

And then does my event handler get somethings back to execute the code or is that supposed to happen somewhere else?

 

I feel like these are easy questions to answer, but I'm not capable of answering them. :/

 

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Link to comment
Share on other sites

Your IMessage class needs to have whatever data you need to pass, and a constructor that allows you to pass them.

 

Keys are tricky, because all of the keyboard information is client-side only, so sending the keyCode of the key pressed is pointless. Instead, I recommend you make separate packets for each action that you want to perform, or at least create some way to distinguish actions.

 

A simple example:

public class NewtMessage implements IMessage {
private byte action;

public NewtMessage(KeyBinding kb) {
if (kb == YourKeys.yourKeyBindings.specialKey) {
action = (byte) 1;
} else { // whatever other actions
action = (byte) 2;
}
}

// write the 'action' byte to the buffer
// read it back

// in your IMessageHandler class' method:
switch(message.action) {
case 1: // do your special action # 1
case 2: // do special action # 2
default: // throw an exception
}
}

 

Obviously that is not optimal code, but it should illustrate what you need. If you want to see some concrete examples, feel free to browse my code.

 

EDIT: Keep in mind that my current code base is for Forge 1180; if you are using 1217+, you will need to also check Keyboard.getEventKeyState() to tell you if the key was pressed (true) or released (false), since KeyInputEvent is now fired for both presses and releases (yay!!!).

Link to comment
Share on other sites

So I been working on this again a bit and worked through a few things.

 

Now I'm having trouble figuring out how to properly implement the packet sending into what I have already.

 

This is my eventshandler:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.entity.EntityLivingBase;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.DamageSource;

import net.minecraft.world.World;

import net.minecraftforge.event.entity.EntityEvent.EntityConstructing;

import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.entity.ExtendedPlayerWater;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.relauncher.Side;

import cpw.mods.fml.relauncher.SideOnly;

 

 

public class EventsHandler {

 

@SubscribeEvent

public void onLivingUpdateEvent(LivingUpdateEvent event, World world) {

EntityLivingBase entity;

 

if (event.entity instanceof EntityPlayer) {

EntityPlayer player = (EntityPlayer) event.entity;

if(event.entity.isWet()) {

int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();

if(watersealing <= 0) {

event.entity.attackEntityFrom(DamageSource.drown, 1.0F);

} else {

--watersealing;

ExtendedPlayerWater.newCurrentWaterSealing(watersealing);

if(watersealing == 200) {

if(!world.isRemote)

player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));

}

if(watersealing == 50) {

if(!world.isRemote)

player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));

}

}

}

/*

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

 

if(Keyboard.isKeyDown(43)) {

event.entity.motionY = 4.0D;

}

} */

}

}

 

@SubscribeEvent

public void onEntityConstructing(EntityConstructing event) {

if (event.entity instanceof EntityPlayer && ExtendedPlayerWater.get((EntityPlayer) event.entity) == null) {

ExtendedPlayerWater.register((EntityPlayer) event.entity);

}

}

}

 

 

 

This is the message that i'm trying to use:

 

 

package robo51.newt.network.packet.server;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.Newt;

import net.minecraft.entity.player.EntityPlayer;

import io.netty.buffer.ByteBuf;

import cpw.mods.fml.common.network.simpleimpl.IMessage;

import cpw.mods.fml.common.network.simpleimpl.MessageContext;

 

/**

*

* A simple message telling the server that the client wants to open a GUI.

*

*/

public class RiseUpMessage implements IMessage {

 

// The basic, no-argument constructor MUST be included to use the new automated handling

public RiseUpMessage() {}

 

public static class Handler extends AbstractServerMessageHandler<RiseUpMessage> {

@Override

public IMessage handleServerMessage(EntityPlayer player, RiseUpMessage message, MessageContext ctx) {

if(player.isInWater()) {

player.setAir(10);

player.motionY = 0.005D;

if(player.isCollidedHorizontally) {

player.motionY = 0.2D;

} else {

player.motionY = -1.0D;

}

}

return null;

}

}

 

@Override

public void fromBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

@Override

public void toBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

}

 

 

 

I also added a KeyHandler for later, not sure if I need something else in this:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.client.settings.KeyBinding;

import net.minecraft.util.StatCollector;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.network.PacketDispatcher;

import robo51.newt.network.packet.server.RiseUpMessage;

import cpw.mods.fml.client.registry.ClientRegistry;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.common.gameevent.InputEvent.KeyInputEvent;

 

 

public class KeyHandler {

/** Storing an instance of Minecraft in a local variable saves having to get it every time */

private final Minecraft mc;

 

/** Key index for easy handling */

public static final int CUSTOM_INV = 0;

 

/** Key descriptions; use a language file to localize the description later */

private static final String[] desc = {"key.tut_inventory.desc"};

 

/** Default key values */

private static final int[] keyValues = {Keyboard.KEY_SPACE};

 

/** Make this public or provide a getter if you'll need access to the key bindings from elsewhere */

public static final KeyBinding[] keys = new KeyBinding[desc.length];

 

public KeyHandler() {

mc = Minecraft.getMinecraft();

for (int i = 0; i < desc.length; ++i) {

keys = new KeyBinding(desc, keyValues, StatCollector.translateToLocal("key.tutorial.label"));

ClientRegistry.registerKeyBinding(keys);

}

}

 

/**

* KeyInputEvent is in the FML package, so we must register to the FML event bus

*/

@SubscribeEvent

public void onKeyInput(KeyInputEvent event) {

// checking inGameHasFocus prevents your keys from firing when the player is typing a chat message

// NOTE that the KeyInputEvent will NOT be posted when a gui screen such as the inventory is open

// so we cannot close an inventory screen from here; that should be done in the GUI itself

if (mc.inGameHasFocus) {

if (keys[CUSTOM_INV].getIsKeyPressed()) {

PacketDispatcher.sendToServer(new RiseUpMessage());

}

}

}

}

 

 

 

I've also been having trouble with the chat components in the EventsHandler, I believe it has to to with the

world parameter that I added to prevent double chat messages.

 

 

How would I properly implement the packet?

I'm just not sure what the server and the client need to handle.

 

And some help with the world/chat problem would be much appreciated. :)

 

Also, Happy Halloween!

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Link to comment
Share on other sites

You CAN NOT add parameters to event handling methods - they take one argument and one argument only: the event class for which they will fire.

 

@SubscribeEvent

public void whateverMethodName(Event event) {}

 

That is the signature your method MUST have. Always use the most specific Event subclass that you can, e.g. LivingUpdateEvent instead of LivingEvent.

 

If you need something that is not part of the event (such as a 'World'), then you must find another way to get it or use a different event. For world objects, it is very easy - every entity has a world object: event.entity.worldObj.

Link to comment
Share on other sites

You CAN NOT add parameters to event handling methods - they take one argument and one argument only: the event class for which they will fire.

 

@SubscribeEvent

public void whateverMethodName(Event event) {}

 

That is the signature your method MUST have. Always use the most specific Event subclass that you can, e.g. LivingUpdateEvent instead of LivingEvent.

 

If you need something that is not part of the event (such as a 'World'), then you must find another way to get it or use a different event. For world objects, it is very easy - every entity has a world object: event.entity.worldObj.

 

Thank you very much for the world help, It's much appreciated :)

 

Would you perhaps be able to help me with the packet?

 

I'm having trouble understanding where I would need to break off in this code:

 

 

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

}

 

 

 

I know the server doesn't handle key-binds, does that mean the packet should contain the isKeyDown or would it be everything after?

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Link to comment
Share on other sites

Your approach to this is kinda bad and not very well planned.

Inside packets (in this case) you shouldn't check anything, no IFs, nothing like that. Packets are used for one purpose - synchronize client with server. By that I mean - you just send one tiny bit of info that will be later used by any side said above and maybe "answered".

 

Let's rethink this:

1. Everything should happen server-side.

2. You have your ExtendedPlayerWater, from what I read in code - this is your ExtendedPlayer class that is holding some value ("watersealing").

3. You want it, so the player has 200 ticks of "extra" "something", BUT ONLY IF he's holding a key down, ay? (detail - 20 ticks = 1 sec, you wrote "200" and then "5 Seconds left").

 

How would I approach this:

1. In your ExtendedPlayerWater create one BOOLEAN value "isUsing".

2. Look at KeyBinding.class - you can call for action on both press and "unpress".

3. Create KeyBinding that will call for packet "ChangeUseState" on either press or unpress (doesn't matter, if it's 0 then it will change to 1, otherwise from 1 to 0).

4. Now create a "ChangeUseState" packet that will send send notification from client to server when the binding from 3. is pressed. In this case you don't even need input, only thing required is ofc. sender (player).

5. On server-receiver you get sender of this packed get his "isUsing" and if(isUsing==0) isUsing = 1 else(otherwise).

6. WHOLE rest of this mess will be handled by server itself, so:

In your "LivingUpdateEvent event":

- I would use PlayerTickEvent instead.

- You make all the checks like you did before but instead of checking the binding, just look into your ExtendedPlayerWater.isUsing.

 

Regarding pressing and "unpressing":

You can send packed on press, on stop pressing, on re-press (double-press), anything you want rly.

 

Regarding packets:

His tutorial is probably best you can find. Until you understand how it works I am not planning on providing done code. Give me something to work with.

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

You can get away with running that code you posted on the client side only.

 

Is this necessarily true? Bear with me, I'm a noob, but I thought the server checks for "proper" movement of the player as an anti-cheat measure. If a mod is moving the player entity in ways that the server doesn't know about, couldn't that potentially cause the player to be kicked if the mod moves the player entity too fast or in a way that the server considers "flight"?

Link to comment
Share on other sites

Is this necessarily true? Bear with me, I'm a noob, but I thought the server checks for "proper" movement of the player as an anti-cheat measure. If a mod is moving the player entity in ways that the server doesn't know about, couldn't that potentially cause the player to be kicked if the mod moves the player entity too fast or in a way that the server considers "flight"?

That is possible, but not likely if you use fairly normal motion values. The code for 'moving wrongly' on the server simply checks if the difference between the player's current and last position is greater than a certain value, so as long as you are within that, you are fine. If you need more than that, then yes, you will probably need to send a packet and handle the position change on the server, but I've never had to do that.

Link to comment
Share on other sites

Maybe I'll explain what I'm tying to accomplish better.

 

I'm changing how the player reacts with water, in my EventsHandler:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.entity.EntityLivingBase;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.DamageSource;

import net.minecraft.world.World;

import net.minecraftforge.event.entity.EntityEvent.EntityConstructing;

import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.entity.ExtendedPlayerWater;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.common.gameevent.TickEvent.PlayerTickEvent;

import cpw.mods.fml.relauncher.Side;

import cpw.mods.fml.relauncher.SideOnly;

 

 

public class EventsHandler {

 

@SubscribeEvent

public void playerTickEvent(PlayerTickEvent event) {

EntityLivingBase entity;

 

if (event.player instanceof EntityPlayer) {

EntityPlayer player = (EntityPlayer) event.player;

if(event.player.isWet()) {

int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();

if(watersealing <= 0) {

event.player.attackEntityFrom(DamageSource.drown, 1.0F);

} else {

--watersealing;

ExtendedPlayerWater.newCurrentWaterSealing(watersealing);

if(watersealing == 200) {

if(!event.player.worldObj.isRemote) {

player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));

}

}

if(watersealing == 50) {

if(!event.player.worldObj.isRemote) {

player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));

}

}

}

}

/*

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

} */

}

}

 

@SubscribeEvent

public void onEntityConstructing(EntityConstructing event) {

if (event.entity instanceof EntityPlayer && ExtendedPlayerWater.get((EntityPlayer) event.entity) == null) {

ExtendedPlayerWater.register((EntityPlayer) event.entity);

}

}

}

 

 

This block of code:

		if(event.player.isWet()) {
			int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();
			if(watersealing <= 0) {
				event.player.attackEntityFrom(DamageSource.drown, 1.0F);
			} else {
				--watersealing;
				ExtendedPlayerWater.newCurrentWaterSealing(watersealing);
				if(watersealing == 200) {
					if(!event.player.worldObj.isRemote) {
						player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));
					}
				}
				if(watersealing == 50) {
					if(!event.player.worldObj.isRemote) {
						player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));
					}
				}
			}
		}

I'm using to check if the player is in contact with water. (I have an item that adds water sealing to the player via an item.)

If they have no water sealing they take damage, otherwise watersealing ticks down, when the sealing hits a certain level a chat message is sent to the player to warn them of imminent failure.

 

In this block:

		if(event.entity.isInWater()) {
			event.entity.setAir(10);
			if(Keyboard.isKeyDown(57)) {
				event.entity.motionY = 0.005D;
				if(event.entity.isCollidedHorizontally) {
					event.entity.motionY = 0.2D;
				}
			} else {
				event.entity.motionY = -1.0D;
			}
		} 

I'm modifying the players movement in water making them sink straight  to the bottom.

They can swim up but it's slow unless pressed up against a block for leverage.

 

And that's where the problem came from.

The server not liking the isKeyDown part.

 

 

I apologize for not knowing very much about packets and the like.

I'm new to java coding and I'm trying to learn through modding.

 

Also if it helps I have a github for this, it can be found here: https://github.com/Robo51/Newt

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.