Jump to content

[SOLVED][1.12.1] EntityDataManager errors driving me crazy


Recommended Posts

Posted (edited)

 So I'm trying to port my wild animals mod from 1.7.10 all the way to 1.12.1. I'm pretty good at porting upwards and of course when new features are added sometimes I consider whether I should convert to the new system. I have complex animations on my client based on state fields in my custom entity class so I need to sync from server to client. In the past I used a custom packet with an NBT compound and frankly it worked well. But I wanted to see if the data manager would be a good alternative.

 

So first of all I simply continued to use my NBT compound and created a DataParameter of type NBT compound. And for my EntityElephant class it worked great. When the elephant gets attacked the animation on the client has it rear up and furthermore nothing crashes. EDIT: Actually, it only appears to work because the code is running on both sides -- it doesn't seem that the information is being sent across to the client.

 

So then I tried to do the same thing for my birds of prey entities. However, no matter which way I try it, I get various full on crashes of the game. I thought maybe it was due to using an NBT compound and so broke it up into individual DataParameter keys instead. And still I get crazy crashes. So I need help. Let me describe the latest crash...

 

So when I try to spawn my EntityHawk using spawn egg the crash says: https://gist.github.com/jabelar/e5b220314afe05a26d62b30f7f18b94d

 

This is the wierd part: Now if you follow the error message, it is happening during the super constructor for my entity (see my code here), specifically when the EntityLivingBase class constructor tries to set the health. This is really weird because this fires before any code runs in my entity class and so cannot be due to my registration of my DataParameter key pairs. 

 

Now of course there is something in the class that runs before the constructor -- the static fields are created. And I'm following the vanilla code's example to instantiate all my DataParameter keys by using the createKey() method. So it MUST be a problem with that. But I cannot see what the problem is. Currently I have all the keys created against the Entity.class but have also tried EntityBirdsOfPrey.class, and the vanilla classes all create their keys against Entity.class. The only thing I could think of was that maybe I have created a duplicate field name, but I traced all the way back the type hierarchy and can't see any that are the same.

 

One other point is that I'm creating my keys against the dataManager instance inherited from the Entity class. I think that is correct, or am I supposed to instantiate my own EntityDataManager?

 

So I need help. Thanks!

 

I guess one question is whether the EntityDataManager is a valid, modern way for syncing a bit of data from the server to the client. Should i be doing something else?

 

Also, if I have code that runs on both sides does the EntityDataManager handle that -- like can I call the set command from the client? Is that ignored? Or should I be checking side before using the set command?

 

 

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

With further testing I found that my EntityElephant data manager isn't working either. It isn't crashing, but no information is getting from server to client. It only appeared to work because the codes was running on both sides.

 

So I'm confused -- I thought the data manager was to help sync bits of info to the client like the old data watcher did. But instead it seems that the data watcher is just running independently on both sides... am I missing some method that causes it to actually send the packet? The dataManager.set() method seems to create a notify change as well as marking both the entry and the set as "dirty" so I thought that meant it would send a packet.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Further tracing and yes by marking the data manager as dirty, the entity tracker sendMetaData() method will sent to all players tracking a packet  SPacketEntityMetadata that takes in the entity ID and the data manager. 

 

So I do feel that a packet should be being sent every time I set a value.

 

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted (edited)

So I wanted to check out the operation of the dataManager a bit more, so in my EntityHerdAnimal in the onLivingUpdate() method I printed out all the entry key-value pairs and whether each entry was marked dirty.

 

The code to print it out was:

   List<DataEntry<?>> entryList = dataManager.getAll();
   System.out.println("On client = "+world.isRemote+" the entity ID "+getEntityId()+" the data manager entry list is: ");
   entryList.forEach(entry -> System.out.println("key = "+entry.getKey()+" value = "+entry.getValue()+" isDirty = "+entry.isDirty()));

 

And the output was pretty much what I expected, except that EVERY entry is always marked dirty every tick. Here is what the console showed:

Spoiler

[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:onLivingUpdate:229]: On client = true the entity ID 163 the data manager entry list is: 
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@0 value = 0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@1 value = 300 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@2 value =  isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@3 value = false isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@4 value = false isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@5 value = false isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@6 value = 0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@7 value = 38.0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@8 value = 0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@9 value = false isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@a value = 0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@b value = 0 isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@c value = false isDirty = true
[21:00:30] [main/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:0,scaleFactor:2.0f,isRearing:0b} isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:onLivingUpdate:229]: On client = false the entity ID 163 the data manager entry list is: 
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@0 value = 0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@1 value = 300 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@2 value =  isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@3 value = false isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@4 value = false isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@5 value = false isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@6 value = 0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@7 value = 38.0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@8 value = 0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@9 value = false isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@a value = 0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@b value = 0 isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@c value = false isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: [com.blogspot.jabelarminecraft.wildanimals.entities.herdanimals.EntityHerdAnimal:lambda$0:230]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:0,scaleFactor:2.0f,isRearing:0b} isDirty = true
[21:00:31] [Server thread/INFO] [STDOUT]: 

The last entry shows my NBT tag compound containing some info on the "rearing" animation for the elephant. The rest of the values are built in to the vanilla classes, for example the value 40 is the health entry You can figure out the other value's meaning by looking at the type hierarchy for EntityTameable.

 

Why are all these always dirty? None of the values are changing at this time (the log above goes on and on every tick with same info and all entries marked dirty). I thought that if they were dirty that a packet would be sent, they'd all be marked clean and they'd only be dirty again if the values changed through another set() call...

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Okay, this is really weird. I decided to increment health (which has a vanilla entry into the datamanager) and increment my rearing counter every living update and to also print out both server and client side data manager entries. The health tracks on the client but the rearing counter does not -- in other words, there is a value in my data parameter that is changed on the server but does not get updated on the client!

 

Here is the console output:

Spoiler

[21:41:28] [Server thread/INFO] [STDOUT]: On client = false the entity ID 162 the data manager entry list is: 
[21:41:28] [Server thread/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@7 value = 34.0 isDirty = true
[21:41:28] [Server thread/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:4,scaleFactor:2.0f,isRearing:0b} isDirty = true
[21:41:28] [main/INFO] [STDOUT]: On client = true the entity ID 162 the data manager entry list is: 
[21:41:28] [main/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@7 value = 34.0 isDirty = true
[21:41:28] [main/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:1,scaleFactor:2.0f,isRearing:0b} isDirty = true
[21:41:30] [main/INFO] [STDOUT]: On client = false the entity ID 162 the data manager entry list is: 
[21:41:30] [Server thread/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@7 value = 35.0 isDirty = true
[21:41:30] [Server thread/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:5,scaleFactor:2.0f,isRearing:0b} isDirty = true

[21:41:30] [main/INFO] [STDOUT]:  On client = true the entity ID 162 the data manager entry list is: 
[21:41:31] [main/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@7 value = 35.0 isDirty = true
[21:41:31] [main/INFO] [STDOUT]: key = net.minecraft.network.datasync.DataParameter@d value = {rearingCounter:1,scaleFactor:2.0f,isRearing:0b} isDirty = true

 

So why is a value that is in the server data manager and marked dirty never showing up in the client data manager, even though other entries are???

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted (edited)
9 hours ago, diesieben07 said:

You are overriding entityInit to do nothing, but that is where all entity classes register their data manager entries (and where you should do it, too). You need to call the super method there.

Ahhh! That explains the current crash for the EntityBirdsOfPrey, I actually had previously been using EntityInit() as I thought that was the right place but was having other troubles with the data manager and so I moved it back to the constructor and I must have mistakenly deleted the super call at the same time leaving the stub.

 

However, now that I moved things back to entityInit() I'm back to my original problem:

 

There is still something fishy in the order of the code in my constructor versus entityInit(). The way I understand it should work is that my constructor first calls super constructor which calls my entityInit() which should first call its super-method and then the rest of my constructor code should run. Since my data parameter is a tag compound, I need to initialize the tag compound before the data parameter registration, but that means that the tag compound init has to be in entityInit(). But when I do that, I get a null pointer exception trying to set the first tag. If I move the same code it works much better in the constructor.

 

This fails with null pointer (console error https://gist.github.com/jabelar/8a305ca1d38c9b2893867a2aa4f73374) on the first line of initSyncCompound():

Spoiler

    public EntityHerdAnimal(World par1World)
    {
        super(par1World);
        setupAI();        
     }
    
    @Override
    public void entityInit()
    {
        super.entityInit();
        initSyncDataCompound();
        dataManager.register(SYNC_COMPOUND, syncDataCompound);               
        setSize(0.9F, 1.3F);
    }
    
    @Override
    public void initSyncDataCompound()
    {
        syncDataCompound.setFloat("scaleFactor", 1.0F);
        syncDataCompound.setInteger("rearingCounter", 0);
        syncDataCompound.setBoolean("isRearing", false);
    }

 

This passes -- same code just moved from entityInit() to constructor:

Spoiler

    public EntityHerdAnimal(World par1World)
    {
        super(par1World);
        initSyncDataCompound();
        dataManager.register(SYNC_COMPOUND, syncDataCompound);               
        setSize(0.9F, 1.3F);
        setupAI();        
     }
    
    @Override
    public void entityInit()
    {
        super.entityInit();
    }
    
    @Override
    public void initSyncDataCompound()
    {
        syncDataCompound.setFloat("scaleFactor", 1.0F);
        syncDataCompound.setInteger("rearingCounter", 0);
        syncDataCompound.setBoolean("isRearing", false);
    }

 

 

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted (edited)

Oh, I was just reading up on constructors and I guess Java initializes class fields in different order than other languanges I'm used to. Apparently they are created AFTER the call to the super constructor. https://stackoverflow.com/questions/20577430/what-is-object-field-initialization-and-constructor-order-in-java

 

So that is a big part of my trouble I think. The entityInit() is called from the inherited Entity constructor which I guess is called before my sync compound is even declared! So basically entityInit() cannot access any class fields...although it is strange then that it could still register the data parameter which is a class field.

 

Let me fix that and see if I'm still having issues with the client sync...

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Yay, finally got it working.

 

Honestly, using that entityInit() and data manager is pretty tricky. Don't think i've ever run into so many crashes in a two day session of programming ever. The main points I was missing was that entityInit() was called before my constructor and that my field initializations were happening after the super constructor.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted (edited)
29 minutes ago, diesieben07 said:

Why on earth are you using an NBT tag here?

I explained in the beginning. I'm porting from 1.7.10 where I used a custom packet and it was easy to serialize NBT for syncing. Then I saw that data manager had a tag compound serializer, so figured the easiest way to port was to use that. But that got me in trouble because in my custom packet system I was of course saving the packet contents into my NBT field. But with data manager this got complicated because presumably it already stores it and I was essentially instantiating it twice and things got out of sync.

 

But basically NBT was a relic from my previous implementation that I thought would be easy to port since all my methods were already expecting that format.

 

I've broken it all into separate data manager entries now though.

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

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.

Announcements



×
×
  • Create New...

Important Information

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