Robo51 Posted October 23, 2014 Posted October 23, 2014 I've been working through and learning how to use events and I've run into a problem. This is the code in question: Reveal hidden contents 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: Reveal hidden contents [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. Quote 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?
Eternaldoom Posted October 23, 2014 Posted October 23, 2014 There's some way to check for the vanilla keys. I forget how. Key bindings don't exist on the server though. You'll probably need to send a packet. Quote Check out my mod, Realms of Chaos, here. If I helped you, be sure to press the "Thank You" button!
Robo51 Posted October 23, 2014 Author Posted October 23, 2014 I see, I have no idea what that would entail. Quote 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?
Eternaldoom Posted October 23, 2014 Posted October 23, 2014 Diesieben07 has a SimpleNetworkWrapper tutorial in the tutorials section. Quote Check out my mod, Realms of Chaos, here. If I helped you, be sure to press the "Thank You" button!
brandon3055 Posted October 23, 2014 Posted October 23, 2014 Pahimar has a great tutorial on keybindings he hasnt discussed packet handling yet but Diesieben07's tutorial should be able to help you with that. Quote I am the author of Draconic Evolution
Robo51 Posted October 25, 2014 Author Posted October 25, 2014 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 Reveal hidden contents 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 Reveal hidden contents 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. Quote 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?
coolAlias Posted October 25, 2014 Posted October 25, 2014 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!!!). Quote http://i.imgur.com/NdrFdld.png[/img]
Robo51 Posted November 1, 2014 Author Posted November 1, 2014 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: Reveal hidden contents 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: Reveal hidden contents 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: Reveal hidden contents 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! Quote 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?
coolAlias Posted November 2, 2014 Posted November 2, 2014 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. Quote http://i.imgur.com/NdrFdld.png[/img]
Robo51 Posted November 3, 2014 Author Posted November 3, 2014 On 11/2/2014 at 6:14 PM, coolAlias said: 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: Reveal hidden contents 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? Quote 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?
Ernio Posted November 4, 2014 Posted November 4, 2014 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. Quote Quote 1.7.10 is no longer supported by forge, you are on your own.
coolAlias Posted November 4, 2014 Posted November 4, 2014 If you are only playing with the player's motion, you don't technically even need to tell the server about it, as the player's position is synced automatically from client to server each tick. You can get away with running that code you posted on the client side only. Quote http://i.imgur.com/NdrFdld.png[/img]
MrCaracal Posted November 4, 2014 Posted November 4, 2014 On 11/4/2014 at 3:02 AM, coolAlias said: 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"? Quote
coolAlias Posted November 4, 2014 Posted November 4, 2014 On 11/4/2014 at 3:23 AM, MrCaracal said: 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. Quote http://i.imgur.com/NdrFdld.png[/img]
Robo51 Posted November 9, 2014 Author Posted November 9, 2014 Maybe I'll explain what I'm tying to accomplish better. I'm changing how the player reacts with water, in my EventsHandler: Reveal hidden contents 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 Quote 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?
Robo51 Posted November 15, 2014 Author Posted November 15, 2014 Would anyone be able to help with this? Quote 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?
coolAlias Posted November 15, 2014 Posted November 15, 2014 All you have to do is move that logic to the client side - handle the modification of the player's motion there, and the rest of your logic (taking damage, etc.) on the server. Quote http://i.imgur.com/NdrFdld.png[/img]
Recommended Posts
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.