Is there any way to give the mod priority over everything, as what seems to be happening is that bukkit is overriding the changes I made to the name?
Sorry, I've never worked with Bukkit; you can give the NameFormat event a different priority, but that only affects when your event listener fires in relation to other event listeners for that event. If Bukkit is using the NameFormat event (which I am almost 100% sure they are NOT, since Bukkit and Forge are totally separate), then you could add the following to your event annotation:
@ForgeSubscribe(priority=EventPriority.LOWEST)
That will make sure your event listener is the last one to fire, meaning it will take write over anything that came before it unless of course the event is cancelable, then you'd probably want HIGHEST priority instead and cancel the event so no other listeners change it.
Could I use an event to check if a player is wearing an armor piece, then check if they are a certain xp, then move that armor piece into an empty slot in their inventory?
One problem I could see is if they are standing on some items and then try and wear armor, the game would try and put an armor piece back into a slot that is now taken.
Is there some sort of event for when an item in moved around the inventory?....
Sure there are. The way I usually do it when I need a reference to the current slot is to use a for loop with 'i':
for (int i = 0; i < player.inventory.getSizeInventory(); ++i) {
ItemStack invStack = player.inventory.getStackInSlot(i);
if (invStack != null && whatever else you want to check) {
// do something like:
player.inventory.setInventorySlotContents(i, some ItemStack or null);
}
}
If you only need to know if the contents of a slot are something and don't need to set the contents, you can use:
for (ItemStack invStack : player.inventory.mainInventory) {
if (invStack != null && invStack.getItem() instanceof YourItem) { // or itemID or whatever you want to check
// do something with that item
}
}
I find the latter method a bit wonky when trying to remove or swap an item, so I usually go with the first.
I am having problems adding a second value to be saved.
I made a new "ExtendedPlayer2" exactly like the first one, and everything works ingame fine, but there are problems on both death and when logging out.
The problem seems to be in the last lines here, whichever .sync comes last sets both saved values to the value of that saved one, ie in this case both the combat xp and the agility xp both get set to the value of the players agility xp
Not sure what is going wrong, here is the code used in the event handler.
It is entirely unnecessary to create a second ExtendedProperties for the same class of Entity; both are for the player, so just add the agilityXp field to your original properties and everything will be fine. That's not to say that you can't make multiple properties for the same type of Entity, just that in your case it doesn't make sense to do so.
As for your problem, I'm guessing it is because you don't distinguish between your packets in any way. Both of them simply write a single integer to the stream and send it to the player, but how is the player to know which value should be handled? An easy way to deal with this is to add a byte identifier to the head of each packet, then parse through them based on that byte:
// packet ID tags:
public static final byte COMBAT = 0, AGILITY = 1;
// when you write your packets, first write the byte tag that identifies it:
outputStream.writeByte(COMBAT);
// then in your PacketHandler, first read in the ID (byte), and handle from there:
byte id = inputStream.readByte();
switch(id) {
case COMBAT: handleCombatPacket(params); break;
case AGILILTY: handleAgilityPacket(params); break;
}
Of course you could also make your own CustomPacket classes that would handle themselves internally.
Anyway, just put your agilityXp in to your original ExtendedProperties, and add a second integer to your packet.
I still don't understand I think, I put everything back into one extendedplayer (I put it into 2 after having the problem hoping that might fix it), when I write the packets should I be sending them as bytes or integers now?
This looks like a complete mess and is almost certainly wrong
public final void sync()
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(8);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
outputStream.writeByte(COMBAT);
outputStream.writeByte(AGILITY);
outputStream.writeInt(this.combatxp);
outputStream.writeInt(this.agilityxp);
}
catch (Exception ex) {ex.printStackTrace();}
Packet250CustomPayload packet = new Packet250CustomPayload("tutchannel", bos.toByteArray());
if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
EntityPlayerMP player1 = (EntityPlayerMP) player;
PacketDispatcher.sendPacketToPlayer(packet, (Player)player1);
}
}
Hmm, ok. A packet is simply a bundle of information, be it bytes, integers, whatever. You must tell it explicitly what data it is sending and what that data is, and it has to be read in the exact same order that it was written. Also, you should tell it the total size of your packet, which in your case is no longer 8.
If you only have one single packet being sent in the entirety of your mod, then you don't need to worry about identifying the packet; otherwise, that is what the byte is for at the beginning. But you only need one id per packet; you put two in there.
You should really read the Packet Handling tutorial another time or two so you understand the basics of what is going on here.
And I don't understand what is going on in the packet handler, what is handleCombatPacket(params) doing?
No. COMBAT would be a constant id for your packet, which is why I wrote it as 'static final'. If you are unsure what I mean by 'constant', I suggest you read this. The point is that it will never change once you assign it a value, since it is a byte, and you can henceforth use it to identify your packet.
'handleCombatPacket(params)' is just pseudo-code that means: I would create a method to handle my combat packet, taking various parameters such as the player and the rest of the packet to read in, so that my main onPacketData method in the PacketHandler doesn't become a mess. Something like the following:
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) {
try {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
byte packetType;
try {
// read in the first byte; this is your packet id that you wrote at the very beginning, e.g. COMBAT
packetType = inputStream.readByte();
// now you can parse through your various packets, performing different actions for each
switch (packetType) {
case COMBAT: handleCombatPacket(packet, (EntityPlayer) player, inputStream); break;
default: System.out.println("Unhandled packet exception for packet id " + packetType);
}
} finally {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Then in your handleCombatPacket, you would finish reading in the rest of the data (combatXp, agilityXp, whatever else) and processing that data (assigning the client-side player property values to those read in from the packet).
This isn't the most elegant setup, but it works just fine. Once you get the hang of packets, you can start creating your own Packet classes that read and process themselves so you don't jumble up the rest of your code with all this packet stuff when it could be handled like this instead:
That's all the code I need to sync my extended properties, and the rest is handled within the packet. The packet handler merely acts as a parser to read in the packet id's and call the appropriate packet's processing method. But that's going beyond a simple explanation, so I'll stop there.
ok I now have this after reading the tutorial, I get no errors but when running it, nothing at all is now saved.
package net.minecraft.src;
public class PackageHandler implements IPacketHandler
{
public static final byte COMBAT = 0, AGILITY = 1;
// Don't need to do anything here.
public PackageHandler() {}
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) {
try {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
byte packetType;
try {
packetType = inputStream.readByte();
switch (packetType) {
case COMBAT: handleCombatPacket(packet, (EntityPlayer) player, inputStream); break;
case AGILITY: handleAgilityPacket(packet, (EntityPlayer) player, inputStream); break;
default: System.out.println("Unhandled packet exception for packet id " + packetType);
}
} finally {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleAgilityPacket(Packet250CustomPayload packet,
EntityPlayer player, DataInputStream inputStream) {
int agilityInt;
try {
agilityInt = inputStream.readInt();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
private void handleCombatPacket(Packet250CustomPayload packet,
EntityPlayer player, DataInputStream inputStream) {
int combatInt;
try {
combatInt = inputStream.readInt();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
Is it my ExtendedPlayer that is the problem, I changed the output stream size to 16 but didn't change anything else from that last code.
... then you need to read the tutorial again. If you are putting all the information into ONE packet, you do NOT need two ids. Just one. Only a single 'SYNC_DATA' packet for your extended properties.
When you 'handle' a packet, that doesn't only mean reading in the data. If you only read the data but don't do anything with it, what do you expect to happen? Exactly: nothing. Once you read the data in, then you need to set the values for your extended properties accordingly:
private void handleSyncPropertiesPacket(Packet250CustomPayload packet,
EntityPlayer player, DataInputStream inputStream) {
int combatXp;
int agilityXp;
// first get the data:
try {
combatXp = inputStream.readInt();
agilityXp= inputStream.readInt();
} catch (IOException e) {
e.printStackTrace();
return;
}
// second get your extended properties and set the values:
ExtendedPlayer info = ExtendedPlayer.get(player);
info.setCombatXp(combatXp);
info.setAgilityXp(agilityXp);
}
Now your data should be synchronized.
If your data is not saving between logins, that means you are not reading or writing the data correctly to NBT, and is a separate issue. But it sounds like your data IS saved, just that the client has the incorrect value (because of your packet issue, above).
Messages showing twice means the method is run on both the client and the server side, so the message is printed once on each side. If you want to know which value belongs to which side, put in something like this:
System.out.println("Combat xp: " + props.getCombatXp() + "; side is client? " + player.worldObj.isRemote);
That will show you exactly what your xp is on each side of the world, so you can see if there is any discrepancy and if so on which side it is occurring.
Made those changes and still getting the same problem. As an aside what is actually happening when the event handler is able to get the right value, but items are not? They are both using the same method.
Because the data is saved in NBT when you quit, and loaded from NBT when you log back in, but only the server will have the correct NBT value on login, because the client doesn't save NBT data. So when you play the first time, the method is run on both sides and the xp seems to be working fine (which it is), then when you log back in, the server still knows the old value, but the client starts at zero unless you tell it what the value loaded from NBT is.
How can I use a single data packet here, if I only use one of the above then only one value is going through? How is sending the meaningless byte "COMBAT" through helpful, to use it at all I have to declare it in packethandler as well.
Thanks.
I'm sorry that you have not understood at all what I've said in the last few posts. Let me try again. COMBAT and AGILITY have nothing to do with the data that is sent. If you only have one single packet in your entire mod, then yes, they would be absolutely meaningless. If, however, you have lots of packets, then how are you distinguishing between them when they are received by the server?
Ok, so you just have one packet. Forget about that byte. Just send two integer values to your packet handler, and read them in directly from onPacketData. Don't even bother making a method to handle it.
And PLEASE read the packet handler tutorial again, carefully, including all the sidebar bits, because clearly you haven't done so. One byte is size 1, an integer is size 4; you have two integers, so 4+4=8 for your packet size. There is lots of important information in that tutorial that I'm sure would help you understand what I've been trying to explain.
I apologize for confusing you with talk about packet ids and the like, but you're going to need to wrap your head around that at some point when you have more than a single packet in your mod.
Updated the EventHandler tutorial to 1.7.2; it mainly just involves changing @ForgeSubscribe to @SubscribeEvent, and everything else works pretty much exactly the same as before. I'll post more about updating IExtendedEntityProperties another time, but you can check my custom inventory tutorial for how to do so if you would like to know now.
Cool - I'll have to check that out at some point! Just updated the main post with more information on the different EVENT_BUSes that can be used, as well as with more information about 1.7.2.
Hi! I did everything according to the tutorial and I everything works fine! But when I die, all variables are reset. Could you tell me how to make variables persist after death?
Hi! I did everything according to the tutorial and I everything works fine! But when I die, all variables are reset. Could you tell me how to make variables persist after death?
It's in the tutorial as well - there is a section specifically labeled "Step 5: Getting your custom data to persist through player death".
Prompt in what file to write the code from lesson?
// save player data:
proxy.storeEntityData(((EntityPlayer) event.entity).username + ExtendedPlayer.EXT_PROP_NAME, playerData);
// save living data:
proxy.storeEntityData(((EntityPlayer) event.entity).username + ExtendedLiving.EXT_PROP_NAME, playerData);
// load player data:
NBTTagCompound playerData = proxy.getEntityData(((EntityPlayer) event.entity).username + ExtendedPlayer.EXT_PROP_NAME);
// load living data
NBTTagCompound playerData = proxy.getEntityData(((EntityPlayer) event.entity).username + ExtendedLiving.EXT_PROP_NAME);
The saving is done in the LivingDeathEvent, and the loading is done in the EntityJoinWorldEvent, as you can see in the code just above that in the spoilers.
[SOLVED]
So, I'm using the PlayerInteractEvent in a handler class and checking for the "Right-click" and the correct heldItem(), but I can't find a way to verify the block that's being interacted with. I know the event.x, y and z are the coordinates, but what is the server-friendly way of getting the block? Currently I'm using Minecraft.getMinecraft().theWorld; and using the .getBlock(event.x, event.y, event.z) to determine the block but a post I found said that getMinecraft() gets the client and not the server. Since that post was pretty old, I'm sure there's a way to get around this.
EDIT: Nevermind!I used
Block block = event.entityPlayer.worldObj.getBlock(x, y, z);
I Must have skipped over it while reading all the event variables.
[SOLVED]
So, I'm using the PlayerInteractEvent in a handler class and checking for the "Right-click" and the correct heldItem(), but I can't find a way to verify the block that's being interacted with. I know the event.x, y and z are the coordinates, but what is the server-friendly way of getting the block? Currently I'm using Minecraft.getMinecraft().theWorld; and using the .getBlock(event.x, event.y, event.z) to determine the block but a post I found said that getMinecraft() gets the client and not the server. Since that post was pretty old, I'm sure there's a way to get around this.
EDIT: Nevermind!I used
Block block = event.entityPlayer.worldObj.getBlock(x, y, z);
I Must have skipped over it while reading all the event variables.
Glad you solved it. Yeah, NEVER use Minecraft.getMinecraft() when you have a World or any type of Entity object in the parameters / available fields, even when you're on the client side. The ONLY time you want to use Minecraft.getMinecraft() for anything is in client-side only classes or methods, such as Render, Gui, etc., otherwise your code will most likely fail on some level.
Glad you solved it. Yeah, NEVER use Minecraft.getMinecraft() when you have a World or any type of Entity object in the parameters / available fields, even when you're on the client side. The ONLY time you want to use Minecraft.getMinecraft() for anything is in client-side only classes or methods, such as Render, Gui, etc., otherwise your code will most likely fail on some level.
Agreed. I was trying to find the way not to use that, and after I tabbed back to eclipse I looked again and found worldObj. Lol Oh my tired eyes.
Hello again! Here, look. I did everything as written in the tutorial, but I do now when killing creatures is not given "my new level" (although it should be given). What's wrong with this code wrong?
@Mod(modid = "TestID", name = "Test", version = "Version Test 1.0")
@NetworkMod(clientSideRequired = true, serverSideRequired = true, channels = { ExtendedPlayer.EXT_PROP_NAME }, packetHandler = TutorialPacketHandler.class)
public class Main {
private static Main instance;
@SidedProxy(clientSide = "example.ClientProxy", serverSide = "example.CommonProxy")
public static CommonProxy proxy;
public Main() {
instance = this;
}
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
}
@EventHandler
public void onInit(FMLInitializationEvent event) {
MinecraftForge.EVENT_BUS.register(new TutEventHandler());
}
public static Main getInstance() {
return instance;
}
}
public class CommonProxy implements IGuiHandler {
/**
* Used to store IExtendedEntityProperties data temporarily between player
* death and respawn
*/
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerRenderers() {
}
@Override
public Object getServerGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z) {
return null;
}
@Override
public Object getClientGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z) {
return null;
}
/**
* Adds an entity's custom data to the map for temporary storage
*
* @param compound
* An NBT Tag Compound that stores the IExtendedEntityProperties
* data only
*/
public static void storeEntityData(String name, NBTTagCompound compound) {
extendedEntityData.put(name, compound);
}
/**
* Removes the compound from the map and returns the NBT tag stored for name
* or null if none exists
*/
public static NBTTagCompound getEntityData(String name) {
return extendedEntityData.remove(name);
}
/**
* Makes it look nicer in the methods save/loadProxyData
*/
private static String getSaveKey(EntityPlayer player) {
return player.username + ":" + ExtendedPlayer.EXT_PROP_NAME;
}
/**
* Does everything I did in onLivingDeathEvent and it's static, so you now
* only need to use the following in the above event:
* ExtendedPlayer.saveProxyData((EntityPlayer) event.entity));
*/
public static void saveProxyData(EntityPlayer player) {
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = new NBTTagCompound();
playerData.saveNBTData(savedData);
CommonProxy.storeEntityData(getSaveKey(player), savedData);
}
/**
* This cleans up the onEntityJoinWorld event by replacing most of the code
* with a single line: ExtendedPlayer.loadProxyData((EntityPlayer)
* event.entity));
*/
public static void loadProxyData(EntityPlayer player) {
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = CommonProxy.getEntityData(getSaveKey(player));
if (savedData != null) {
playerData.loadNBTData(savedData);
}
public class ExtendedPlayer implements IExtendedEntityProperties {
public final static String EXT_PROP_NAME = "Level";
private final EntityPlayer player;
private int level;
public static final int levelID = 20;
public ExtendedPlayer(EntityPlayer player) {
this.player = player;
this.player.getDataWatcher().addObject(levelID, this.level);
}
/**
* Used to register these extended properties for the player during
* EntityConstructing event This method is for convenience only; it will
* make your code look nicer
*/
public static final void register(EntityPlayer player) {
player.registerExtendedProperties(ExtendedPlayer.EXT_PROP_NAME, new ExtendedPlayer(player));
}
/**
* Returns ExtendedPlayer properties for player This method is for
* convenience only; it will make your code look nicer
*/
public static final ExtendedPlayer get(EntityPlayer player) {
return (ExtendedPlayer) player.getExtendedProperties(EXT_PROP_NAME);
}
@Override
public void saveNBTData(NBTTagCompound compound) {
NBTTagCompound properties = new NBTTagCompound();
properties.setInteger("newLevel", this.player.getDataWatcher().getWatchableObjectInt(levelID));
this.player.getDataWatcher().updateObject(levelID, properties.getInteger("newLevel"));
compound.setTag(EXT_PROP_NAME, properties);
}
@Override
public void init(Entity entity, World world) {
}
/**
* Sends a packet to the client containing information stored on the server
* for ExtendedPlayer
*/
public final void sync() {
ByteArrayOutputStream bos = new ByteArrayOutputStream(4);
DataOutputStream outputStream = new DataOutputStream(bos);
public class TutEventHandler {
@ForgeSubscribe
public void onEntityConstructing(EntityConstructing event) {
if (event.entity instanceof EntityPlayer) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.entity);
if (exp == null) {
ExtendedPlayer.register((EntityPlayer) event.entity);
}
}
}
@ForgeSubscribe
public void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayerMP) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.entity);
Hello again! Here, look. I did everything as written in the tutorial, but I do now when killing creatures is not given "my new level" (although it should be given). What's wrong with this code wrong?
Move this line from saveNBTData to loadNBTData:
this.player.getDataWatcher().updateObject(levelID, properties.getInteger("newLevel"));
// and remove this line, as it's not needed anymore:
this.level = properties.getInteger("newLevel");
Also, since you are using DataWatcher to track your level, you don't need to send a packet or worry about syncing anything at this point.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumSorry, I've never worked with Bukkit; you can give the NameFormat event a different priority, but that only affects when your event listener fires in relation to other event listeners for that event. If Bukkit is using the NameFormat event (which I am almost 100% sure they are NOT, since Bukkit and Forge are totally separate), then you could add the following to your event annotation:
That will make sure your event listener is the last one to fire, meaning it will take write over anything that came before it unless of course the event is cancelable, then you'd probably want HIGHEST priority instead and cancel the event so no other listeners change it.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumSure there are. The way I usually do it when I need a reference to the current slot is to use a for loop with 'i':
for (int i = 0; i < player.inventory.getSizeInventory(); ++i) { ItemStack invStack = player.inventory.getStackInSlot(i); if (invStack != null && whatever else you want to check) { // do something like: player.inventory.setInventorySlotContents(i, some ItemStack or null); } }If you only need to know if the contents of a slot are something and don't need to set the contents, you can use:
for (ItemStack invStack : player.inventory.mainInventory) { if (invStack != null && invStack.getItem() instanceof YourItem) { // or itemID or whatever you want to check // do something with that item } }I find the latter method a bit wonky when trying to remove or swap an item, so I usually go with the first.
I have no donations, but thank you so much for your time!
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumNo donation is necessary, but thanks, and you're welcome xD
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumIt is entirely unnecessary to create a second ExtendedProperties for the same class of Entity; both are for the player, so just add the agilityXp field to your original properties and everything will be fine. That's not to say that you can't make multiple properties for the same type of Entity, just that in your case it doesn't make sense to do so.
As for your problem, I'm guessing it is because you don't distinguish between your packets in any way. Both of them simply write a single integer to the stream and send it to the player, but how is the player to know which value should be handled? An easy way to deal with this is to add a byte identifier to the head of each packet, then parse through them based on that byte:
// packet ID tags: public static final byte COMBAT = 0, AGILITY = 1; // when you write your packets, first write the byte tag that identifies it: outputStream.writeByte(COMBAT); // then in your PacketHandler, first read in the ID (byte), and handle from there: byte id = inputStream.readByte(); switch(id) { case COMBAT: handleCombatPacket(params); break; case AGILILTY: handleAgilityPacket(params); break; }Of course you could also make your own CustomPacket classes that would handle themselves internally.
Anyway, just put your agilityXp in to your original ExtendedProperties, and add a second integer to your packet.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumHmm, ok. A packet is simply a bundle of information, be it bytes, integers, whatever. You must tell it explicitly what data it is sending and what that data is, and it has to be read in the exact same order that it was written. Also, you should tell it the total size of your packet, which in your case is no longer 8.
If you only have one single packet being sent in the entirety of your mod, then you don't need to worry about identifying the packet; otherwise, that is what the byte is for at the beginning. But you only need one id per packet; you put two in there.
You should really read the Packet Handling tutorial another time or two so you understand the basics of what is going on here.
No. COMBAT would be a constant id for your packet, which is why I wrote it as 'static final'. If you are unsure what I mean by 'constant', I suggest you read this. The point is that it will never change once you assign it a value, since it is a byte, and you can henceforth use it to identify your packet.
'handleCombatPacket(params)' is just pseudo-code that means: I would create a method to handle my combat packet, taking various parameters such as the player and the rest of the packet to read in, so that my main onPacketData method in the PacketHandler doesn't become a mess. Something like the following:
@Override public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) { try { DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data)); byte packetType; try { // read in the first byte; this is your packet id that you wrote at the very beginning, e.g. COMBAT packetType = inputStream.readByte(); // now you can parse through your various packets, performing different actions for each switch (packetType) { case COMBAT: handleCombatPacket(packet, (EntityPlayer) player, inputStream); break; default: System.out.println("Unhandled packet exception for packet id " + packetType); } } finally { inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } }Then in your handleCombatPacket, you would finish reading in the rest of the data (combatXp, agilityXp, whatever else) and processing that data (assigning the client-side player property values to those read in from the packet).
This isn't the most elegant setup, but it works just fine. Once you get the hang of packets, you can start creating your own Packet classes that read and process themselves so you don't jumble up the rest of your code with all this packet stuff when it could be handled like this instead:
That's all the code I need to sync my extended properties, and the rest is handled within the packet. The packet handler merely acts as a parser to read in the packet id's and call the appropriate packet's processing method. But that's going beyond a simple explanation, so I'll stop there.
-
View User Profile
-
View Posts
-
Send Message
Curse Premium... then you need to read the tutorial again. If you are putting all the information into ONE packet, you do NOT need two ids. Just one. Only a single 'SYNC_DATA' packet for your extended properties.
When you 'handle' a packet, that doesn't only mean reading in the data. If you only read the data but don't do anything with it, what do you expect to happen? Exactly: nothing. Once you read the data in, then you need to set the values for your extended properties accordingly:
private void handleSyncPropertiesPacket(Packet250CustomPayload packet, EntityPlayer player, DataInputStream inputStream) { int combatXp; int agilityXp; // first get the data: try { combatXp = inputStream.readInt(); agilityXp= inputStream.readInt(); } catch (IOException e) { e.printStackTrace(); return; } // second get your extended properties and set the values: ExtendedPlayer info = ExtendedPlayer.get(player); info.setCombatXp(combatXp); info.setAgilityXp(agilityXp); }Now your data should be synchronized.
If your data is not saving between logins, that means you are not reading or writing the data correctly to NBT, and is a separate issue. But it sounds like your data IS saved, just that the client has the incorrect value (because of your packet issue, above).
Messages showing twice means the method is run on both the client and the server side, so the message is printed once on each side. If you want to know which value belongs to which side, put in something like this:
System.out.println("Combat xp: " + props.getCombatXp() + "; side is client? " + player.worldObj.isRemote);That will show you exactly what your xp is on each side of the world, so you can see if there is any discrepancy and if so on which side it is occurring.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumBecause the data is saved in NBT when you quit, and loaded from NBT when you log back in, but only the server will have the correct NBT value on login, because the client doesn't save NBT data. So when you play the first time, the method is run on both sides and the xp seems to be working fine (which it is), then when you log back in, the server still knows the old value, but the client starts at zero unless you tell it what the value loaded from NBT is.
I'm sorry that you have not understood at all what I've said in the last few posts. Let me try again. COMBAT and AGILITY have nothing to do with the data that is sent. If you only have one single packet in your entire mod, then yes, they would be absolutely meaningless. If, however, you have lots of packets, then how are you distinguishing between them when they are received by the server?
Ok, so you just have one packet. Forget about that byte. Just send two integer values to your packet handler, and read them in directly from onPacketData. Don't even bother making a method to handle it.
And PLEASE read the packet handler tutorial again, carefully, including all the sidebar bits, because clearly you haven't done so. One byte is size 1, an integer is size 4; you have two integers, so 4+4=8 for your packet size. There is lots of important information in that tutorial that I'm sure would help you understand what I've been trying to explain.
I apologize for confusing you with talk about packet ids and the like, but you're going to need to wrap your head around that at some point when you have more than a single packet in your mod.
-
View User Profile
-
View Posts
-
Send Message
Curse Premium-
View User Profile
-
View Posts
-
Send Message
Curse PremiumCool - I'll have to check that out at some point! Just updated the main post with more information on the different EVENT_BUSes that can be used, as well as with more information about 1.7.2.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumIt's in the tutorial as well - there is a section specifically labeled "Step 5: Getting your custom data to persist through player death".
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumThe saving is done in the LivingDeathEvent, and the loading is done in the EntityJoinWorldEvent, as you can see in the code just above that in the spoilers.
So, I'm using the PlayerInteractEvent in a handler class and checking for the "Right-click" and the correct heldItem(), but I can't find a way to verify the block that's being interacted with. I know the event.x, y and z are the coordinates, but what is the server-friendly way of getting the block? Currently I'm using Minecraft.getMinecraft().theWorld; and using the .getBlock(event.x, event.y, event.z) to determine the block but a post I found said that getMinecraft() gets the client and not the server. Since that post was pretty old, I'm sure there's a way to get around this.
EDIT: Nevermind!I used
I Must have skipped over it while reading all the event variables.
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumGlad you solved it. Yeah, NEVER use Minecraft.getMinecraft() when you have a World or any type of Entity object in the parameters / available fields, even when you're on the client side. The ONLY time you want to use Minecraft.getMinecraft() for anything is in client-side only classes or methods, such as Render, Gui, etc., otherwise your code will most likely fail on some level.
Agreed. I was trying to find the way not to use that, and after I tabbed back to eclipse I looked again and found worldObj. Lol Oh my tired eyes.
import net.minecraft.client.Minecraft;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
@Mod(modid = "TestID", name = "Test", version = "Version Test 1.0")
@NetworkMod(clientSideRequired = true, serverSideRequired = true, channels = { ExtendedPlayer.EXT_PROP_NAME }, packetHandler = TutorialPacketHandler.class)
public class Main {
private static Main instance;
@SidedProxy(clientSide = "example.ClientProxy", serverSide = "example.CommonProxy")
public static CommonProxy proxy;
public Main() {
instance = this;
}
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
}
@EventHandler
public void onInit(FMLInitializationEvent event) {
MinecraftForge.EVENT_BUS.register(new TutEventHandler());
}
public static Main getInstance() {
return instance;
}
}
public class ClientProxy extends CommonProxy {
}
import java.util.HashMap;
import java.util.Map;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import cpw.mods.fml.common.network.IGuiHandler;
public class CommonProxy implements IGuiHandler {
/**
* Used to store IExtendedEntityProperties data temporarily between player
* death and respawn
*/
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerRenderers() {
}
@Override
public Object getServerGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z) {
return null;
}
@Override
public Object getClientGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z) {
return null;
}
/**
* Adds an entity's custom data to the map for temporary storage
*
* @param compound
* An NBT Tag Compound that stores the IExtendedEntityProperties
* data only
*/
public static void storeEntityData(String name, NBTTagCompound compound) {
extendedEntityData.put(name, compound);
}
/**
* Removes the compound from the map and returns the NBT tag stored for name
* or null if none exists
*/
public static NBTTagCompound getEntityData(String name) {
return extendedEntityData.remove(name);
}
/**
* Makes it look nicer in the methods save/loadProxyData
*/
private static String getSaveKey(EntityPlayer player) {
return player.username + ":" + ExtendedPlayer.EXT_PROP_NAME;
}
/**
* Does everything I did in onLivingDeathEvent and it's static, so you now
* only need to use the following in the above event:
* ExtendedPlayer.saveProxyData((EntityPlayer) event.entity));
*/
public static void saveProxyData(EntityPlayer player) {
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = new NBTTagCompound();
playerData.saveNBTData(savedData);
CommonProxy.storeEntityData(getSaveKey(player), savedData);
}
/**
* This cleans up the onEntityJoinWorld event by replacing most of the code
* with a single line: ExtendedPlayer.loadProxyData((EntityPlayer)
* event.entity));
*/
public static void loadProxyData(EntityPlayer player) {
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = CommonProxy.getEntityData(getSaveKey(player));
if (savedData != null) {
playerData.loadNBTData(savedData);
}
playerData.sync();
}
}
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.packet.Packet250CustomPayload;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;
import cpw.mods.fml.common.network.PacketDispatcher;
import cpw.mods.fml.common.network.Player;
public class ExtendedPlayer implements IExtendedEntityProperties {
public final static String EXT_PROP_NAME = "Level";
private final EntityPlayer player;
private int level;
public static final int levelID = 20;
public ExtendedPlayer(EntityPlayer player) {
this.player = player;
this.player.getDataWatcher().addObject(levelID, this.level);
}
/**
* Used to register these extended properties for the player during
* EntityConstructing event This method is for convenience only; it will
* make your code look nicer
*/
public static final void register(EntityPlayer player) {
player.registerExtendedProperties(ExtendedPlayer.EXT_PROP_NAME, new ExtendedPlayer(player));
}
/**
* Returns ExtendedPlayer properties for player This method is for
* convenience only; it will make your code look nicer
*/
public static final ExtendedPlayer get(EntityPlayer player) {
return (ExtendedPlayer) player.getExtendedProperties(EXT_PROP_NAME);
}
@Override
public void saveNBTData(NBTTagCompound compound) {
NBTTagCompound properties = new NBTTagCompound();
properties.setInteger("newLevel", this.player.getDataWatcher().getWatchableObjectInt(levelID));
this.player.getDataWatcher().updateObject(levelID, properties.getInteger("newLevel"));
compound.setTag(EXT_PROP_NAME, properties);
}
@Override
public void loadNBTData(NBTTagCompound compound) {
NBTTagCompound properties = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);
this.level = properties.getInteger("newLevel");
System.out.println("[NBT] Level from NBT: " + this.level);
}
@Override
public void init(Entity entity, World world) {
}
/**
* Sends a packet to the client containing information stored on the server
* for ExtendedPlayer
*/
public final void sync() {
ByteArrayOutputStream bos = new ByteArrayOutputStream(4);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
outputStream.writeInt(this.level);
} catch (Exception ex) {
ex.printStackTrace();
}
Packet250CustomPayload packet = new Packet250CustomPayload(EXT_PROP_NAME, bos.toByteArray());
if (!player.worldObj.isRemote) {
EntityPlayerMP player1 = (EntityPlayerMP) player;
PacketDispatcher.sendPacketToPlayer(packet, (Player) player1);
}
}
public void setLevel(int par1) {
this.level = par1;
this.sync();
}
public final int getLevel() {
return this.player.getDataWatcher().getWatchableObjectInt(levelID);
}
public static void saveProxyData(EntityPlayer entity) {
Main.proxy.saveProxyData(entity);
}
}
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.event.ForgeSubscribe;
import net.minecraftforge.event.entity.EntityEvent.EntityConstructing;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
public class TutEventHandler {
@ForgeSubscribe
public void onEntityConstructing(EntityConstructing event) {
if (event.entity instanceof EntityPlayer) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.entity);
if (exp == null) {
ExtendedPlayer.register((EntityPlayer) event.entity);
}
}
}
@ForgeSubscribe
public void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayerMP) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.entity);
NBTTagCompound playerData = Main.proxy.getEntityData(((EntityPlayer) event.entity).username);
if (playerData != null) {
exp.loadNBTData(playerData);
}
exp.sync();
}
}
@ForgeSubscribe
public void onLivingDeathEvent(LivingDeathEvent event) {
if (!event.entity.worldObj.isRemote) {
if (event.entity instanceof EntityPlayerMP) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.entity);
NBTTagCompound playerData = new NBTTagCompound();
exp.saveNBTData(playerData);
Main.proxy.storeEntityData(((EntityPlayer) event.entity).username, playerData);
ExtendedPlayer.saveProxyData((EntityPlayer) event.entity);
}
if (event.source.getEntity() instanceof EntityPlayerMP) {
ExtendedPlayer exp = ExtendedPlayer.get((EntityPlayer) event.source.getEntity());
exp.setLevel(exp.getLevel() + 1);
exp.sync();
}
}
}
}
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet250CustomPayload;
import cpw.mods.fml.common.network.IPacketHandler;
import cpw.mods.fml.common.network.Player;
public class TutorialPacketHandler implements IPacketHandler {
public TutorialPacketHandler() {
}
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) {
if (packet.channel.equals(ExtendedPlayer.EXT_PROP_NAME)) {
handleExtendedProperties(packet, player);
}
}
private void handleExtendedProperties(Packet250CustomPayload packet, Player player) {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
ExtendedPlayer props = ExtendedPlayer.get((EntityPlayer) player);
try {
props.setLevel(inputStream.readInt());
} catch (IOException e) {
e.printStackTrace();
return;
}
System.out.println("[PACKET] Level from packet: " + props.getLevel());
}
}
-
View User Profile
-
View Posts
-
Send Message
Curse PremiumMove this line from saveNBTData to loadNBTData:
this.player.getDataWatcher().updateObject(levelID, properties.getInteger("newLevel")); // and remove this line, as it's not needed anymore: this.level = properties.getInteger("newLevel");Also, since you are using DataWatcher to track your level, you don't need to send a packet or worry about syncing anything at this point.