Sirenix

Member
I hate nullreff crashes, in onthink I can never seem to figure them out. The idea Im trying to do here is, make a create that when spawned, attacks only Player Moviles (KillPM fight mode) or attack other uncontrolled creatures (PMBC) I get standard greeting of:

System.NullReferenceException: Object reference not set to an instance of an object.
at Server.Mobiles.TameRevenant.OnThink()
at Server.Mobiles.BaseAI.AITimer.OnTick()
at Server.Timer.Slice()
at Server.Core.Main(String[] args)

when I spawn the mobile with either of the fightmodes added. Here's what i've tried in the IsEnemy method:
(lines 62-73)
Code:
public virtual bool IsEnemy(Mobile m)
     {
       XmlIsEnemy a = (XmlIsEnemy)XmlAttach.FindAttachment(this, typeof(XmlIsEnemy));

       if (a != null)
       {
         return a.IsEnemy(m);
       }

       OppositionGroup g = OppositionGroup;

       if (g != null && g.IsEnemy(this, m))
       {
         return true;
       }

       if (m is BaseGuard)
       {
         return false;
       }

       if (GetFactionAllegiance(m) == Allegiance.Ally)
       {
         return false;
       }

       Ethic ourEthic = EthicAllegiance;
       Player pl = Ethics.Player.Find(m, true);

       if (pl != null && pl.IsShielded && (ourEthic == null || ourEthic == pl.Ethic))
       {
         return false;
       }

       if (m is PlayerMobile && ((PlayerMobile)m).HonorActive)
       {
         return false;
       }

       if (!(m is BaseCreature) || m is MilitiaFighter)
       {
         return true;
       }

       if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)))
       {
         return false;
       }

       BaseCreature c = (BaseCreature)m;

       if ((FightMode == FightMode.Evil && m.Karma < 0) || (c.FightMode == FightMode.Evil && Karma < 0))
       {
         return true;
       }
       if ((FightMode == FightMode.Good && m.Karma > 100) || (c.FightMode == FightMode.Good && Karma > 100))
       {
         return true;
       }
       if ((FightMode == FightMode.KillPM && m is PlayerMobile && m != null) || (c.FightMode == FightMode.KillPM && m is PlayerMobile && m != null))
       {
         return true;
       }
       if ((FightMode == FightMode.KillBC && m is BaseCreature && m != null ) || (c.FightMode == FightMode.KillBC && m is BaseCreature && m != null))
       {
         BaseCreature bc = m as BaseCreature;
         if (bc.ControlMaster == null)
         {
           return true;
         }
       }
       return (m_iTeam != c.m_iTeam || ((m_bSummoned || m_bControlled) != (c.m_bSummoned || c.m_bControlled))
          /* || c.Combatant == this*/);
     }

To my knowlage I haven't change the Onthink in BaseCreature but incase something needs to be added to it I'll post that as well:

Code:
    public virtual void OnThink()
     {
       long tc = Core.TickCount;

       if (EnableRummaging && CanRummageCorpses && !Summoned && !Controlled && tc - m_NextRummageTime >= 0)
       {
         double min, max;

         if (ChanceToRummage > Utility.RandomDouble() && Rummage())
         {
           min = MinutesToNextRummageMin;
           max = MinutesToNextRummageMax;
         }
         else
         {
           min = MinutesToNextChanceMin;
           max = MinutesToNextChanceMax;
         }

         double delay = min + (Utility.RandomDouble() * (max - min));
         m_NextRummageTime = tc + (int)TimeSpan.FromMinutes(delay).TotalMilliseconds;
       }

       if (CanBreath && tc - m_NextBreathTime >= 0)
         // tested: controlled dragons do breath fire, what about summoned skeletal dragons?
       {
         Mobile target = Combatant;

         if (target != null && target.Alive && !target.IsDeadBondedPet && CanBeHarmful(target) && target.Map == Map &&
           !IsDeadBondedPet && target.InRange(this, BreathRange) && InLOS(target) && !BardPacified)
         {
           if ((Core.TickCount - m_NextBreathTime) < 30000 && Utility.RandomBool())
           {
             BreathStart(target);
           }

           m_NextBreathTime = tc +
                    (int)
                    TimeSpan.FromSeconds(BreathMinDelay + ((Utility.RandomDouble() * (BreathMaxDelay - BreathMinDelay))))
                        .TotalMilliseconds;
         }
       }

       if ((CanHeal || CanHealOwner) && Alive && !IsHealing && !BardPacified)
       {
         Mobile owner = ControlMaster;

         if (owner != null && CanHealOwner && tc - m_NextHealOwnerTime >= 0 && CanBeBeneficial(owner, true, true) &&
           owner.Map == Map && InRange(owner, HealStartRange) && InLOS(owner) && owner.Hits < HealOwnerTrigger * owner.HitsMax)
         {
           HealStart(owner);

           m_NextHealOwnerTime = tc + (int)TimeSpan.FromSeconds(HealOwnerInterval).TotalMilliseconds;
         }
         else if (CanHeal && tc - m_NextHealTime >= 0 && CanBeBeneficial(this) && (Hits < HealTrigger * HitsMax || Poisoned))
         {
           HealStart(this);

           m_NextHealTime = tc + (int)TimeSpan.FromSeconds(HealInterval).TotalMilliseconds;
         }
       }

       if (ReturnsToHome && IsSpawnerBound() && !InRange(Home, RangeHome))
       {
         if ((Combatant == null) && (Warmode == false) && Utility.RandomDouble() < .10) /* some throttling */
         {
           m_FailedReturnHome = !Move(GetDirectionTo(Home.X, Home.Y)) ? m_FailedReturnHome + 1 : 0;

           if (m_FailedReturnHome > 5)
           {
             SetLocation(Home, true);

             m_FailedReturnHome = 0;
           }
         }
       }
       else
       {
         m_FailedReturnHome = 0;
       }

       if (HasAura && tc - m_NextAura >= 0)
       {
         AuraDamage();
         m_NextAura = tc + (int)AuraInterval.TotalMilliseconds;
       }
     }
Thanks for any help with this. Everyone has that one fine point that always drives them crazy, and null refs is mine lol.
 
Hey !

I would add a null check for m_NextAura and m_NextHealTime.

But it would help to know how you initialize them in first time, and also if you could report the crash log in debug mode, that would help too ;)

Thx
 
To run your server in debug mode with windows, Create a new shortcut,
or create a shortcut from the runuo.exe,
Insure the path for the shortcut is set to your runuo.exe file.
At the end of the path add "-debug". you can then use that shortcut for debugging.
 
Oh I dono about the m_NextAura and m_NextHealTime stuff that was Poeherean's deal.

I cant seem to figure out how to edit the path/target of the Serve.UO exe. when I try to add "-debug" it says its not valid. lol. I seen this done before but I seem to be having a brain fart.

Heres the whole BC script though. Edits I added for the fightmoded KillPM and KillBC are around 1045ish.
 

Attachments

  • BaseCreature.rar
    33.5 KB · Views: 0
Please, right click on your ServUO.exe file, then choose "Send to -> Desktop".
Then edit the shortcut, and just add -debug at the end of the path.

Thanks :)
 
I'll tell ya how all this got started. Using the NecromageAI, the revenants when they are spawned by the Necromage just stand there, and I want to create a "wild revenant" that attacks any player, or a "tame revenant" that attacks other non controlled creatures. I got a few Hireables that have the necromageai, so making them spawn a wild revenant would be a bad thing. My Idea for a solution was to just create a case In the AI script that checks to see if the caster of the spell is controlled or not, and spawn one or the other of the custom revenants, however, its the revenants themselfs when created even using Add. that crash the server.

The crashed debug report does make more sence. Heres the Debug crash report,

System.NullReferenceException: Object reference not set to an instance of an object.
at Server.Mobiles.WildRevenant.OnThink() in c:\Custom UO\Avatars Kingdom 4\Scripts\My Customs\Systems\Custom Spells\Spell Helpers\WildRevenant.cs:line 87
at Server.Mobiles.BaseAI.AITimer.OnTick() in c:\Custom UO\Avatars Kingdom 4\Scripts\Mobiles\AI\BaseAI.cs:line 3237
at Server.Timer.Slice()
at Server.Core.Main(String[] args)

Heres the OnThink method in the actual WildRevenant.cs:
Line 87 is line 4 below:
Code:
    public override void OnThink()
     {
       if (!m_Target.Alive || DateTime.UtcNow > m_ExpireTime)
       {
         Kill();
         return;
       }
       else if (Map != m_Target.Map || !InRange(m_Target, 15))
       {
         Map fromMap = Map;
         Point3D from = Location;

         Map toMap = m_Target.Map;
         Point3D to = m_Target.Location;

         if (toMap != null)
         {
           for (int i = 0; i < 5; ++i)
           {
             Point3D loc = new Point3D(to.X - 4 + Utility.Random(9), to.Y - 4 + Utility.Random(9), to.Z);

             if (toMap.CanSpawnMobile(loc))
             {
               to = loc;
               break;
             }
             else
             {
               loc.Z = toMap.GetAverageZ(loc.X, loc.Y);

               if (toMap.CanSpawnMobile(loc))
               {
                 to = loc;
                 break;
               }
             }
           }
         }

         Map = toMap;
         Location = to;

         ProcessDelta();

         Effects.SendLocationParticles(
           EffectItem.Create(from, fromMap, EffectItem.DefaultDuration), 0x3728, 1, 13, 37, 7, 5023, 0);
         FixedParticles(0x3728, 1, 13, 5023, 37, 7, EffectLayer.Waist);

         PlaySound(0x37D);
       }

       if (m_Target.Hidden && InRange(m_Target, 3) && Core.TickCount >= NextSkillTime && UseSkill(SkillName.DetectHidden))
       {
         Target targ = Target;

         if (targ != null)
         {
           targ.Invoke(this, this);
         }
       }

       Combatant = m_Target;
       FocusMob = m_Target;

       if (AIObject != null)
       {
         AIObject.Action = ActionType.Combat;
       }

       base.OnThink();
     }

And the OnThink method in the BaseAI
Line 3237 is line 24 below:
Code:
      protected override void OnTick()
       {
         if (m_Owner.m_Mobile.Deleted)
         {
           Stop();
           return;
         }
         else if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal)
         {
           m_Owner.Deactivate();
           return;
         }
         else if (m_Owner.m_Mobile.PlayerRangeSensitive) //have to check this in the timer....
         {
           Sector sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile);
           if (!sect.Active)
           {
             m_Owner.Deactivate();
             return;
           }
         }

         m_Owner.m_Mobile.OnThink();

         if (m_Owner.m_Mobile.Deleted)
         {
           Stop();
           return;
         }
         else if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal)
         {
           m_Owner.Deactivate();
           return;
         }

         if (m_Owner.m_Mobile.BardPacified)
         {
           m_Owner.DoBardPacified();
         }
         else if (m_Owner.m_Mobile.BardProvoked)
         {
           m_Owner.DoBardProvoked();
         }
         else
         {
           if (!m_Owner.m_Mobile.Controlled)
           {
             if (!m_Owner.Think())
             {
               Stop();
               return;
             }
           }
           else
           {
             if (!m_Owner.Obey())
             {
               Stop();
               return;
             }
           }
         }

         if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0)
         {
           m_Owner.DetectHidden();

           // Not exactly OSI style, approximation.
           int delay = (15000 / m_Owner.m_Mobile.Int);

           if (delay > 60)
           {
             delay = 60;
           }

           int min = delay * (9 / 10); // 13s at 1000 int, 33s at 400 int, 54s at <250 int
           int max = delay * (10 / 9); // 16s at 1000 int, 41s at 400 int, 66s at <250 int

           m_Owner.m_NextDetectHidden = Core.TickCount +
                          (int)TimeSpan.FromSeconds(Utility.RandomMinMax(min, max)).TotalMilliseconds;
         }
       }
 

Attachments

  • WildRevenant and BaseAI.rar
    16.9 KB · Views: 3
Did you set m_Target or m_ExpireTime ?
Check for null values before
if(m_Target == null || m_ExpireTime == null)
//Do something if we don't have a target or expire time

NullReferenceException means that some variable does not contains any value.
How can you say that m_Target is alive or not without it containing any mobile/value attached ?
;)
Hope this help figuring out this and many other possible null reference exceptions you may have
 
I'll tell ya how all this got started. Using the NecromageAI, the revenants when they are spawned by the Necromage just stand there, and I want to create a "wild revenant" that attacks any player, or a "tame revenant" that attacks other non controlled creatures. I got a few Hireables that have the necromageai, so making them spawn a wild revenant would be a bad thing. My Idea for a solution was to just create a case In the AI script that checks to see if the caster of the spell is controlled or not, and spawn one or the other of the custom revenants, however, its the revenants themselfs when created even using Add. that crash the server.

for a wild revenant use
AI_Berserk (The NPC will attack anyone in its vicinity that is not aligned to its group, even other NPCs)
FightMode.Closest (The NPC will attack the closest player mobile in its perception range.)

for the tame one use
AI_Animal (NPC is an animal - generally not aggressive unless provoked or other factors of the NPC deem otherwise)

That would get you half way there. I would suggest copying an AI and fight mode and making a alternate version of them to use rather than changing baseai.
 
I pretty much copied the normal Revenant that came with the Stock ServeUO folder for the spell, and removed the scales to give set values for stuff. But for that if(m_Target == null || m_ExpireTime == null) I'd just throw it right above line 4 there right?

I did have a few issues with trying to edit the baseAI I might try and copy the animal ai to make a better acting tame revenant, since the target assigning part of the spell seems to be what causes the revenants to just stand there. I could be wrong though.
 
Well in the wild reverent script, I tried putting if(m_Target == null || m_ExpireTime == null) {} above
Code:
if (!m_Target.Alive || DateTime.UtcNow > m_ExpireTime)
       {
         Kill();
         return;
       }
and had the same crash. Any other idea? Right now I got him spawned with just the stock fightmode closest for testing purposes.

It crashes the moment hes spawned still.
 
Well in the wild reverent script, I tried putting if(m_Target == null || m_ExpireTime == null) {} above
Code:
if (!m_Target.Alive || DateTime.UtcNow > m_ExpireTime)
       {
         Kill();
         return;
       }
and had the same crash. Any other idea? Right now I got him spawned with just the stock fightmode closest for testing purposes.

It crashes the moment hes spawned still.


How did you write the code you have tried ?

Personnally, I'd write it that way :

Code:
if ( m_Target != null && m_ExpireTime != null && (!m_Target.Alive || DateTime.UtcNow > m_ExpireTime))
{
   Kill();
   return;
}

I also wonder about the pertinency to use DateTime.UtcNow ... I'd use DateTime.Now here (but it's up to you to see if the result you get is what you are looking for).

Now I admit the whole issue would require a deeper look at the code ...

But good luck with that attempt ;)
 
Ya it still crashes with that method, same result hehe. :/ I know it has to do with the on think method's deletion timer. What I dont get is why the normal revenant doesnt crash the shard using the same method. Is there an easier way to do a self deleting creature after 1 or 2 min then using the OnThink method?
 
Your constructor:

Code:
  [Constructable]
  public WildRevenant()

And here is the Revenant's constructor:

Code:
     public Revenant(Mobile caster, Mobile target, TimeSpan duration)

Since you do not populate target, therefore m_Target is null, which is why you get the null reference exception.
 

Active Shards

Donations

Total amount
$0.00
Goal
$1,000.00
Back