// in the Gui class:
@Override
protected void keyTyped(char c, int key) {
// key '1' is Escape, and since my Gui was related to inventory, I also flag the vanilla 'inventory' key to close it just for convenience
if (key == 1 || key == mc.gameSettings.keyBindInventory.getKeyCode() || key == YourKeyHandler.keys[YourKeyHandler.CUSTOM_KEY].getKeyCode()) {
mc.thePlayer.closeScreen();
}
}
I have succesfully made custom inventory with 2 slots. But for now they can only accept items that i give them what items can be stored there.
But i want to change that to additional armor slot like 5th armor slot. Not boots legs chest or helmet. But like a cape or gloves.
Can you please help me with that, i have no idea how to do that. It would be the greatest thing . I will be checking your awesome tutorial for your replay.
You wold need to create a new class for the kind of armor you want to put there and use polymorphism. Let's say you want to add rings. You would need a class called "Ring" that extends item and have your custom rings all extend that class. Then you would check if the item is an instance of a ring, with instanceof.
Exactly like explained there ^^^^ One other thing you need to be aware of, if you want your custom armor slots to actually DO something (and I need to put this in the tutorial), is you need to check them every tick and call Item#onUpdate for the items in the slots; if you make new 'armor' slots, you'd want to also call Item#onArmorTick (or whatever that method is called now), in addition to onUpdate - you want your slots to behave as close as possible to the vanilla style. This way, if someone ever adds a new Ring, Cape, or whatever, they can have it do things each tick (like regular armor) just by using the onArmorTick method.
When doing so, however, be very careful to pass the correct parameters xD
If you could make a tutorial that would be awesome !!! for custom armor slots. I would really appriciate it.
That's what this whole topic is already about Once you have the custom slots added, which is what the tutorial in the OP shows, adding extra functionality is easy:
1. Subscribe to PlayerTickEvent (or LivingUpdateEvent for event.entity instanceof EntityPlayer, but the former is better)
2. In that method, check all of your custom inventory slots and call the appropriate methods (e.g. Item#onUpdate) for each of them
See? Two steps and your Items will behave just like vanilla items do in the inventory. If you wanted to add armor bonuses, it would take a little bit more work, but this will get you 99% of the functionality that you will see in most mods.
Thanks for this but sory i have no idea how to do that, i have made github, i saw you were on. And i gave you editing ability. Could you edit this 2 lines that you were talking about please, i would really love it. here is the link: https://github.com/proalt/CustomInventoryForArmor
All my files are on, that i made from this tutorial.
If you don't know what I mean by Forge Events, have a look here - it's a tutorial on how to use them. Sorry I don't typically help people by writing code for them - I just try to teach you how to write it yourself.
...the PacketPipeline class (and you actually probably shouldn't be using that now, but that's a post for another day).\
*prod*
I've digested your tutorial on custom inventories. Thank you for writing it. But the PacketPipeline code has been marked as obsolete in the wiki link. Something about memory leaks. Anyway, I eagerly await your new implementation. Thanks!
I've digested your tutorial on custom inventories. Thank you for writing it. But the PacketPipeline code has been marked as obsolete in the wiki link. Something about memory leaks. Anyway, I eagerly await your new implementation. Thanks!
I've just about got it down While the one I wrote is slightly more complex, syntax-wise, than what is really necessary, I quite like it.
I plan on writing a new tutorial specifically about packet handling; here is the current (useable) code if you wanted to get started now. Alternatively, you could see a simpler way to do it here.
PacketDispatcher class (provides SimpleNetworkWrapper instance and wrapper methods)
/**
*
* This class will house the SimpleNetworkWrapper instance, which I will name 'dispatcher',
* as well as give us a logical place to from which register our packets. These two things
* could be done anywhere, however, even in your Main class, but I will be adding other
* functionality (see below) that gives this class a bit more utility.
*
* While unnecessary, I'm going to turn this class into a 'wrapper' for SimpleNetworkWrapper
* so that instead of writing "PacketDispatcher.dispatcher.{method}" I can simply write
* "PacketDispatcher.{method}" All this does is make it quicker to type and slightly shorter;
* if you do not care about that, then make the 'dispatcher' field public instead of private,
* or, if you do not want to add a new class just for one field and one static method that
* you could put anywhere, feel free to put them wherever.
*
* For further convenience, I have also added two extra sendToAllAround methods: one which
* takes an EntityPlayer and one which takes coordinates.
*
*/
public class PacketDispatcher
{
// a simple counter will allow us to get rid of 'magic' numbers used during packet registration
private static byte packetId = 0;
/**
* The SimpleNetworkWrapper instance is used both to register and send packets.
* Since I will be adding wrapper methods, this field is private, but you should
* make it public if you plan on using it directly.
*/
private static final SimpleNetworkWrapper dispatcher = NetworkRegistry.INSTANCE.newSimpleChannel(TutorialMain.MOD_ID);
/**
* Call this during pre-init or loading and register all of your packets (messages) here
*/
public static final void registerPackets() {
// Using an incrementing field instead of hard-coded numerals, I don't need to think
// about what number comes next or if I missed on should I ever rearrange the order
// of registration (for instance, if you wanted to alphabetize them... yeah...)
// It's even easier if you create a convenient 'registerMessage' method:
PacketDispatcher.registerMessage(OpenGuiMessage.class, Side.SERVER);
PacketDispatcher.registerMessage(SyncPlayerPropsMessage.class, Side.CLIENT);
// If you don't want to make a 'registerMessage' method, you can do it directly:
//PacketDispatcher.dispatcher.registerMessage(OpenGuiMessage.class, OpenGuiMessage.class, packetId++, Side.SERVER);
//PacketDispatcher.dispatcher.registerMessage(SyncPlayerPropsMessage.class, SyncPlayerPropsMessage.class, packetId++, Side.CLIENT);
}
/**
* Registers a message and message handler
* @param messageClass must implement both IMessage and IMessageHandler<MessageClass>
*/
private static final void registerMessage(Class messageClass, Side side) {
PacketDispatcher.dispatcher.registerMessage(messageClass, messageClass, packetId++, side);
}
//========================================================//
// The following methods are the 'wrapper' methods; again,
// this just makes sending a message slightly more compact
// and is purely a matter of stylistic preference
//========================================================//
/**
* Send this message to the specified player.
* See {@link SimpleNetworkWrapper#sendTo(IMessage, EntityPlayerMP)}
*/
public static final void sendTo(IMessage message, EntityPlayerMP player) {
PacketDispatcher.dispatcher.sendTo(message, player);
}
/**
* Send this message to everyone within a certain range of a point.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, NetworkRegistry.TargetPoint)}
*/
public static final void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) {
PacketDispatcher.dispatcher.sendToAllAround(message, point);
}
/**
* Sends a message to everyone within a certain range of the coordinates in the same dimension.
*/
public static final void sendToAllAround(IMessage message, int dimension, double x, double y, double z, double range) {
PacketDispatcher.sendToAllAround(message, new NetworkRegistry.TargetPoint(dimension, x, y, z, range));
}
/**
* Sends a message to everyone within a certain range of the player provided.
*/
public static final void sendToAllAround(IMessage message, EntityPlayer player, double range) {
PacketDispatcher.sendToAllAround(message, player.worldObj.provider.dimensionId, player.posX, player.posY, player.posZ, range);
}
/**
* Send this message to everyone within the supplied dimension.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, int)}
*/
public static final void sendToDimension(IMessage message, int dimensionId) {
PacketDispatcher.dispatcher.sendToDimension(message, dimensionId);
}
/**
* Send this message to the server.
* See {@link SimpleNetworkWrapper#sendToServer(IMessage)}
*/
public static final void sendToServer(IMessage message) {
PacketDispatcher.dispatcher.sendToServer(message);
}
}
AbstractMessage class (does some fun stuff for later)
/**
*
* Syntax for extending this class is "public class YourMessage extends AbstractMessage<YourMessage>"
*
* I prefer to have an EntityPlayer readily available when handling packets, as well as to
* know which side I'm on without having to check every time, so I handle those operations
* here and pass off the rest of the work to abstract methods to be handled in each sub-class.
*
* There is nothing about this class that is more 'correct' than the more typical ways of
* dealing with packets, so if this way doesn't make much sense to you, go ahead and use
* whatever way does make sense - it's really just a matter of personal preference.
*
* @author coolAlias
*
*/
public abstract class AbstractMessage<T extends IMessage> implements IMessage, IMessageHandler <T, IMessage>
{
/**
* Handle a message received on the client side
* @return a message to send back to the Server, or null if no reply is necessary
*/
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);
/**
* Handle a message received on the server side
* @return a message to send back to the Client, or null if no reply is necessary
*/
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);
/*
* Here is where I parse the side and get the player to pass on to the abstract methods.
* This way it is immediately clear which side received the packet without having to
* remember or check on which side it was registered, the player is immediately available
* without a lengthy syntax, and I just find it easier.
*/
/**
* CAUTION: IMessageHandler operates as a separate instance of your message class,
* meaning your class fields will not be what you expect! Use the "T message" parameter
* to access the fields with the correct values, as that is the message that was sent.
*
* This is because message handling is done with a separate class instance;
* in fact, technically you could implement the IMessageHandler as an entirely
* different class:
* public class YourMessage implements IMessage
* public class YourMessageHandler implements IMessageHandler<YourMessage>
*
* From the above it is easier to see why the class fields from 'YourMessage' will not
* be accessible in the class 'YourMessageHandler', and even though we implement them
* together, the actual instance of the class that is used during fromBytes/toBytes is
* different from that used during onMessage (but the former is passed into onMessage
* as the 'message' parameter!).
*/
@Override
public IMessage onMessage(T message, MessageContext ctx) {
if (ctx.side == Side.CLIENT) {
return handleClientMessage(Minecraft.getMinecraft().thePlayer, message, ctx);
} else {
return handleServerMessage(ctx.getServerHandler().playerEntity, message, ctx);
}
}
}
Specific Message (packet) classes
/**
* If you are going with the more simple route, then this class declaration would be:
*
* public class OpenGuiMessage implements IMessage, IMessageHandler <OpenGuiMessage, IMessage>
*
* and you would need to implement the IMessageHandler method directly in each of your
* message classes instead of once generically like I did in AbstractMessage.
*
*/
public class OpenGuiMessage extends AbstractMessage<OpenGuiMessage>
{
// this will store the id of the gui to open
private int id;
// The basic, no-argument constructor MUST be included to use the new automated handling
public OpenGuiMessage() {}
// if there are any class fields, be sure to provide a constructor that allows
// for them to be initialized, and use that constructor when sending the packet
public OpenGuiMessage(int id) {
this.id = id;
}
@Override
public void fromBytes(ByteBuf buffer) {
// basic Input/Output operations, very much like DataInputStream
id = buffer.readInt();
}
@Override
public void toBytes(ByteBuf buffer) {
// basic Input/Output operations, very much like DataOutputStream
buffer.writeInt(id);
}
@Override
public IMessage handleClientMessage(EntityPlayer player, OpenGuiMessage message, MessageContext ctx) {
return null;
}
@Override
public IMessage handleServerMessage(EntityPlayer player, OpenGuiMessage message, MessageContext ctx) {
// because we sent the gui's id with the packet, we can handle all cases with one line:
player.openGui(TutorialMain.instance, message.id, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
return null;
}
}
Please note that most of the stuff I did in there is ENTIRELY UNNECESSARY, and I only did it out of personal preference The other, simpler solution I linked to above works just fine, too. I just wanted the player object to be available to me directly when handling the message, and then got a little carried away with it. It's still pretty straightforward, but choose whichever style suits you best.
Please note that most of the stuff I did in there is ENTIRELY UNNECESSARY, and I only did it out of personal preference The other, simpler solution I linked to above works just fine, too. I just wanted the player object to be available to me directly when handling the message, and then got a little carried away with it. It's still pretty straightforward, but choose whichever style suits you best.
Hm. I already had a bunch of registered events set up from a while back if I ever needed them. I have a PlayerTickEvent set up and registered in commonproxy's initialize(). So that all should be fine and working. I'll continue testing but how do I check for a specific Armor slot. Like the actual lines of coding. I'm doing something majorly wrong. I was able to get slots to be checked for but I can't find the route to the custom slots
Hm. I already had a bunch of registered events set up from a while back if I ever needed them. I have a PlayerTickEvent set up and registered in commonproxy's initialize(). So that all should be fine and working. I'll continue testing but how do I check for a specific Armor slot. Like the actual lines of coding. I'm doing something majorly wrong. I was able to get slots to be checked for but I can't find the route to the custom slots
If you followed my tutorial, then your custom slots would be located in your Extended Properties class, e.g. ExtendedPlayer, and you would retrieve them by first getting the extended properties for the player, and then accessing the inventory field in your properties, just like any other class member:
public class SomeClass {
public Type someField; // where Type is 'int', 'String', 'IInventory', or whatever
}
// elsewhere:
SomeClass object = new SomeClass();
object.someField = some_value;
This is basic Java syntax, not anything specific to Minecraft modding. If you look in the tutorial, you'll see where I access the ExtendedProperties for a player several times like "ExtendedPlayer.get(player).someField", so if you named your inventory slots 'inventory', you would have "ExtendedPlayer.get(player).inventory". And there you go, you've just accessed your custom inventory and can iterate through the slots (if you stored them as an array) or access them one-by-one (if for some reason you stored each one as a separate field). If you don't understand any of the terms I've used, please please please Google them or search the JavaDocs on Oracle's website - you will be doing yourself a HUGE favor by learning the fundamentals of Java.
If you followed my tutorial, then your custom slots would be located in your Extended Properties class, e.g. ExtendedPlayer, and you would retrieve them by first getting the extended properties for the player, and then accessing the inventory field in your properties, just like any other class member:
public class SomeClass {
public Type someField; // where Type is 'int', 'String', 'IInventory', or whatever
}
// elsewhere:
SomeClass object = new SomeClass();
object.someField = some_value;
This is basic Java syntax, not anything specific to Minecraft modding. If you look in the tutorial, you'll see where I access the ExtendedProperties for a player several times like "ExtendedPlayer.get(player).someField", so if you named your inventory slots 'inventory', you would have "ExtendedPlayer.get(player).inventory". And there you go, you've just accessed your custom inventory and can iterate through the slots (if you stored them as an array) or access them one-by-one (if for some reason you stored each one as a separate field). If you don't understand any of the terms I've used, please please please Google them or search the JavaDocs on Oracle's website - you will be doing yourself a HUGE favor by learning the fundamentals of Java.
Player won't resolve to a variable. and I realized after I got to the .inventory that I don't know what the actual update tick for armor requires me to do. Does it just want me to have it direct it to the slot. I was playing around having special stuff happen to normal inventory but I'm just here to update so it will read the special equip's defense/attack bonuses. I'm trying to understand this but... right now I've got it as
@SubscribeEvent
public void onArmorTick(PlayerTickEvent event)
{
if (ExtendedPlayer.get(player).inventory
Hello, I have some problems with this tutorial
I dont know what you mean with "you have one, right? If not, check the prerequisites for help" in Step 4.1 ...
And becouse of that, I dont know how to finish it.
Some help would be nice =)
(PS: I am very bad in English, so thats why I have this problem.)
Hello, I have some problems with this tutorial
I dont know what you mean with "you have one, right? If not, check the prerequisites for help" in Step 4.1 ...
And becouse of that, I dont know how to finish it.
Some help would be nice =)
(PS: I am very bad in English, so thats why I have this problem.)
"add 3 lines to our existing ExtendedPlayer class, (you have one, right?)", here "you have one" refers to "one (an) ExtendedPlayer class", as in you have an ExtendedPlayer class, right? In section 4.1, we are adding several lines to the ExtendedPlayer class.
Player won't resolve to a variable. and I realized after I got to the .inventory that I don't know what the actual update tick for armor requires me to do. Does it just want me to have it direct it to the slot. I was playing around having special stuff happen to normal inventory but I'm just here to update so it will read the special equip's defense/attack bonuses. I'm trying to understand this but... right now I've got it as
@SubscribeEvent
public void onArmorTick(PlayerTickEvent event)
{
if (ExtendedPlayer.get(player).inventory
Please heed my earlier advice and spend some time learning basic Java. Google "Java can't resolve to a variable" or something like that, and you will probably find some useful information. Or you can start here: http://docs.oracle.com/javase/tutorial/java/index.html
Please heed my earlier advice and spend some time learning basic Java. Google "Java can't resolve to a variable" or something like that, and you will probably find some useful information. Or you can start here: http://docs.oracle.com/javase/tutorial/java/index.html
So I did some research and fixed the problem. I can't find the custom slots in it though.
I've changed the slot number a bunch of times yet I'm not getting any successes to know if I even have the right slot
I got it to work when the player is hurt and when another entity is hurt
So I did some research and fixed the problem. I can't find the custom slots in it though.
I've changed the slot number a bunch of times yet I'm not getting any successes to know if I even have the right slot
I got it to work when the player is hurt and when another entity is hurt
Awesome, that's progress xD Can you post the code with which you are having trouble?
@SubscribeEvent
public void onLivingHurtEnemy(LivingHurtEvent event)
{
if (!(event.entity instanceof EntityPlayer)) {
{
System.out.println("got thus far");
EntityPlayer player = (EntityPlayer) event.entityLiving;
if (ExtendedPlayer.get(player).inventory.getSizeInventory() != 0)
{
System.out.println("your pain is noticed");
}
else
{
}
}
}
}
@SubscribeEvent
public void onLivingHurtPlayer(LivingHurtEvent event)
{
if (event.entity instanceof EntityPlayer) {
{
EntityPlayer player = (EntityPlayer) event.entityLiving;
if (ExtendedPlayer.get(player).inventory.getSizeInventory() != 0)
{
System.out.println("your pain is shared");
}
else
{
}
}
}
}
I would think that it would be a better idea to add the stats here (you mentioned using attributes, but would it be able to stack?). And pretty much just adding events that hurt mobs or take more of your damage if the slot is wearing the right item. And specific dmg/def for different items in the same slot
In the entity living getting hurt it obviously doesn't work since it's not a player entity, but I threw it in there. I'm not sure how calculating attack bonuses would even be added. (resistance (defense) for that sake as well.)
Hi! You've made a very cool tutorial!! So I've got a problem with NBT's, I want to create chests that can comunicate each other but I don't know how to read the bytes in a chest by reading the NBT tag from an other chest. Can you help me? Sorry for my bad English
Thanks, and sorry, but that is well outside the scope of this tutorial. I recommend you create a new topic in the "Modification Development" section of the forums - likely you will get many people helping you there. Good luck!
Okay, now i made custom inventory and the custom armor slot, but i came to a problem. I Give my armor normal one example helmet on and its up now i give helmet to the custom helmet slot and it doesnt show up on my head of the entity or anything on shield amount, nothing shows up. I can wear 2 helmets at a time but only 1 Works becouse i guess the custom armor slot that i made doesnt accept effect of the armor or item that is in ?! help
This is the next section I plan to add to the tutorial, as it seems to come up a lot. Adding a custom inventory slot by itself doesn't automatically work like a normal inventory slot, but it's easy to add. As I explained in some earlier post, you need to do the following:
1. Find an appropriate ticking method or event, such as PlayerTickEvent
2. Within the above method/event, get the player's custom inventory slots from your ExtendedPlayer or similar class
3. Run through each slot and, if there is an itemstack in there, call the appropriate tick methods:
// stack is an ItemStack (or possibly null) retrieved from each of your custom inventory slots
// for ALL items:
if (stack != null) {
// call the onUpdate method with your custom slot index, however you decide to define that, and whether
// the item is considered to be 'held' i.e. equipped, probably always true for custom slots
stack.getItem().onUpdate(stack, player.worldObj, player, slotIndex, isHeld);
// now for ARMOR type items, still within the not null check:
if (stack.getItem() instanceof ItemArmor) { // or custom items that you want to behave like armor
// NOTE that onArmorTick is actually an Item class method, not only ItemArmor, so you could
// technically call it for all items without checking if it's armor at all
stack.getItem().onArmorTick(player.worldObj, player, stack);
}
}
That should get some basic ticking functionality for you.
For AttributeModifiers, it's a bit trickier, as those calculations are done somewhere deep in vanilla code that is not readily accessible so far as I know (I didn't look too hard, but please let me know if you find one!). The solution I've come up with is a bit clunky, but it works:
1. Create an interface, such as IEquippable, with a method such as 'public void onEquipChange(EntityPlayer, ItemStack, int customSlotIndex, bool isEquipped)'
2. Each of your Items that can be equipped in a custom slot should implement this method, removing and adding all the modifiers from getItemAttributeModifiers(ItemStack) and any other custom bonuses you have
3. For each of your custom slots, store a 'lastWornItem' in the ExtendedPlayer (all of them should be null initially)
4. During the update tick, check if the lastWornItem is different than what is currently in the slot and, if so, call your custom method first with the last worn item to unequip its bonuses, then with the new itemstack to apply its bonuses.
It's probably not the most efficient solution, but it's not terribly inefficient, either, so long as you don't go crazy with custom slots and add in tons of checks everywhere. You can see such an implementation in my Zelda mod - check out the ItemArmorBoots and ExtendedPlayer classes, but please note that I did NOT create an interface, though I should have and should do, as it is a cleaner and more flexible solution.
For rendering, this first requires that the client side is aware of what is in your custom slots, so whenever the currently equipped item changes or the player first joins the world, you will have to send the ItemStack to the client in a packet (there is a vanilla packet for this that you can use as a template, but not directly because your slots are not in the regular inventory system).
Once the client is aware of all the equipped items in the custom slots (or just whichever ones you want to add rendering for), then it's merely a matter of rendering it, probably RenderPlayerEvent.Post is a good place for that. I will leave the render code up to you, as that is outside the scope of this tutorial.
Hope that helps, and I do plan on adding a more detailed approach to the tutorial at some point in the future.
I Just followed this tutorial to try and make an Item that had only one slot but it isn't working. When I right-click with the item equipped it does nothing and I haven't the foggiest what could be causing that. Any help would be welcome.
Also, tell me if you want to see my version of the code. The only things I changed were some of the names (Obviously) and the INV_SIZE = 1 instead of 8
I Just followed this tutorial to try and make an Item that had only one slot but it isn't working. When I right-click with the item equipped it does nothing and I haven't the foggiest what could be causing that. Any help would be welcome.
Also, tell me if you want to see my version of the code. The only things I changed were some of the names (Obviously) and the INV_SIZE = 1 instead of 8
Eating any good bacteria lately? (in reference to your username...).
Yes, I need to see your code, but before you show it to me, perhaps try going back through the tutorial and make sure you followed every step correctly - you may just find your answer there.
Awesome. Thanks ^.^
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumExactly like explained there ^^^^ One other thing you need to be aware of, if you want your custom armor slots to actually DO something (and I need to put this in the tutorial), is you need to check them every tick and call Item#onUpdate for the items in the slots; if you make new 'armor' slots, you'd want to also call Item#onArmorTick (or whatever that method is called now), in addition to onUpdate - you want your slots to behave as close as possible to the vanilla style. This way, if someone ever adds a new Ring, Cape, or whatever, they can have it do things each tick (like regular armor) just by using the onArmorTick method.
When doing so, however, be very careful to pass the correct parameters xD
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumThat's what this whole topic is already about
1. Subscribe to PlayerTickEvent (or LivingUpdateEvent for event.entity instanceof EntityPlayer, but the former is better)
2. In that method, check all of your custom inventory slots and call the appropriate methods (e.g. Item#onUpdate) for each of them
See? Two steps and your Items will behave just like vanilla items do in the inventory. If you wanted to add armor bonuses, it would take a little bit more work, but this will get you 99% of the functionality that you will see in most mods.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumIf you don't know what I mean by Forge Events, have a look here - it's a tutorial on how to use them. Sorry I don't typically help people by writing code for them - I just try to teach you how to write it yourself.
*prod*
I've digested your tutorial on custom inventories. Thank you for writing it. But the PacketPipeline code has been marked as obsolete in the wiki link. Something about memory leaks. Anyway, I eagerly await your new implementation. Thanks!
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumI've just about got it down
I plan on writing a new tutorial specifically about packet handling; here is the current (useable) code if you wanted to get started now. Alternatively, you could see a simpler way to do it here.
PacketDispatcher class (provides SimpleNetworkWrapper instance and wrapper methods)
/** * * This class will house the SimpleNetworkWrapper instance, which I will name 'dispatcher', * as well as give us a logical place to from which register our packets. These two things * could be done anywhere, however, even in your Main class, but I will be adding other * functionality (see below) that gives this class a bit more utility. * * While unnecessary, I'm going to turn this class into a 'wrapper' for SimpleNetworkWrapper * so that instead of writing "PacketDispatcher.dispatcher.{method}" I can simply write * "PacketDispatcher.{method}" All this does is make it quicker to type and slightly shorter; * if you do not care about that, then make the 'dispatcher' field public instead of private, * or, if you do not want to add a new class just for one field and one static method that * you could put anywhere, feel free to put them wherever. * * For further convenience, I have also added two extra sendToAllAround methods: one which * takes an EntityPlayer and one which takes coordinates. * */ public class PacketDispatcher { // a simple counter will allow us to get rid of 'magic' numbers used during packet registration private static byte packetId = 0; /** * The SimpleNetworkWrapper instance is used both to register and send packets. * Since I will be adding wrapper methods, this field is private, but you should * make it public if you plan on using it directly. */ private static final SimpleNetworkWrapper dispatcher = NetworkRegistry.INSTANCE.newSimpleChannel(TutorialMain.MOD_ID); /** * Call this during pre-init or loading and register all of your packets (messages) here */ public static final void registerPackets() { // Using an incrementing field instead of hard-coded numerals, I don't need to think // about what number comes next or if I missed on should I ever rearrange the order // of registration (for instance, if you wanted to alphabetize them... yeah...) // It's even easier if you create a convenient 'registerMessage' method: PacketDispatcher.registerMessage(OpenGuiMessage.class, Side.SERVER); PacketDispatcher.registerMessage(SyncPlayerPropsMessage.class, Side.CLIENT); // If you don't want to make a 'registerMessage' method, you can do it directly: //PacketDispatcher.dispatcher.registerMessage(OpenGuiMessage.class, OpenGuiMessage.class, packetId++, Side.SERVER); //PacketDispatcher.dispatcher.registerMessage(SyncPlayerPropsMessage.class, SyncPlayerPropsMessage.class, packetId++, Side.CLIENT); } /** * Registers a message and message handler * @param messageClass must implement both IMessage and IMessageHandler<MessageClass> */ private static final void registerMessage(Class messageClass, Side side) { PacketDispatcher.dispatcher.registerMessage(messageClass, messageClass, packetId++, side); } //========================================================// // The following methods are the 'wrapper' methods; again, // this just makes sending a message slightly more compact // and is purely a matter of stylistic preference //========================================================// /** * Send this message to the specified player. * See {@link SimpleNetworkWrapper#sendTo(IMessage, EntityPlayerMP)} */ public static final void sendTo(IMessage message, EntityPlayerMP player) { PacketDispatcher.dispatcher.sendTo(message, player); } /** * Send this message to everyone within a certain range of a point. * See {@link SimpleNetworkWrapper#sendToDimension(IMessage, NetworkRegistry.TargetPoint)} */ public static final void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) { PacketDispatcher.dispatcher.sendToAllAround(message, point); } /** * Sends a message to everyone within a certain range of the coordinates in the same dimension. */ public static final void sendToAllAround(IMessage message, int dimension, double x, double y, double z, double range) { PacketDispatcher.sendToAllAround(message, new NetworkRegistry.TargetPoint(dimension, x, y, z, range)); } /** * Sends a message to everyone within a certain range of the player provided. */ public static final void sendToAllAround(IMessage message, EntityPlayer player, double range) { PacketDispatcher.sendToAllAround(message, player.worldObj.provider.dimensionId, player.posX, player.posY, player.posZ, range); } /** * Send this message to everyone within the supplied dimension. * See {@link SimpleNetworkWrapper#sendToDimension(IMessage, int)} */ public static final void sendToDimension(IMessage message, int dimensionId) { PacketDispatcher.dispatcher.sendToDimension(message, dimensionId); } /** * Send this message to the server. * See {@link SimpleNetworkWrapper#sendToServer(IMessage)} */ public static final void sendToServer(IMessage message) { PacketDispatcher.dispatcher.sendToServer(message); } }/** * * Syntax for extending this class is "public class YourMessage extends AbstractMessage<YourMessage>" * * I prefer to have an EntityPlayer readily available when handling packets, as well as to * know which side I'm on without having to check every time, so I handle those operations * here and pass off the rest of the work to abstract methods to be handled in each sub-class. * * There is nothing about this class that is more 'correct' than the more typical ways of * dealing with packets, so if this way doesn't make much sense to you, go ahead and use * whatever way does make sense - it's really just a matter of personal preference. * * @author coolAlias * */ public abstract class AbstractMessage<T extends IMessage> implements IMessage, IMessageHandler <T, IMessage> { /** * Handle a message received on the client side * @return a message to send back to the Server, or null if no reply is necessary */ public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx); /** * Handle a message received on the server side * @return a message to send back to the Client, or null if no reply is necessary */ public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx); /* * Here is where I parse the side and get the player to pass on to the abstract methods. * This way it is immediately clear which side received the packet without having to * remember or check on which side it was registered, the player is immediately available * without a lengthy syntax, and I just find it easier. */ /** * CAUTION: IMessageHandler operates as a separate instance of your message class, * meaning your class fields will not be what you expect! Use the "T message" parameter * to access the fields with the correct values, as that is the message that was sent. * * This is because message handling is done with a separate class instance; * in fact, technically you could implement the IMessageHandler as an entirely * different class: * public class YourMessage implements IMessage * public class YourMessageHandler implements IMessageHandler<YourMessage> * * From the above it is easier to see why the class fields from 'YourMessage' will not * be accessible in the class 'YourMessageHandler', and even though we implement them * together, the actual instance of the class that is used during fromBytes/toBytes is * different from that used during onMessage (but the former is passed into onMessage * as the 'message' parameter!). */ @Override public IMessage onMessage(T message, MessageContext ctx) { if (ctx.side == Side.CLIENT) { return handleClientMessage(Minecraft.getMinecraft().thePlayer, message, ctx); } else { return handleServerMessage(ctx.getServerHandler().playerEntity, message, ctx); } } }/** * If you are going with the more simple route, then this class declaration would be: * * public class OpenGuiMessage implements IMessage, IMessageHandler <OpenGuiMessage, IMessage> * * and you would need to implement the IMessageHandler method directly in each of your * message classes instead of once generically like I did in AbstractMessage. * */ public class OpenGuiMessage extends AbstractMessage<OpenGuiMessage> { // this will store the id of the gui to open private int id; // The basic, no-argument constructor MUST be included to use the new automated handling public OpenGuiMessage() {} // if there are any class fields, be sure to provide a constructor that allows // for them to be initialized, and use that constructor when sending the packet public OpenGuiMessage(int id) { this.id = id; } @Override public void fromBytes(ByteBuf buffer) { // basic Input/Output operations, very much like DataInputStream id = buffer.readInt(); } @Override public void toBytes(ByteBuf buffer) { // basic Input/Output operations, very much like DataOutputStream buffer.writeInt(id); } @Override public IMessage handleClientMessage(EntityPlayer player, OpenGuiMessage message, MessageContext ctx) { return null; } @Override public IMessage handleServerMessage(EntityPlayer player, OpenGuiMessage message, MessageContext ctx) { // because we sent the gui's id with the packet, we can handle all cases with one line: player.openGui(TutorialMain.instance, message.id, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ); return null; } }Hm. I already had a bunch of registered events set up from a while back if I ever needed them. I have a PlayerTickEvent set up and registered in commonproxy's initialize(). So that all should be fine and working. I'll continue testing but how do I check for a specific Armor slot. Like the actual lines of coding. I'm doing something majorly wrong. I was able to get slots to be checked for but I can't find the route to the custom slots
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumIf you followed my tutorial, then your custom slots would be located in your Extended Properties class, e.g. ExtendedPlayer, and you would retrieve them by first getting the extended properties for the player, and then accessing the inventory field in your properties, just like any other class member:
public class SomeClass { public Type someField; // where Type is 'int', 'String', 'IInventory', or whatever } // elsewhere: SomeClass object = new SomeClass(); object.someField = some_value;This is basic Java syntax, not anything specific to Minecraft modding. If you look in the tutorial, you'll see where I access the ExtendedProperties for a player several times like "ExtendedPlayer.get(player).someField", so if you named your inventory slots 'inventory', you would have "ExtendedPlayer.get(player).inventory". And there you go, you've just accessed your custom inventory and can iterate through the slots (if you stored them as an array) or access them one-by-one (if for some reason you stored each one as a separate field). If you don't understand any of the terms I've used, please please please Google them or search the JavaDocs on Oracle's website - you will be doing yourself a HUGE favor by learning the fundamentals of Java.
Player won't resolve to a variable. and I realized after I got to the .inventory that I don't know what the actual update tick for armor requires me to do. Does it just want me to have it direct it to the slot. I was playing around having special stuff happen to normal inventory but I'm just here to update so it will read the special equip's defense/attack bonuses. I'm trying to understand this but... right now I've got it as
@SubscribeEvent
public void onArmorTick(PlayerTickEvent event)
{
if (ExtendedPlayer.get(player).inventory
I dont know what you mean with "you have one, right? If not, check the prerequisites for help" in Step 4.1 ...
And becouse of that, I dont know how to finish it.
Some help would be nice =)
(PS: I am very bad in English, so thats why I have this problem.)
-
View User Profile
-
View Posts
-
Send Message
Curse Premium"add 3 lines to our existing ExtendedPlayer class, (you have one, right?)", here "you have one" refers to "one (an) ExtendedPlayer class", as in you have an ExtendedPlayer class, right? In section 4.1, we are adding several lines to the ExtendedPlayer class.
Please heed my earlier advice and spend some time learning basic Java. Google "Java can't resolve to a variable" or something like that, and you will probably find some useful information. Or you can start here: http://docs.oracle.com/javase/tutorial/java/index.html
So I did some research and fixed the problem. I can't find the custom slots in it though.
I've changed the slot number a bunch of times yet I'm not getting any successes to know if I even have the right slot
I got it to work when the player is hurt and when another entity is hurt
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumAwesome, that's progress xD Can you post the code with which you are having trouble?
@SubscribeEvent
public void onLivingHurtEnemy(LivingHurtEvent event)
{
if (!(event.entity instanceof EntityPlayer)) {
{
System.out.println("got thus far");
EntityPlayer player = (EntityPlayer) event.entityLiving;
if (ExtendedPlayer.get(player).inventory.getSizeInventory() != 0)
{
System.out.println("your pain is noticed");
}
else
{
}
}
}
}
@SubscribeEvent
public void onLivingHurtPlayer(LivingHurtEvent event)
{
if (event.entity instanceof EntityPlayer) {
{
EntityPlayer player = (EntityPlayer) event.entityLiving;
if (ExtendedPlayer.get(player).inventory.getSizeInventory() != 0)
{
System.out.println("your pain is shared");
}
else
{
}
}
}
}
I would think that it would be a better idea to add the stats here (you mentioned using attributes, but would it be able to stack?). And pretty much just adding events that hurt mobs or take more of your damage if the slot is wearing the right item. And specific dmg/def for different items in the same slot
In the entity living getting hurt it obviously doesn't work since it's not a player entity, but I threw it in there. I'm not sure how calculating attack bonuses would even be added. (resistance (defense) for that sake as well.)
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumThanks, and sorry, but that is well outside the scope of this tutorial. I recommend you create a new topic in the "Modification Development" section of the forums - likely you will get many people helping you there. Good luck!
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumThis is the next section I plan to add to the tutorial, as it seems to come up a lot. Adding a custom inventory slot by itself doesn't automatically work like a normal inventory slot, but it's easy to add. As I explained in some earlier post, you need to do the following:
1. Find an appropriate ticking method or event, such as PlayerTickEvent
2. Within the above method/event, get the player's custom inventory slots from your ExtendedPlayer or similar class
3. Run through each slot and, if there is an itemstack in there, call the appropriate tick methods:
// stack is an ItemStack (or possibly null) retrieved from each of your custom inventory slots // for ALL items: if (stack != null) { // call the onUpdate method with your custom slot index, however you decide to define that, and whether // the item is considered to be 'held' i.e. equipped, probably always true for custom slots stack.getItem().onUpdate(stack, player.worldObj, player, slotIndex, isHeld); // now for ARMOR type items, still within the not null check: if (stack.getItem() instanceof ItemArmor) { // or custom items that you want to behave like armor // NOTE that onArmorTick is actually an Item class method, not only ItemArmor, so you could // technically call it for all items without checking if it's armor at all stack.getItem().onArmorTick(player.worldObj, player, stack); } }That should get some basic ticking functionality for you.
For AttributeModifiers, it's a bit trickier, as those calculations are done somewhere deep in vanilla code that is not readily accessible so far as I know (I didn't look too hard, but please let me know if you find one!). The solution I've come up with is a bit clunky, but it works:
1. Create an interface, such as IEquippable, with a method such as 'public void onEquipChange(EntityPlayer, ItemStack, int customSlotIndex, bool isEquipped)'
2. Each of your Items that can be equipped in a custom slot should implement this method, removing and adding all the modifiers from getItemAttributeModifiers(ItemStack) and any other custom bonuses you have
3. For each of your custom slots, store a 'lastWornItem' in the ExtendedPlayer (all of them should be null initially)
4. During the update tick, check if the lastWornItem is different than what is currently in the slot and, if so, call your custom method first with the last worn item to unequip its bonuses, then with the new itemstack to apply its bonuses.
It's probably not the most efficient solution, but it's not terribly inefficient, either, so long as you don't go crazy with custom slots and add in tons of checks everywhere. You can see such an implementation in my Zelda mod - check out the ItemArmorBoots and ExtendedPlayer classes, but please note that I did NOT create an interface, though I should have and should do, as it is a cleaner and more flexible solution.
For rendering, this first requires that the client side is aware of what is in your custom slots, so whenever the currently equipped item changes or the player first joins the world, you will have to send the ItemStack to the client in a packet (there is a vanilla packet for this that you can use as a template, but not directly because your slots are not in the regular inventory system).
Once the client is aware of all the equipped items in the custom slots (or just whichever ones you want to add rendering for), then it's merely a matter of rendering it, probably RenderPlayerEvent.Post is a good place for that. I will leave the render code up to you, as that is outside the scope of this tutorial.
Hope that helps, and I do plan on adding a more detailed approach to the tutorial at some point in the future.
Regards,
coolAlias
The tutor suggested I should do it before.
Also, tell me if you want to see my version of the code. The only things I changed were some of the names (Obviously) and the INV_SIZE = 1 instead of 8
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumI have no idea what you mean by that...
Eating any good bacteria lately? (in reference to your username...).
Yes, I need to see your code, but before you show it to me, perhaps try going back through the tutorial and make sure you followed every step correctly - you may just find your answer there.