Jump to content

[Solved] Can you access the capability data of a player that is offline? AND How to properly use a weak reference of an Entity.


Kriptikz

Recommended Posts

I'm having trouble with the readingNBT method of an entity.

Here is the code:

Spoiler

	protected void readEntityFromNBT(NBTTagCompound compound)
	{
		System.out.println("*********Reading NBT**************");
		
		this.setCasterId(UUID.fromString(compound.getString("caster_uuid")));
		this.caster = getCaster();
		this.setSpellData(this.caster);

        if (compound.hasKey("direction", 9) && compound.getTagList("direction", 6).tagCount() == 3)
        {
            NBTTagList nbttaglist1 = compound.getTagList("direction", 6);
            this.motionX = nbttaglist1.getDoubleAt(0);
            this.motionY = nbttaglist1.getDoubleAt(1);
            this.motionZ = nbttaglist1.getDoubleAt(2);
        }
        else
        {
            this.setDead();
        }
	}

	public void setSpellData(EntityLivingBase caster)
	{
		this.spellData = caster.getCapability(SpellDataProvider.SPELL_DATA, null);
	}

 

When it gets to the line: this.setSpellData(this.caster) this.caster is null so I get an error trying to get the capability.

I was thinking of having the entity keep going after cast, even if the player logs off and still level up the spell in the players capability if it hits an entity.

I think i'm just going to have the entity die if the player logs off.

Link to comment
Share on other sites

So I shouldn't have:

private EntityLivingBase caster;

In my entity class at all, and should instead use either PlayerList::getPlayerByUUID or World::GetPlayerEntityByUUID anytime I reference the caster? PlayerList::getPlayerByUUID also seems to return null if the player isn't online though, So I think I'm going to go with setting the entity to dead if PlayerList::getPlayerByUUID or World::GetPlayerEntityByUUID returns null.

No idea how to use a WeakReference but I'll look into it. Why use a WeakReference over private EntityLivingBase caster

Edited by Kriptikz
Link to comment
Share on other sites

A WeakReference points to an object that can be collected by the GC.
Storing the player object directly, even after the player logs out, keeps it in memory, when it is no longer required. As such, overuse of this practice can and will lead to "memory leaks".

  • Like 1

Also previously known as eAndPi.

"Pi, is there a station coming up where we can board your train of thought?" -Kronnn

Published Mods: Underworld

Handy links: Vic_'s Forge events Own WIP Tutorials.

Link to comment
Share on other sites

For example:

Imagine both player use the spell that holds the reference to the other.  These two EntityPlayer objects now have hard references to each other.

Both players then log out.

The garbage collector comes along and sees that EntityPlayerA holds a reference to EntityPlayerB and EntityPlayerB holds a reference to EntityPlayerA.  Both objects still reference the other and therefor cannot be GC'd without potentially breaking something, so GC leaves them alone.  GC has no way to determine that this is a closed and isolated loop due to the space-time complexity of making that determination.  See: https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Reference_counting

Both players log back on again and repeat the process.  Every time they do, it allocates two new EntityPlayer objects that can never be GC'd.

  • Like 1

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

So if I want to cache the entity I will use a weak reference like this? :

Spoiler

	private WeakReference<EntityLivingBase> caster;

	public EntitySpellBase(World world, EntityLivingBase caster)
	{
		this(world);
		
		this.caster = new WeakReference<EntityLivingBase>(caster);
		this.setCasterId(caster.getUniqueID());
		this.setSpellData(caster);
		this.setPositionAndDirection(caster);
	}

 

From what I understand so far, using a weak reference can get GC'd at any moment correct? 

So anytime I use caster I would need to check if it is null and if it is use:

this.caster = new WeakReference<EntityLivingBase>(caster);

Correct?

OR, Should I put the weak reference inside the entities onUpdate() method as well as the doSpell() method because these are the two places where the caster is referenced. I'm still worried about it getting GC'd in the middle of using it though, can it get GC'd in the middle of a method that it was created in?

Here is the code, the WeakReference isn't implemented but this is what I'm working with:

https://github.com/Kriptikz/Archmage/blob/1.11.2/src/main/java/kriptikz/archmage/entity/EntitySpellBase.java

Edited by Kriptikz
Link to comment
Share on other sites

Awesome Thanks! I'm still trying to wrap my head around why using a WeakReference is necessary though.

So why I'm using a WeakReference variable in the entity rather than a StrongReference, such as,

private EntityLivingBase caster; Is because of possible memory leak. Does this mean that even if the entity is setDead() and in World::updateEntities() , I believe this is where the entity is removed if Entity::isDead, the entity is removed but may not be set up so it can be GC'd?

Another way to put this is, shouldn't any variables/references inside the entity, weak or strong, and the entity itself be set to be GC'd when it is removed?

  • Like 1
Link to comment
Share on other sites

21 minutes ago, diesieben07 said:

If you have a strong reference to the caster, your entity will keep that caster loaded, even if the caster has already died. Say a player spawns your entity and then logs out. Your caster entity will still be loaded and reference the (now dead) player entity. Because of that the dead player entity cannot be garbage collected, a memory leak.

If you have a WeakReference, the player entity can be garbage collected, your reference will just turn to null.

My EntitySpell should only last about 10-20 seconds max and then it gets setDead(). So the player spawns the EntitySpell and then logs out. The EntitySpell is referencing the player entity, which means the player entity cannot be GC'd. The EntitySpell keeps existing for 10 more seconds, then gets setDead(). Now that EntitySpell is dead, its strong reference to the player entity should be removed when EntitySpell is GC'd, thus allowing player entity to also be GC'd, is this correct?

 Also, back to the capability, if I have a strong reference to the players capability in EntitySpell and the player logs out the EntitySpell will still be holding that reference, right? So if I change some of the players capability data in EntitySpell using its strong reference to the players capability, when the player is logged out, will that actually effect the players capability data? Will that change in the capability data when the player was logged out hold true when the player logs back in?

Link to comment
Share on other sites

2 minutes ago, diesieben07 said:

Reference to what I said above, you cannot interact with an EntityPlayer object in a reasonable way if that player has logged out. Why do you need this capability access?

In my mod the player has spells. Each spell has a level and xp associated with it. So taking the fireball spell for example, if the player casts a fireball spell and it hits an entity it will damage the entity relative to the level of the spell, higher level spell does more damage. I also want the players SpellData capability to increase the xp for the fireball spell so the spell can level up and get stronger. Why I wanted to access the capability after the player logged out is because if the player casts the fireball spell, which will spawn an EntitySpell for the fireball, and then the player logs out I wanted the fireball to continue going until it impacts something. When it impacts an entity I need the SpellData capability to get the fireball spells level so it knows how much damage to do. Also, when it impacts an entity, I need to get the players SpellData capability and increase the fireball spells xp. The problem is the player is logged out, so I was wondering if I could modify the capability when the player was logged out so this would still work. It seems I probably shouldn't do it this way, but I was curious if it was possible. What I'm going to do instead is if the player is logged off the spell will die.

I wanted to store the player in a strong reference because I don't like the idea of creating an EntityLivingBase and ISpellData object every tick because it seems like overkill but I guess it is necessary:

    @Override
    public void onUpdate()
    { 
	EntityLivingBase caster = getCaster();
    	
    	if (caster == null)
    	{
    		this.setDead();
    		return;
    	}
    	
    	ISpellData spellData = caster.getCapability(SpellDataProvider.SPELL_DATA, null);

	// Do spell movement updates and logic 

 

Link to comment
Share on other sites

46 minutes ago, diesieben07 said:

Ehm... nothing in that code is creating an ISpellData or EntityLivingBase object.

I shouldn't have said creating an object, I mean by creating the reference the computer has to allocate more memory to store that reference. I was thinking why recreate another reference every tick if I can just have one main reference. It seems I may be misunderstanding a fundamental programming concept, as allocating memory for the reference is probably too negligible to really matter. Every reference does allocate more memory though, right?

 

46 minutes ago, diesieben07 said:

As for your issue, how about you make it so the spell just does not give XP when the player logs out? That even makes sense outside of the game-mechanics: If you can't witness the event, you can't really gain experience from it, can you?

Interesting, now I'm torn between 3 possible implementations lol.

1. When the player logs off the spells energy source is then gone so the spell vanishes doing nothing. 

2. When the player logs off the spell continues going and does its effects but the player will not get xp from it.

3. Have the player gain xp when the spell is initially spawned.

 

Probably going to test all 3 and see how it goes. This is all pretty much off topic now though, so thanks for all the help I really appreciate you sticking with me and helping me wrap my head around this stuff.

Edited by Kriptikz
Link to comment
Share on other sites

6 minutes ago, diesieben07 said:

Even if you had the reference stored in a field, the code would eventually still go to main memory, fetch the object reference and store it in a CPU register. It boils down to the same thing. This is very much premature optimization, if it is an optimization at all.

Didn't even know premature optimization was a thing lol. I've gotta be careful with that.

Link to comment
Share on other sites

7 hours ago, diesieben07 said:

Ehm, this is untrue and not how Java's GC works. It starts at very specific "root objects" (for example everything that's currently in a local variable) and from there just follows every reference. In the end it collects everything that was not visited. Loops are not a problem for the GC.

Simplified example is simplified for ease of comprehension.

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

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Sometimes, tweaking load order or specific configs can also help ease those conflicts.
    • I have recently tired downloading forge so i can run mods for hypixel skyblock but whenever i download forge and do it the same way as videos and tutorials forge keeps downloading as log file and doesnt download the .jar file any help will be aprecciated.
    • So I have created a Create Mod Server with my friends and i to play everyone can join play except one of my friends because when he tries to launch the game from the CurseForge Launcher it will load but never open or give him a crash report, we have tried everything from deleting CF and reinstalling it to Updating his drivers, IDK what to do if anyone can help I would be very Grateful
    • I get this error when trying to start the server for forged mods. In the code below it mentions distant horizons but no matter what mod I put in (deleting distant horizons) it still gives an error and doesn't launch Crash Report UUID: 3e91d5c7-18a7-43c2-a935-a8d28de560d1 FML: 47.3 Forge: net.minecraftforge:47.3.10[23:33:47] [main/ERROR] [minecraft/Main]: Failed to start the minecraft server net.minecraftforge.fml.LoadingFailedException: Loading errors encountered: [    Distant Horizons (distanthorizons) encountered an error during the sided_setup event phase§7java.lang.ExceptionInInitializerError: null]     at net.minecraftforge.fml.ModLoader.waitForTransition(ModLoader.java:246) ~[fmlcore-1.20.1-47.3.10.jar%23104!/:?] {}     at net.minecraftforge.fml.ModLoader.lambda$dispatchAndHandleError$20(ModLoader.java:210) ~[fmlcore-1.20.1-47.3.10.jar%23104!/:?] {}     at java.util.Optional.ifPresent(Optional.java:178) ~[?:?] {}     at net.minecraftforge.fml.ModLoader.dispatchAndHandleError(ModLoader.java:210) ~[fmlcore-1.20.1-47.3.10.jar%23104!/:?] {}     at net.minecraftforge.fml.ModLoader.lambda$loadMods$15(ModLoader.java:190) ~[fmlcore-1.20.1-47.3.10.jar%23104!/:?] {}     at java.lang.Iterable.forEach(Iterable.java:75) ~[?:?] {}     at net.minecraftforge.fml.ModLoader.loadMods(ModLoader.java:190) ~[fmlcore-1.20.1-47.3.10.jar%23104!/:?] {}     at net.minecraft.server.loading.ServerModLoader.load(ServerModLoader.java:31) ~[forge-1.20.1-47.3.10-universal.jar%23108!/:?] {re:classloading}     at net.minecraft.server.Main.main(Main.java:125) ~[server-1.20.1-20230612.114412-srg.jar%23103!/:?] {re:classloading}     at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {}     at java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {}     at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:111) ~[fmlloader-1.20.1-47.3.10.jar%2369!/:?] {}     at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.lambda$run$1(CommonLaunchHandler.java:103) ~[fmlloader-1.20.1-47.3.10.jar%2369!/:?] {}     at net.minecraftforge.fml.loading.targets.CommonServerLaunchHandler.lambda$makeService$0(CommonServerLaunchHandler.java:27) ~[fmlloader-1.20.1-47.3.10.jar%2369!/:?] {}     at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) ~[modlauncher-10.0.9.jar%2355!/:?] {}     at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) ~[modlauncher-10.0.9.jar%2355!/:?] {}     at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) ~[modlauncher-10.0.9.jar%2355!/:?] {}     at cpw.mods.modlauncher.Launcher.run(Launcher.java:108) ~[modlauncher-10.0.9.jar%2355!/:?] {}     at cpw.mods.modlauncher.Launcher.main(Launcher.java:78) ~[modlauncher-10.0.9.jar%2355!/:?] {}     at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141) ~[bootstraplauncher-1.1.2.jar:?] {}  
    • This error keeps coming up after player tries to join server, doesn't happen to anyone else.  Error: Internal Exception: io.netty.handler.codec.DecoderException: java.lang.ArrayIndexOutOfBoundsException: Index 17196645 out of bounds for length 13 Heres the latest.log https://pastebin.com/uaw3KC0K  Heres the debug.log https://drive.google.com/file/d/1QzqtCMUf7ps1Iz85AOsMM7W8QhbHjBeU/view?usp=sharing
  • Topics

×
×
  • Create New...

Important Information

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