So should my method be Entity or EntityItem, as EntityItem throws errors?
Really not sure where I am going with this, kind of horrific code.
Just basic java knowledge failure there, how do I add to the private int in ExtendedPlayer, or can I just make it public?
Make a method to add xp in your ExtendedPlayer class:
public void addXp(int amount) { this.combatxp += amount; }
Then in the event:
@ForgeSubscribe
public void EntityItemPickupEvent(EntityPlayer player, EntityItem item)
{
if (item instanceof EntityXPOrb)
{
// you need to cast 'item' as EntityXPOrb to access the EntityXPOrb class methods
ExtendedPlayer.get(player).addXp(((EntityXPOrb) item).getXpValue());
}
}
java.lang.IllegalArgumentException: Method public void net.minecraft.src.ModEventHandler.EntityItemPickupEvent(net.minecraft.entity.player.EntityPlayer,net.minecraft.entity.Entity) has @ForgeSubscribe annotation, but requires 2 arguments. Event handler methods must require a single argument.
Derp sorry, totally escaped me that your method signature is incorrect. For Forge Events, the ONLY method parameter you can have is the Event class that you are watching.
@ForgeSubscribe
public void onItemPickup(EntityItemPickupEvent event) {
// rest of code goes here, using event.item instead of 'item'
}
Also, you are using the incorrect field in EntityItemPickupEvent.
'event.entity' is NOT an EntityItem, it is the entity picking up the item.
'event.item' is the EntityItem, as I mentioned earlier.
Btw, you've got a 'public boolean checkcombatxp(int amount)' method in your ExtendedPlayer that looks like it's doing some stuff you may not want it to (subtracting xp amounts and checking 'sufficient'). That's why I always encourage people to type out by hand all the code they are using rather than using copy / paste - it helps to prevent this kind of situation
Also, you are using the incorrect field in EntityItemPickupEvent.
'event.entity' is NOT an EntityItem, it is the entity picking up the item.
That brings me to another point: other entities besides EntityPlayer can pick up items, but they will not have your ExtendedPlayer properties, so if they happen to pick up an Xp Orb somehow, Minecraft will crash. Just check if (event.entity instanceof EntityPlayer) before using your extended properties.
Incompatible conditional operand types EntityItem and EntityXPOrb
The Error when I use item not entity :/
The Boolean I am not actually using anywhere yet, so just kept it in without editing for now.
Oh, dang it yeah... EntityXPOrb extends Entity, not EntityItem... which means that you're S.O.L. for using EntityItemPickupEvent Unfortunately, there isn't an easy hook or event for this, so you'll have to do some magic. One way might be to store the player's current xp amount in extended properties as a separate value, and every so often compare the player's current xp to the stored value and add the difference (if positive) to your combatxp field. You could use the LivingUpdateEvent and check every so often by using modular division on the player's ticks existed or something like that.
That would cause all kinds of problems with dieing and using it enchanting though :/...
What kinds of problems would that cause? You wouldn't modify the player's Xp, you'd just store a temporary copy of it for comparison. The logic of it is like this:
1. Game starts, store player's current xp in props.lastXp
2. Some number of ticks later, check if players current xp is greater than props.lastXp
3. If so, add player.currentXp - props.lastXp to props.combatXp and set props.lastXp to player.currentXp
4. rinse and repeat
How does that have any effect on enchanting or death?
How often could you check without effecting performance?
If you checked every minute say, any xp gained after dieing and between the next check would not be added.
You could check every single tick if you wanted to without noticeably affecting performance, though it would be better to check less frequently. How big of a deal if you miss a few Xp due to dying in that narrow window between checks? The answer to that determines how frequently you should check.
I was reminded of certain oddities in Minecraft code today while trying to disarm a zombie when the player is attacked, using the LivingAttackEvent. When I was attacked by a flesh-wielding zombie, I could see the disarming was successful because the EntityItem would go flying off, but the zombie appeared to still be holding flesh?!
It turns out that the LivingAttackEvent is only called on the server side when mobs or any non-player entity is the source of the attack, which makes sense, but this means that the client side version of the zombie never gets his held item set to null, which means it still looks like he's holding rotten flesh when he shouldn't be.
This brings me to my point: often times Forge events ONLY get called on one side or the other, and this may or may not have various ramifications for your code. If something is not working as expected, check which side(s) the event is being called on and you may just find some useful information.
An easy way to check which side(s) an event is being called on is to put debugging code at the beginning of the event method; so long as that event has access to some kind of entity it is easy to distinguish side, and most events do have an entity:
@ForgeSubscribe
public void someEventMethod(SomeEvent event) {
System.out.println("Some event called; is this the client side? " + event.entity.worldObj.isRemote);
}
Just thought I'd share my random experience today. Good luck coding!
Got another error, this time in the packet handling portion of the gui tutorial, at these lines it's saying .isServer cannot be resolved or is not a field, but I can see it clearly in the Side.java class, really confusing me.
Any Idea what is going wrong here, I get no errors but when I run the game, minecraft stops responding (does not crash as such so no error messages)
@ForgeSubscribe
public void onLivingUpdateEvent(LivingUpdateEvent event){
if (event.entity instanceof EntityPlayer)
{
EntityPlayer player = (EntityPlayer) event.entity;
int x = 1;
while ( x < 41);
{
if (player.inventory.getStackInSlot(x) == new ItemStack(Block.grass)){
player.inventory.setInventorySlotContents(x, new ItemStack(Block.dirt));
}
x++;
}
I am trying to check every inventory slot and replace all grass with dirt.
You are getting stuck in an infinite loop. Don't use 'while' to check inventory, use 'for' loops instead, either using getStackInSlot(i) or calling the inventory directly:
for (ItemStack stack : player.inventory.mainInventory) {
if (stack != null && stack.itemID == Block.grass.blockID) {
stack.itemID = Block.dirt.blockID;
}
}
I tried it turning dirt into glass and it was quite hilarious digging around
EDIT: The reason you are stuck in an infinite loop is at the end of the parenthesis surrounding your while statement, there is a semi-colon, so it says "while 'x' is less than 41, STOP" which means x never gets incremented. Remove the semi-colon and your logic should work, though you're basically writing a 'for' loop using a 'while' statement.
One more question, any idea how I could add a prefix or suffix to a players name using events?
For example checking if they have over 10 exp, then applying the prefix [2] before their name.
Sure. One way to do it is to change the display name itself, the other is to intercept the rendering of the display name and add stuff to it. I believe the latter is the way some mods add things like hp bars below names.
To intercept rendering, you need to look at the event types in 'minecraftforge/client/event'; you'll see one called 'RenderLivingEvent', open that up and you'll see there are several different types. Then open up 'RenderLiving' and search for 'RenderLivingEvent', you will find all the places that that event is posted. Luckily, there is one type of the event in particular that gets posted at the beginning of the passSpecialRender method, and that is where the name tag is rendered.
When that event gets posted, you can cancel it so that the standard name doesn't get displayed, then copy / paste the passSpecialRender code into your own method and change what you need. Just be sure to keep everything else intact, as it's needed to render other things properly
So, one last question for you, and in hindsight I should probably put this in the keybinding tutorial page but oh well, I'm already writing it, so in your comment there you showed a better way to do multiple keybindings which helped a lot, but if I'm correct that has the keymap on the keybinding class, which the server can't access, so when you send a packet, how does the server know what key was pressed?
I'm also confused on this line in the example packet you listed(specifically ModInfo.CHANNEL), is it specific to your mod or am I missing something?:
return new Packet250CustomPayload(ModInfo.CHANNEL, bos.toByteArray());
So, one last question for you, and in hindsight I should probably put this in the keybinding tutorial page but oh well, I'm already writing it, so in your comment there you showed a better way to do multiple keybindings which helped a lot, but if I'm correct that has the keymap on the keybinding class, which the server can't access, so when you send a packet, how does the server know what key was pressed?
I actually don't recommend storing the keybindings in a HashMap, as the map does not get updated if the player changes the key bindings using the in-game menu. Likewise, it's not necessary to use a Config file for keys, since the default values are set in the KeyBinding class and the keys are already configurable via the in-game options menu.
I thought I had updated that post with this information - I'll check again.
However, you can still create KeyBinding array and store your key bindings in there, which will give you access to them from anywhere:
/** Key descriptions - this is what the player sees when changing key bindings in-game */
public static final String[] desc = {"Does this","Does that", etc. };
/** Default key values; must be in the same order as the descriptions */
private static final int[] keyValues = {Keyboard.KEY_A, Keyboard.KEY_B, etc. };
public static final KeyBinding[] keys = new KeyBinding[desc.length];
/**
* Initializes keybindings and registers a new KeyHandler instance
*/
public static final void init() {
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 QuestKeyHandler(keys, repeat));
}
Sorry, I guess I wasn't that clear, what I mean was in this part in the packethandler:
}
switch (key) {
case Keyboard.KEY_RBRACKET: System.out.println("This worked"); break;
}
what I have there doesn't seem to be working.
Right. You will need to handle the key presses from within your KeyHandler's keyDown and/or keyUp methods, rather than sending a blanket 'key pressed Packet', although you could still send a packet like that using the byte codes for your key index, rather than the key code itself:
/** Key index for easy handling and retrieval of keys and key descriptions */
public static final byte KEY_ONE = 0, KEY_TWO = 1, etc; // please use more descriptive names
// when making your packet, you'll have to use lots of 'if / else-if' statements since we no longer have a handy HashMap
// this is in keyDown method:
byte key;
if (kb.keyCode == keys[KEY_ONE].keyCode) { key = KEY_ONE; }
else if (kb.keyCode == keys[KEY_TWO].keyCode) { key = KEY_TWO; }
etc.
// Now you can write the byte 'key' into your packet, and handle cases normally
// (in PacketHandler after reading the byte 'key')
switch(key) {
case YourKeyBindings.KEY_ONE: // do key one stuff, break;
case YourKeyBindings.KEY_TWO: // do key two stuff, break;
etc.
}
But you can see that you already have to parse through the keys once in your KeyHandler just to get the key code, so why not handle them directly there instead of parsing through them a second time in the packet handler?
I'm really sorry about the confusion caused by that post - it was due to my lack of understanding about the way keys can be configured, and I just really love HashMaps
Which works, but is there a way to do it without editing base classes? Amazingly my mod still edits no base classes and would be nice to keep that.
Of course there is. I was just showing you the class, hoping you could figure out the rest, not so you could edit the base class.
Do you see "ForgeEventFactory.getPlayerDisplayName" ? In Eclipse, 'ctrl-click' on 'getPlayerDisplayName' and it will open the implementation of that method, where you will see that it posts an event! You already know how to handle events, so once you find the event you need (which I just showed you how to find), I assume you'll be all set.
'ctrl-click' is a very powerful tool - I use it all the time to 'stack trace' or just to open up a class, method, or even variable that I want to take a closer look at. I highly recommend everyone to use this tool as much as possible, as you will then find most of the answers yourself (in fact, that's how I found the answers to most of the above questions - I don't know this stuff off the top of my head!)
Well, I think I got that to work, but for some reason I'm getting this error on world startup and when joining a server:
I could really use some help, can give you some of the code if that would help at all, which classes would you need?
An input/output error can occur with packets if you try to read more data than you wrote, since the stream will close when the end is reached and any further attempts to read will throw this exception.
Packets MUST be written and read in the exact same order with the exact same amount of data. Post the code in which you make the packet, as well as the code in which you read the packet.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumMake a method to add xp in your ExtendedPlayer class:
public void addXp(int amount) { this.combatxp += amount; }Then in the event:
@ForgeSubscribe public void EntityItemPickupEvent(EntityPlayer player, EntityItem item) { if (item instanceof EntityXPOrb) { // you need to cast 'item' as EntityXPOrb to access the EntityXPOrb class methods ExtendedPlayer.get(player).addXp(((EntityXPOrb) item).getXpValue()); } }-
View User Profile
-
View Posts
-
Send Message
Curse PremiumDerp sorry, totally escaped me that your method signature is incorrect. For Forge Events, the ONLY method parameter you can have is the Event class that you are watching.
@ForgeSubscribe public void onItemPickup(EntityItemPickupEvent event) { // rest of code goes here, using event.item instead of 'item' }-
View User Profile
-
View Posts
-
Send Message
Curse PremiumDid you register your event handling class to the MinecraftForge.EVENT_BUS? See 'Creating and Using Your EventHandler' in the first post
-
View User Profile
-
View Posts
-
Send Message
Curse Premium'event.entity' is NOT an EntityItem, it is the entity picking up the item.
'event.item' is the EntityItem, as I mentioned earlier.
Btw, you've got a 'public boolean checkcombatxp(int amount)' method in your ExtendedPlayer that looks like it's doing some stuff you may not want it to (subtracting xp amounts and checking 'sufficient'). That's why I always encourage people to type out by hand all the code they are using rather than using copy / paste - it helps to prevent this kind of situation
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumThat brings me to another point: other entities besides EntityPlayer can pick up items, but they will not have your ExtendedPlayer properties, so if they happen to pick up an Xp Orb somehow, Minecraft will crash. Just check if (event.entity instanceof EntityPlayer) before using your extended properties.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumOh, dang it yeah... EntityXPOrb extends Entity, not EntityItem... which means that you're S.O.L. for using EntityItemPickupEvent
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumWhat kinds of problems would that cause? You wouldn't modify the player's Xp, you'd just store a temporary copy of it for comparison. The logic of it is like this:
1. Game starts, store player's current xp in props.lastXp
2. Some number of ticks later, check if players current xp is greater than props.lastXp
3. If so, add player.currentXp - props.lastXp to props.combatXp and set props.lastXp to player.currentXp
4. rinse and repeat
How does that have any effect on enchanting or death?
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumYou could check every single tick if you wanted to without noticeably affecting performance, though it would be better to check less frequently. How big of a deal if you miss a few Xp due to dying in that narrow window between checks? The answer to that determines how frequently you should check.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumIt turns out that the LivingAttackEvent is only called on the server side when mobs or any non-player entity is the source of the attack, which makes sense, but this means that the client side version of the zombie never gets his held item set to null, which means it still looks like he's holding rotten flesh when he shouldn't be.
This brings me to my point: often times Forge events ONLY get called on one side or the other, and this may or may not have various ramifications for your code. If something is not working as expected, check which side(s) the event is being called on and you may just find some useful information.
An easy way to check which side(s) an event is being called on is to put debugging code at the beginning of the event method; so long as that event has access to some kind of entity it is easy to distinguish side, and most events do have an entity:
@ForgeSubscribe public void someEventMethod(SomeEvent event) { System.out.println("Some event called; is this the client side? " + event.entity.worldObj.isRemote); }Just thought I'd share my random experience today. Good luck coding!
if (FMLCommonHandler.instance().getEffectiveSide()].isServer) { EntityPlayerMP player1 = (EntityPlayerMP) player; PacketDispatcher.sendPacketToPlayer(packet, (Player) player1); }EDIT: Nevermind, i feel really dumb now, just have to add a parenthesis...
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumYou are getting stuck in an infinite loop. Don't use 'while' to check inventory, use 'for' loops instead, either using getStackInSlot(i) or calling the inventory directly:
for (ItemStack stack : player.inventory.mainInventory) { if (stack != null && stack.itemID == Block.grass.blockID) { stack.itemID = Block.dirt.blockID; } }I tried it turning dirt into glass and it was quite hilarious digging around
EDIT: The reason you are stuck in an infinite loop is at the end of the parenthesis surrounding your while statement, there is a semi-colon, so it says "while 'x' is less than 41, STOP" which means x never gets incremented. Remove the semi-colon and your logic should work, though you're basically writing a 'for' loop using a 'while' statement.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumSure. One way to do it is to change the display name itself, the other is to intercept the rendering of the display name and add stuff to it. I believe the latter is the way some mods add things like hp bars below names.
To intercept rendering, you need to look at the event types in 'minecraftforge/client/event'; you'll see one called 'RenderLivingEvent', open that up and you'll see there are several different types. Then open up 'RenderLiving' and search for 'RenderLivingEvent', you will find all the places that that event is posted. Luckily, there is one type of the event in particular that gets posted at the beginning of the passSpecialRender method, and that is where the name tag is rendered.
When that event gets posted, you can cancel it so that the standard name doesn't get displayed, then copy / paste the passSpecialRender code into your own method and change what you need. Just be sure to keep everything else intact, as it's needed to render other things properly
I'm also confused on this line in the example packet you listed(specifically ModInfo.CHANNEL), is it specific to your mod or am I missing something?:
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumI actually don't recommend storing the keybindings in a HashMap, as the map does not get updated if the player changes the key bindings using the in-game menu. Likewise, it's not necessary to use a Config file for keys, since the default values are set in the KeyBinding class and the keys are already configurable via the in-game options menu.
I thought I had updated that post with this information - I'll check again.
However, you can still create KeyBinding array and store your key bindings in there, which will give you access to them from anywhere:
/** Key descriptions - this is what the player sees when changing key bindings in-game */ public static final String[] desc = {"Does this","Does that", etc. }; /** Default key values; must be in the same order as the descriptions */ private static final int[] keyValues = {Keyboard.KEY_A, Keyboard.KEY_B, etc. }; public static final KeyBinding[] keys = new KeyBinding[desc.length]; /** * Initializes keybindings and registers a new KeyHandler instance */ public static final void init() { 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 QuestKeyHandler(keys, repeat)); }Yes, 'ModInfo' is just a reference class that stores the String identifier for my packet's CHANNEL.
} switch (key) { case Keyboard.KEY_RBRACKET: System.out.println("This worked"); break; }what I have there doesn't seem to be working.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumOh right, chat
public String getDisplayName() { if(this.displayname == null) { this.displayname = ForgeEventFactory.getPlayerDisplayName(this, this.username); } return this.displayname; }That should easily give you what you need.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumRight. You will need to handle the key presses from within your KeyHandler's keyDown and/or keyUp methods, rather than sending a blanket 'key pressed Packet', although you could still send a packet like that using the byte codes for your key index, rather than the key code itself:
/** Key index for easy handling and retrieval of keys and key descriptions */ public static final byte KEY_ONE = 0, KEY_TWO = 1, etc; // please use more descriptive names // when making your packet, you'll have to use lots of 'if / else-if' statements since we no longer have a handy HashMap // this is in keyDown method: byte key; if (kb.keyCode == keys[KEY_ONE].keyCode) { key = KEY_ONE; } else if (kb.keyCode == keys[KEY_TWO].keyCode) { key = KEY_TWO; } etc. // Now you can write the byte 'key' into your packet, and handle cases normally // (in PacketHandler after reading the byte 'key') switch(key) { case YourKeyBindings.KEY_ONE: // do key one stuff, break; case YourKeyBindings.KEY_TWO: // do key two stuff, break; etc. }But you can see that you already have to parse through the keys once in your KeyHandler just to get the key code, so why not handle them directly there instead of parsing through them a second time in the packet handler?
I'm really sorry about the confusion caused by that post - it was due to my lack of understanding about the way keys can be configured, and I just really love HashMaps
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumOf course there is. I was just showing you the class, hoping you could figure out the rest, not so you could edit the base class.
Do you see "ForgeEventFactory.getPlayerDisplayName" ? In Eclipse, 'ctrl-click' on 'getPlayerDisplayName' and it will open the implementation of that method, where you will see that it posts an event! You already know how to handle events, so once you find the event you need (which I just showed you how to find), I assume you'll be all set.
'ctrl-click' is a very powerful tool - I use it all the time to 'stack trace' or just to open up a class, method, or even variable that I want to take a closer look at. I highly recommend everyone to use this tool as much as possible, as you will then find most of the answers yourself (in fact, that's how I found the answers to most of the above questions - I don't know this stuff off the top of my head!)
Well, I think I got that to work, but for some reason I'm getting this error on world startup and when joining a server:
2013-12-24 18:17:06 [INFO] [STDERR] java.io.EOFException
2013-12-24 18:17:06 [INFO] [STDERR] at java.io.DataInputStream.readByte(Unknown Source)
2013-12-24 18:17:06 [INFO] [STDERR] at afg.flash.PacketHandler.handleExtendedProperties(PacketHandler.java:46)
2013-12-24 18:17:06 [INFO] [STDERR] at afg.flash.PacketHandler.onPacketData(PacketHandler.java:23)
2013-12-24 18:17:06 [INFO] [STDERR] at cpw.mods.fml.common.network.NetworkRegistry.handlePacket(NetworkRegistry.java:255)
2013-12-24 18:17:06 [INFO] [STDERR] at cpw.mods.fml.common.network.NetworkRegistry.handleCustomPacket(NetworkRegistry.java:245)
2013-12-24 18:17:06 [INFO] [STDERR] at cpw.mods.fml.common.network.FMLNetworkHandler.handlePacket250Packet(FMLNetworkHandler.java:85)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.client.multiplayer.NetClientHandler.handleCustomPayload(NetClientHandler.java:1651)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.network.packet.Packet250CustomPayload.processPacket(Packet250CustomPayload.java:70)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.network.MemoryConnection.processReadPackets(MemoryConnection.java:89)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.client.Minecraft.runTick(Minecraft.java:1967)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:910)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.client.Minecraft.run(Minecraft.java:838)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.client.main.Main.main(Main.java:93)
2013-12-24 18:17:06 [INFO] [STDERR] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-12-24 18:17:06 [INFO] [STDERR] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
2013-12-24 18:17:06 [INFO] [STDERR] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
2013-12-24 18:17:06 [INFO] [STDERR] at java.lang.reflect.Method.invoke(Unknown Source)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.launchwrapper.Launch.launch(Launch.java:131)
2013-12-24 18:17:06 [INFO] [STDERR] at net.minecraft.launchwrapper.Launch.main(Launch.java:27)
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumAn input/output error can occur with packets if you try to read more data than you wrote, since the stream will close when the end is reached and any further attempts to read will throw this exception.
Packets MUST be written and read in the exact same order with the exact same amount of data. Post the code in which you make the packet, as well as the code in which you read the packet.