Jump to content

[1.7.10] NBT persistent saving issue - adding additional player data


Thornack

Recommended Posts

Hi everyone,

 

So I have a class that implements IExtendedEntityProperties which I use to give the player a party of entities. I want the party members to be selectable at a later time so that the player can select whichever party member he chooses. Currently my entities get saved to the party slot but when the player dies or logs off and then logs back on the party member data doesnt persist. Im not sure what the problem is. This is my class. If you have any questions let me know, and any help is appreciated.

 

My PlayerParty class

 

 

public class PlayerParty implements IExtendedEntityProperties {

protected EntityPlayer entityPlayer;
    
public static final int NUM_SLOTS = 6;
private static final String EXTENDED_ENTITY_PROPERTIES_TAGNAME = "PlayerParty";

protected NBTTagCompound[] partyNbt = new NBTTagCompound[NUM_SLOTS];

public PlayerParty(EntityPlayer player) {
	this.entityPlayer = player;
}

//sets the party member in the specified slot. Stores only the NBT data, which is enough to recreate the party member later.
public void setPartyMember(int slot, EntityPartyMember partyMember){
	NBTTagCompound compound = new NBTTagCompound();
	partyMember.writeToNBT(compound);
	partyNbt[slot] = compound; 
	System.out.println("partyMember added to party " + partyNbt[slot]);
}

//returns true if the player has a free party slot
public boolean hasFreeSlot(){
	return (this.getNextFreeSlot() >= 0);
}

//gets the first free slot in the player's party. The NBT tag compound that slot position should
//be null if the slot is free.
public int getNextFreeSlot(){
	for(int i = 0; i < NUM_SLOTS; i++){
		if(this.partyNbt[i] == null)
			return i;
	}
	return -1;
}

public NBTTagCompound getPartyMember(int slot){
	return partyNbt[slot];
}


/**
 * Used to register these extended properties for the player during EntityConstructing event
 */
public static final void register(EntityPlayer player) {
	player.registerExtendedProperties(EXTENDED_ENTITY_PROPERTIES_TAGNAME, new PlayerParty(player));
}

/**
 * Copies additional player data from the given BattleMobExtendedPlayerHelper instance
 * Avoids NBT disk I/O overhead when cloning a player after respawn
 */
public void copy(PlayerParty properties) {
	}

/**
 * Returns BattleMobExtendedPlayerHelper properties for player
 */
public static final PlayerParty get(EntityPlayer player) {
	return (PlayerParty) player.getExtendedProperties(EXTENDED_ENTITY_PROPERTIES_TAGNAME);
}



@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound properties = new NBTTagCompound();
	for(int i=0; i < NUM_SLOTS; i++){
		if(partyNbt[i] != null){
			compound.setTag("Slot"+i, partyNbt[i]);
		}
	}
	compound.setTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME, properties);
}

@Override
public void loadNBTData(NBTTagCompound compound) {

	NBTTagCompound properties = compound.getCompoundTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME);
	if(properties != null){
		for(int i=0; i < NUM_SLOTS; i++){
			if(properties.hasKey("Slot"+i)){
				partyNbt[i] = properties.getCompoundTag("Slot"+i);
			}
		}
	}
}

@Override
public void init(Entity entity, World world) {

}

}

 

 

I use this method elsewhere to get the party members (it works)

 

 

public void tryToAddMobToParty(){
	PlayerParty pp = PlayerParty.get(Minecraft.getMinecraft().thePlayer);
	System.out.println("free slot? " + pp.hasFreeSlot());
	if(pp.hasFreeSlot() == true){
	pp.setPartyMember(pp.getNextFreeSlot(), this.hitEntity);	
	System.out.println(this.hitEntity + " saved to slot " + pp.getNextFreeSlot());
	}else{
		this.displayChatMessage(EnumChatFormatting.RED + "Your Party Is Currently Full!");
		System.out.println("next free slot " + pp.getNextFreeSlot());//should return -1 at this point because party is full [works]
		pp.getPartyMember(0);
		System.out.println("first in party " + pp.getPartyMember(0));
		pp.getPartyMember(1);
		System.out.println("second in party " + pp.getPartyMember(1));
		pp.getPartyMember(2);
		System.out.println("thirdt in party " + pp.getPartyMember(2));
		pp.getPartyMember(3);
		System.out.println("fourth in party " + pp.getPartyMember(3));
		pp.getPartyMember(4);
		System.out.println("fifth in party " + pp.getPartyMember(4));
		pp.getPartyMember(5);
		System.out.println("sixth in party " + pp.getPartyMember(5));
	}
}

 

 

 

Link to comment
Share on other sites

Oh god, please!

I see this every other time someone uses IEEP. At runtime, NBT does not matter. Represent your data properly, if you have an int, make an int field, not a field holding an NBTTagInt.

Yes, eventually you will save to NBT. But that does not mean you have to stuff your data into NBT all the time. NBT is only for saving to disk. Only only only. Nothing else.

 

To which are you referring to im sorry I dont quite understand your comment

Link to comment
Share on other sites

I guess my problem is that I need an array of "slots" that can store entity data, where each slot stores all of the information about the entity that was assigned to the slot.

That's kind of diesieben's point - each entity already has all of that data, why make it again when you can just make a reference to that entity?

 

That last part is the main point: don't store an NBT tag when you can store an actual value, e.g. the entity reference, integer, or whatever else.

 

Anyway, see here for further information on persisting IEEP data through death and dismemberment.

Link to comment
Share on other sites

Does anyone know of a way to save a stable reference to the entity so that it is easy to get the entity at a later date whenever i want? i tried getEntityById but the id changes since it is not a UUID. Im not sure how to get the entity from the UUID, and am not aware of any other good references to entities.

Link to comment
Share on other sites

Does anyone know of a way to save a stable reference to the entity so that it is easy to get the entity at a later date whenever i want? i tried getEntityById but the id changes since it is not a UUID. Im not sure how to get the entity from the UUID, and am not aware of any other good references to entities.

For players, uuid is always and everywhere the same, get uuid and get player:

UUID uuid = EntityPlayer.func_146094_a(player.getGameProfile());
EntityPlayer p = world.func_152378_a(uuid);

For entities:

entity.getUniqueID() or entity.getPersistentID()

For entity getters, you will need to loop through loaded entities and check if uuid matches...

Link to comment
Share on other sites

Yes I know all of that but my party involves entities not players... hence my problem, to note, The entities in my party get impacted by an entity that is thrown by the player whereupon they get saved to NBT and set to dead in the world and their NBT data is transferred to the impact entity and then to a holder entity that allows animations to play and then if animations are successful the NBT is transferred to an itemstack and the holder entity is set to dead and the itemstack then acts like a spawn/despawn egg. The UUID's persist throughout this process oh and the itemstack can spawn another impact entity to get the "tamed" entity  to spawn/despawn it at the will of a player. I want the entity that is captured to join the players party at the first time it is caught and to remain in teh party throughout any subsequent spawn/despawns.

 

This is why I tried to create a party where the slots are NBT Tag Compounds that are saved to the player to give him easy access to the party members so that the player can switch places with the party members easily. But I guess that is not proper, so I am looking for a solution to achieve this result. I got it figured out to the point where I can get the switching to work but the party doesnt persist through player death or log off

Link to comment
Share on other sites

BTW I know how to get the UUID I dont know how to get the entity from the UUID - basically not sure how to say the following in code "Hey server I want to get this entity from UUID ashfjws23857tiodjswufy847390ri and all associated properties including health, position, extra stats ive given my entity etc etc.."

 

[NOTE] - the UUID's are different and unpredictable depending on which entity the player captures and the player will be able to swap out party members for different ones so the solution im looking for needs to be able to account for that hence why I thought the NBT approach I posted above would work.

Link to comment
Share on other sites

BTW I know how to get the UUID I dont know how to get the entity from the UUID - basically not sure how to say the following in code "Hey server I want to get this entity from UUID ashfjws23857tiodjswufy847390ri and all associated properties including health, position, extra stats ive given my entity etc etc.."

 

[NOTE] - the UUID's are different and unpredictable depending on which entity the player captures and the player will be able to swap out party members for different ones so the solution im looking for needs to be able to account for that hence why I thought the NBT approach I posted above would work.

To get entity from uuid, loop through world entities and check uuid match...

Link to comment
Share on other sites

Does anyone know of a way to save a stable reference to the entity so that it is easy to get the entity at a later date whenever i want? i tried getEntityById but the id changes since it is not a UUID. Im not sure how to get the entity from the UUID, and am not aware of any other good references to entities.

For players, uuid is always and everywhere the same, get uuid and get player:

UUID uuid = EntityPlayer.func_146094_a(player.getGameProfile());
EntityPlayer p = world.func_152378_a(uuid);

For entities:

entity.getUniqueID() or entity.getPersistentID()

For entity getters, you will need to loop through loaded entities and check if uuid matches...

 

I'll just point out that this doesn't work for servers that are in Offline mode, because it doesn't retrieve UUIDs from Mojang.

http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/1291999-unique-artifacts-powerful-randomly-generated-items?comment=1408

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Which is a real, serious issue that even Mojang suffers from.

The Game should never ever just give the player a random UUID. If you have no internet connection and the launcher has no cached UUID available, the Game must not let you in! But the launcher does it, which can corrupt worlds (in vanilla Wolves forget their owner for example).

 

I've seen this when the dedicated server is in offline mode and a client connects.  There's an internet connection, but the server (having the server.properties file say that it's offline) just doesn't fetch the UUIDs.  Keybounce has his server set up that way so that for storytelling purposes he can do name-changes and have "NPCs" for a Youtube series he's doing.  The official name-change service is not sufficient (requiring a 30 day wait between changes!)

 

That is: the client sends a valid UUID that Mojang recognizes, but the server can't find a player with that UUID.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

BTW I know how to get the UUID I dont know how to get the entity from the UUID - basically not sure how to say the following in code "Hey server I want to get this entity from UUID ashfjws23857tiodjswufy847390ri and all associated properties including health, position, extra stats ive given my entity etc etc.."

 

[NOTE] - the UUID's are different and unpredictable depending on which entity the player captures and the player will be able to swap out party members for different ones so the solution im looking for needs to be able to account for that hence why I thought the NBT approach I posted above would work.

To get entity from uuid, loop through world entities and check uuid match...

 

isnt looping through the entire world entities list very messy and intensive? wouldn't that cause lag? I need a fairly clean solution, some way to give the player access to my custom entities and all of their data easily since my spawn/despawn/spawn again / despawn again ...etc process will happen frequently and I need to be able to have the data each time it happens and it needs to be persistent. My entities will have stats that change over time and the player will be able to become each entity in his/her party and in order for that to work I need to be able to get the entities in a robust and not very intensive way so that the process doesnt cause crazy lag cause if this is occurring in a multiplayer, it probably isnt good to have several players looping through the entire entities list every time they want to change whomever they are in their party.

 

Especially because the player holds the entity they want in the form of an item... where that itemstack contains all of the desired entities NBT data

Link to comment
Share on other sites

Ok so I found something really weird

 

If I add a variable called test that is an int to my class

public class PlayerParty implements IExtendedEntityProperties {

protected EntityPlayer entityPlayer;
        public int test = 0;
public static final int NUM_SLOTS = 6;
//.....the rest of the class
}

 

then inside my setParty Member Method I do test = 100 as seen in the method below and outprint the value of test when the method is called I get

[server thread/INFO] [sTDOUT]: [custommod.player.properties.PlayerParty:setPartyMember:28]: setting test to100 //this is as expected

public void setPartyMember(int slot, EntityPartyMember partyMember){
	NBTTagCompound compound = new NBTTagCompound();
	partyMember.writeToNBT(compound);
	partyNbt[slot] = compound; 
                test = 100;
                System.out.println("setting test to" + test);
	System.out.println("partyMember added to party " + partyNbt[slot]);
}

 

then if I put a system.outprint line to get the value of test when NBT data gets saved I get the following

 

[server thread/INFO] [sTDOUT]: [custommod.player.properties.PlayerParty:saveNBTData:80]: test equals 0 //this is the problem...why would it be 0? when I just set it to be 100!

@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound properties = new NBTTagCompound();
	for(int i=0; i < NUM_SLOTS; i++){
                System.out.println("test equals " + test);
		if(partyNbt[i] != null){
			compound.setTag("Slot"+i, partyNbt[i]);
		}
	}
	compound.setTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME, properties);
}

 

Does anyone know why?

Link to comment
Share on other sites

So basically, my method setPartyMember gets called server side, at which point the variable test is set to be 100. I do not call any other method to reset test to 0 but for some reason it is 0 when saveNBTData is called... I have no idea why...

 

*Note to force the saveNBTData method to be called hit the escape key to bring up the log off menu

Link to comment
Share on other sites

Look at your saving code. ;p

 

@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound properties = new NBTTagCompound();
	for(int i=0; i < NUM_SLOTS; i++){
                System.out.println("test equals " + test);
		if(partyNbt[i] != null){
			properties.setTag("Slot"+i, partyNbt[i]);
		}
	}
	compound.setTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME, properties);
}

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

Link to comment
Share on other sites

Is it because when I get the instance of my party class I am passing in the client player using

 

PlayernParty ppp = PlayerParty.get(Minecraft.getMinecraft().thePlayer);

 

but I am calling my setPartyMember code server side and as such only the client player is updated and not the server player? is that what is going on and does the saveNBTData method work only from server player?

 

public void setPartyMember(int slot, EntityPartyMember partyMember){
	NBTTagCompound compound = new NBTTagCompound();
	partyMember.writeToNBT(compound);
	partyNbt[slot] = compound; 
                test = 100;
                System.out.println("setting test to" + test);
	System.out.println("partyMember added to party " + partyNbt[slot]);
}

Link to comment
Share on other sites

Also, The reason why I cannot simply just save a reference to my entity is because the entity only exists inside my itemstack and does not exist in the world. So I need to be able to save the entity NBT to the players extended properties in order to get its data.

Link to comment
Share on other sites

Also, I have a bit of a problem I think. I need to call this method on server but I need to have the client player. I am not sure how to get the client player while also calling the method on the server without doing PlayerParty pp = PlayerParty.get(Minecraft.getMinecraft().thePlayer); - I think this may be causing the weird problems where I need packets.

public void tryToAddMobToParty(){
	PlayerParty pp = PlayerParty.get(Minecraft.getMinecraft().thePlayer);
	System.out.println("free slot? " + pp.hasFreeSlot());
	if(pp.hasFreeSlot() == true){
	pp.setPartyMember(pp.getNextFreeSlot(), this.hitEntity);	
	System.out.println(this.hitEntity + " saved to slot " + pp.getNextFreeSlot());
	}else{
		this.displayChatMessage(EnumChatFormatting.RED + "Your Party Is Currently Full!");
		System.out.println("next free slot " + pp.getNextFreeSlot());//should return -1 at this point because party is full [works]
		pp.getPartyMember(0);
		System.out.println("first in party " + pp.getPartyMember(0));
		pp.getPartyMember(1);
		System.out.println("second in party " + pp.getPartyMember(1));
		pp.getPartyMember(2);
		System.out.println("thirdt in party " + pp.getPartyMember(2));
		pp.getPartyMember(3);
		System.out.println("fourth in party " + pp.getPartyMember(3));
		pp.getPartyMember(4);
		System.out.println("fifth in party " + pp.getPartyMember(4));
		pp.getPartyMember(5);
		System.out.println("sixth in party " + pp.getPartyMember(5));
	}
}

Link to comment
Share on other sites

Thornack, srsly bro - past your whole code that is used by IEEP. If you don't know where the problem is, how can you expect others to find it without your full code. Aside from the fact mentioned above (diesiben), when I pointed out your saving method - you made typo.

 

You:

@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound properties = new NBTTagCompound();
	for(int i=0; i < NUM_SLOTS; i++){
                System.out.println("test equals " + test);
		if(partyNbt[i] != null){
			compound.setTag("Slot"+i, partyNbt[i]);
		}
	}
	compound.setTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME, properties);
}

Me: (compound parameter is the full NBT of given player, you shouldnt save data directly there, so I belive it's a typo)

@Override

public void saveNBTData(NBTTagCompound compound) {

NBTTagCompound properties = new NBTTagCompound();

for(int i=0; i < NUM_SLOTS; i++){

                System.out.println("test equals " + test);

if(partyNbt != null){

properties.setTag("Slot"+i, partyNbt);

}

}

compound.setTag(EXTENDED_ENTITY_PROPERTIES_TAGNAME, properties);

}

 

If you care for my opinion - your design is NOT good. Many here would probably help you do better (suggestions), but you would need to post full code as picking parts that YOU THINK don't work doesn't mean that those are only ones that are badly written.

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

Link to comment
Share on other sites

So pokemon it is. :P Few things 1st:

- Why are you saving entity to both ItemStack and player?

- Will this caught entity always be your custom made one (EntityCustom), following that - consider using EntityLivingBase && !EntityPlayer.

- What is EntityPartyMember - my guess it's an interface, but how do you use it?

 

Now to problems:

Many pointed out- you can't use Minecraft.class on server. How to fix it? By passing args.

Your #onEntityCaught() is called from The Holder that has the entity it is catching and has the catcher who want to catch it. Change it to #onEntityCaught(EntityPlayer catcher, EntityCustom entityCaught), you will need to pass them and then do same with #tryToAddMobToParty() -> #tryToAddMobToParty(EntityPlayer catcher, EntityCustom entityCaught).

That way you will now be able to:

PlayerParty pp = PlayerParty.get(catcher);

 

I don't quite know where those methods are palced (example: pp.setPartyMember(pp.getNextFreeSlot(), this.hitEntity); seems like being inside holder/projectile, which mean you can access it faster) but you probably get what I mean.

 

I am sometimes bored (I really am) so if you want insight help call me Skype: ernio333

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

Link to comment
Share on other sites

So pokemon it is. :P Few things 1st:

- Why are you saving entity to both ItemStack and player?

- Will this caught entity always be your custom made one (EntityCustom), following that - consider using EntityLivingBase && !EntityPlayer.

- What is EntityPartyMember - my guess it's an interface, but how do you use it?

 

Now to problems:

Many pointed out- you can't use Minecraft.class on server. How to fix it? By passing args.

Your #onEntityCaught() is called from The Holder that has the entity it is catching and has the catcher who want to catch it. Change it to #onEntityCaught(EntityPlayer catcher, EntityCustom entityCaught), you will need to pass them and then do same with #tryToAddMobToParty() -> #tryToAddMobToParty(EntityPlayer catcher, EntityCustom entityCaught).

That way you will now be able to:

PlayerParty pp = PlayerParty.get(catcher);

 

I don't quite know where those methods are palced (example: pp.setPartyMember(pp.getNextFreeSlot(), this.hitEntity); seems like being inside holder/projectile, which mean you can access it faster) but you probably get what I mean.

 

I am sometimes bored (I really am) so if you want insight help call me Skype: ernio333

 

pokemon nah but you can think of it as similar to pokeball thrown at a pokemon in an attempt to catch it i guess if that clarifies the end goal. im trying to get that sort of functionality kinda but with a twist, the twist is my holder entity and what it does and the custom animation based on my calculation and you get an egg afterwards rather than a pokeball. ill switch over from using stuff liek EntityCustom and ImpactEntity to EntityPokemon and EntityPokeball if that helps clarify my code?

 

-atm I am saving to both the itemstack and the player just to see if I can and how to do it, eventually I want it to save first to itemstack and when itemstack is picked up by the player then the entity that is inside the itemstack is saved to players party. (think of the itemstack as a spawn egg or if it helps visualize what I want to achieve think of it as a pokeball or whatever youd like to imagine it as Im looking for that kind of functionality roughly)

 

-the caught entity will always be of the same type (of type EntityPartyMember) and is my custom entity, I plan to eliminate all vanilla entities from my mod and dont plan to make my mod compatible with others as that would be a pain to code

 

-EntityPartyMember is the class that I made all of my entities inherit from, it extends EntityAnimal implements IEntityAdditionalSpawnData, I implement my stats system for EntityPartyMember among other stuff.

so EntityEachCustomEntityInMyMod->EntityPartyMember->EntityAnimal->EntityAgeable->EntityCreature->EntityLiving->EntityLivingBase->Entity

 

ok ya i thought i would have to pass arguments it wont be an easy fix but ill give it a go

 

and you are correct I could access it faster but I dont want to, I want it accessed in order i think (unless ill suffer performance issues due to my order or some technical impossible wall I cannot get over or around that prevents me from achieving my goal) but i dont think that should be the case.

 

next time im modding I may give you a should over skype

Link to comment
Share on other sites

Join the conversation

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

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

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

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

×   Your previous content has been restored.   Clear editor

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



×
×
  • Create New...

Important Information

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