Jump to content

Kriptikz

Members
  • Posts

    81
  • Joined

  • Last visited

Posts posted by Kriptikz

  1. if getEntityFromUuid() return null when you use it in readNBT(), then don't use it there, do what I recommended above.

    The only thing you would need to change from how I do it in my code would be instead of:

    caster = this.world.getPlayerEntityByUUID(this.casterId);

     

    you would do something like:

    caster = this.world.getMinecraftServer().getEntityFromUuid(this.casterId);

    I didn't test this out so no idea if you need isRemote() before using this or honestly if this is how you would actually get WorldServer in order to use getEntityFromUuid() but this is what I would try.

  2. In my mod I have my spells store who the spellcaster is by uuid and I store the player entity in a weak reference so you don't have to get the entity by uuid every time you want to use it and it's a weak reference, instead of a normal EntityLivingBase, to prevent possible memory leaks.

    Here is where I read/write the uuid to nbt:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/entity/EntitySpellBase.java#L302-L326

     

    Then anytime you need to do something based on the entity you can either use, also make sure to check for null if using this directly:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/entity/EntitySpellBase.java#L235-L248

     

    Or you can do what I do, which is every time onUpdate runs I store the caster in a local EntityLivingBase and if the caster is offline getCaster() will return null so I just set my entity to dead and return:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/entity/EntitySpellBase.java#L118-L124

     

  3. I'm pretty new to modding Minecraft so no idea if I'm actually doing this properly, but I call super.renderParticle() in my custom particle to let the vanilla code handle the rendering, mainly because I don't know how to properly use opengl and also I don't think writing the rendering myself is necessary for basic particles.

    Here is my custom fire particle and where I call super.renderParticle():

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/client/particle/ParticleFire.java#L43-L48

    The only custom code in my ParticleFire#renderParticle() is to change the scale of the particle over time so it shrinks. Then I just call super.renderParticle to do the actually rendering.

    Also, in order to use the custom texture here:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/client/particle/ParticleFire.java#L39

    I register it here, in the TextureStitchEvent:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/client/TextureStitcher.java

     

    Also, remember you have to register the class using TextureStitchEvent, I do that in ClientProxy here:

    https://github.com/Kriptikz/Archmage/blob/1.11.2-Refactor/src/main/java/kriptikz/archmage/proxy/ClientProxy.java#L24

     

     

     

     

     

  4. You can try offsetting it by a couple blocks and see what happens, it may be colliding with the thrower. Also if your using most of EntityThrowable code why not just extend from it and override/add stuff. Minecraft also has an EntityFireball class that you could check out.

    But yeah, working Git repo will make helping you out now and possibly in the future much easier.

     

  5. This is the tutorial I followed by the man himself. It is old, but still works as the general concepts are the same. I don't think you have to create the debug configurations anymore, as they were already there for me for 1.11. I did have to remove the VM arguments in order for it to work though.

    Also you shouldn't have to change anything in the build.gradle file except for

    Spoiler
    
    version = "1.11.2-0.0.1" // CHANGE 0.0.1 to correct mod version
    group = "yourname.modid" // CHANGE yourname to your name/username and ofcourse change modid to your modid
    archivesBaseName = "modid" // CHANGE modid

     

    Hope this helps.

    https://www.youtube.com/watch?v=xp2jmt47yTQ

    • Like 2
  6. Take a closer look at EntityArrows onUpdate() method, your missing a lot of stuff in yours, for example that would be where you call onHit() after doing some raytracing and/or calling findEntityOnPath(). You should also check out EntityFireball, mainly how onUpdate() is written in there and how its different than EntityArrows onUpdate().

    You could also just extend EntityThrowable and change some stuff in it to suit your needs, like getGravityVelocity to something smaller and override onImpact() to handle what it does on impact.

    • Like 1
  7. 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.

  8. 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.

  9. 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 

     

  10. 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?

  11. 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
  12. 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

  13. 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

  14. 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.

  15. When spawning an entity from an items onPlayerStoppedUsing method, the entity works server side but it seems it's not getting spawned client side.

    Here is my item class:

    https://github.com/Kriptikz/Archmage/blob/1.11.2/src/main/java/kriptikz/archmage/item/ItemWand.java#L55

    From what I understand, an entity should only be spawned server side and the server will tell the client to spawn the entity, but it's not spawning it on the client.

    This worked for me previously before I changed my spell class hierarchy to something I thought would work better for me. Basically I have it set up so my ItemWand class will be cleaner,

    previously I had this:

    Spoiler
    
    	@Override
    	public void onPlayerStoppedUsing(ItemStack stack, World worldIn, EntityLivingBase entityLiving, int timeleft)
    	{
    		if (timeleft <= 975)
    		{		
    			if (!worldIn.isRemote)
    			{
    				EntitySpellProjectile spell = null;
    				IPlayerData playerData = PlayerDataProvider.getPlayerCapability(entityLiving);
    				
    				switch(playerData.getSelectedSpell())
    				{
    					case 0:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.FIREBALL);
    						break;
    					case 1:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.STUN);
    						break;
    					case 2:
    						SpellSelfHeal.healPlayer((EntityPlayer) entityLiving,
    								((SpellDataHelper.getHealAmount(playerData.getSelectedSpell(),
    										playerData.getSpellLevel(playerData.getSelectedSpell())))));
    
    						float manaCost = SpellDataHelper.getManaCost(playerData.getSelectedSpell(),
    								playerData.getSpellLevel(playerData.getSelectedSpell()));
    						
    						if ((playerData.getMana() - manaCost) >= 0)
    						{
    							playerData.setMana(playerData.getMana() - manaCost);
    						}
    						else
    						{
    							entityLiving.sendMessage(new TextComponentString("Not enough mana!!!"));
    						}
    
    						break;
    					case 3:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.HEALPROJECTILE);
    						break;
    					case 4:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.DIZZIFY);
    						break;
    					case 5:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.SLOW);
    						break;
    					case 6: 
    						// SpellDummyClone
    						if ((playerData.getMana() - 25) >= 0)
    						{
    							playerData.setMana(playerData.getMana() - 25);
    							
    							RayTraceResult result = this.rayTrace(worldIn, (EntityPlayer)entityLiving, false);
    							BlockPos blockPos = result.getBlockPos();
    							
    							worldIn.spawnEntity(new EntityDummyTarget(worldIn, blockPos, entityLiving.getPosition(), 100, (EntityPlayer)entityLiving));
    						}
    						else
    						{
    							entityLiving.sendMessage(new TextComponentString("Not enough mana!!!"));
    						}
    						
    						break;
    					case 7:
    						if (entityLiving instanceof EntityPlayer)
    						{
    							SpellLevitate.levitate((EntityPlayer) entityLiving);
    						}
    						break;
    					case 8:
    						spell = new EntitySpellProjectile(worldIn, entityLiving, SpellProjectileHandler.WARP);
    						break;
    					case 9:
    						TeleportDestination teleportDestination = playerData.getTeleportDestination();
    						BlockPos teleportPos = teleportDestination.getTeleportPos();
    						int dimId = teleportDestination.getTeleportDimId();
    						int oldId = entityLiving.getEntityWorld().provider.getDimension();
    						
    				        if (oldId != dimId)
    				        {
    				            TeleportDestination.teleportToDimension((EntityPlayer) entityLiving, dimId, teleportPos.getX() + 0.5, teleportPos.getY() + 1.5, teleportPos.getZ() + 0.5);
    				        } 
    				        else
    				        {
    				            ((EntityPlayer) entityLiving).setPositionAndUpdate(teleportPos.getX()+0.5, teleportPos.getY() + 0.5, teleportPos.getZ()+0.5);
    				        }
    						
    						break;
    					
    				}
    				if (spell != null)
    				{
    					float manaCost = SpellDataHelper.getManaCost(playerData.getSelectedSpell(), playerData.getSpellLevel(playerData.getSelectedSpell()));
    					
    					if ((playerData.getMana() - manaCost) >= 0)
    					{
    						playerData.setMana(playerData.getMana() - manaCost);
    						worldIn.spawnEntity(spell);						
    					}
    					else
    					{
    						entityLiving.sendMessage(new TextComponentString("Not enough mana!!!"));
    					}
    				
    				}
    
    			}
    		}
    	}

     

    Some spells are projectiles and some are not. So in the new version I have ISpellBase interface which has only two methods, isProjectile() and doSpell(). From here I have another base class called EntitySpellBase. If the spell is a projectile I extend EntitySpellBase, if not it just implements ISpellBase. I use isProjectile() to determine if the spell is a projectile, if it is I spawn it, if its not I just doSpell(). My goal with the change was to make the code cleaner and make it easier to implement different models for different spell projectiles as well as allow handling a spell easily whether it is suppose to be a projectile or not.

    So in the switch statement any spell could be initialized like this, whether it is suppose to be an entity or not:

    Spoiler
    
    spell = new SpellFireball(worldIn, entityLiving);

     

    Where SpellFireball is replaced by the corresponding spell.

    This seemed like it could work nicely, but maybe it's a bad design. I'm pretty new to programming in general so any criticism is welcome. I do have other designs in mind but I liked this one the most. 

×
×
  • Create New...

Important Information

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