Official Fulqrum Publishing forum

Official Fulqrum Publishing forum (http://forum.fulqrumpublishing.com/index.php)
-   FMB, Mission & Campaign builder Discussions (http://forum.fulqrumpublishing.com/forumdisplay.php?f=203)
-   -   Null exception (http://forum.fulqrumpublishing.com/showthread.php?t=30429)

Smokeynz 03-12-2012 11:23 PM

Null exception
 
@ kodiak or others who could ponder an issue.

We have been noticing after a mission is running for a while(it isnt consistant) we see a null exception error

System.NullReferenceException: Object reference not set to an instance of an object. at(blah blah CLOD code references)

I've been pondering if the reference is actually objects or lists of objects as Ai aircraft that are destroyed through despawner type methods. However the list reference is not completely cleared or purged from the system operating memory. Or if you have dual conditions to call a despawner, say at the completion of a path and time period, whre the object completes the path, the timer reference carries on and looks for the object when times up, and when the object does not exsist anymore and thus the exception.

The question is, is what I discribe a possibility, then if so, how do we purge the references. I was wondering if despawners should despawn the mission number they spawn in on.

FG28_Kodiak 03-13-2012 04:23 AM

if you have a list for example:

List<AiAircraft> PlaneList = new List<AiAircraft>();

PlaneList.Add(aircraft1); // only examples
PlaneList.Add(aircraft2);


aircraft2.Destroy();

So now we have the aircraft2 already in List. Maybe more of destroyed Aircrafts. To remove them from List:
PlaneList.RemoveAll(item => item == null); // removes all null valued Aircrafts from list.

Smokeynz 03-13-2012 08:10 AM

Ok I can confirm after testing it is a double up on one object, being an Aiaircraft
The problem is because I have applied two options for despawning aiaircraft,
one is via Ontaskcompleted or final path point, then I run
Timeout(despawntime2, () => { damageAiControlledPlane(actor); });

However I also run OnActorcreated upon the same object
Timeout(despawntime, () => { damageAiControlledPlane(actor); });

So hence I am trying to run the despawner upon the same object twice, in early testing the nullpointer exception did not show up, which is kinda random reaction.

The theory for why I use time periods is to allow aiplane use for players, but also I want to despawn for short flight path aiaircraft, since the timer period is 45~60 minutes.

So choice is, rely on one timer or somehow purge the remaining object timer
Usually the problem is overall timer as this is longer, but either can also overlap.

Kodiak I see how to reference a list object by null, but how to reference this aiaircraft as it is the timer acting upon the ai aircraft.

Code:




 #region Despawner 

    /*
    despawner timer added to original code, sets overall time period for actors,
    Advantage is, if delayed, say trying to land behind masses of planes, actors despawn at timer period set anyway.
    allows for players to fly groups of planes,
    Player can re eneter flights or groups, as bombers take them to target, use each at target then fly group home
    despawn only triggers once player leaves whole group
*/
    public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
    {
        base.OnActorCreated(missionNumber, shortName, actor);

        // note despawntime (set as varible at start of cs)
        if (actor is AiGroundActor && missionNumber > 0)//main mission is left alone
            //if ((actor as AiGroundActor).Type() != maddox.game.world.AiGroundActorType.AAGun)//all ai ground except aa guns
            Timeout(despawntime3, () =>
            {
                if (actor != null)
                { (actor as AiGroundActor).Destroy(); }
            });

        if (actor is AiAircraft)       
            Timeout(despawntime, () => { damageAiControlledPlane(actor); });   
    }
 
    public override void OnPlaceLeave(Player player, AiActor actor, int placeIndex)
    {
        base.OnPlaceLeave(player, actor, placeIndex);
        Timeout(despawntime, () => { damageAiControlledPlane(actor); });       
    }
   
    // Path completion despawner, when flight path ends despawner action triggered
    // note:final despawner detects players in group   

    public override void OnActorTaskCompleted(int missionNumber, string shortName, AiActor actor)
    {
        base.OnActorTaskCompleted(missionNumber, shortName, actor);
        if (actor is AiGroundActor)
            if (actor != null)
                Timeout(despawntime2, () => { (actor as AiGroundActor).Destroy(); });
           
        Timeout(despawntime2, () => { damageAiControlledPlane(actor); });
    }

    //check if a player is in any of the "places"
    private bool isAiControlledPlane(AiAircraft aircraft)
    {
        if (aircraft == null) return false;       
        for (int i = 0; i < aircraft.Places(); i++)
            if (aircraft.Player(i) != null)
                return false;
        return true;
    }

    private void destroyPlane(AiAircraft aircraft)
    {
        if (aircraft != null)
            aircraft.Destroy();
    }

    //actual Aiaircraft despawner, note it polls player detection
    private void damageAiControlledPlane(AiActor actorMain)
    {       
        foreach (AiActor actor in actorMain.Group().GetItems())
        {
            if (actor == null || !(actor is AiAircraft))
                return;
            AiAircraft aircraft = (actor as AiAircraft);
            if (!isAiControlledPlane(aircraft))
                return;
            if (aircraft == null)
                return;
            (actor as AiAircraft).Destroy();           
        }
    }

    #endregion

    /*=========================================*/


FG28_Kodiak 03-13-2012 08:46 AM

For me it seems the error is in
Code:

private void damageAiControlledPlane(AiActor actorMain)
    {       
        foreach (AiActor actor in actorMain.Group().GetItems())
        {
            if (actor == null || !(actor is AiAircraft))
                return;
            AiAircraft aircraft = (actor as AiAircraft);
            if (!isAiControlledPlane(aircraft))
                return;
            if (aircraft == null)
                return;
            (actor as AiAircraft).Destroy();           
        }
    }

Why you use the Group to destroy a single Airplane? Do you test the Group for null value? It make no sense to me to use the Group in this case.

Smokeynz 03-13-2012 09:24 AM

hmmm that bit is straight from collection, despawner, unmodified.

Thought it was trying to handle group of planes, works without me applying several attemps to run this, as single routine ok, with double up it seems cause error.

Ataros 03-13-2012 05:09 PM

IIRC group must be used in case a player creates e.g. 6 bombers, then leaves his aircraft to create another group of 5 bombers, etc., etc. If each aircraft in a group is not removed this would lead to AI spamming. Groups and foreach cycle is the solution to this IIRC.

Smokeynz 03-13-2012 05:39 PM

cheers Ataros, Yep thats how I understood it

For the moment I decided to run just one Triggered despawn timer from the On created.

Actually I wonder if the despawning routine justs needs a catch for null, timers completing looking for aircraft to despawn that have been destroyed already, or despawned already.
Im sure in the case of aircraft destroyed we see the same nullpoint exception looking for the aircraftat a later time.
Also be nice to purge references of aircraft from the oncreated that dont exist anymore(this happens with despawner, but maybe not by ingame shot down destroyed)

FG28_Kodiak 03-14-2012 04:19 AM

No the group is not nessesary, every Actor you created calls OnActorCreated(..) so every Actor is destroyed after the amount of time.
It's doppelt gemoppelt as we germans says ;).
And there is no test if the group exists.
if(actorMain.Group() != null)

Smokeynz 03-14-2012 06:49 AM

So I presume this trimmed version is all that is needed

NOTE this edit cause another error...sigh
Code:

private void damageAiControlledPlane(AiActor actorMain)
    {       
        if (actor == null || !(actor is AiAircraft))
              return;
        AiAircraft aircraft = (actor as AiAircraft);
        if (!isAiControlledPlane(aircraft))
              return;
        if (aircraft == null)
              return;
        (actor as AiAircraft).Destroy();           
    }


FG28_Kodiak 03-14-2012 07:30 AM

Why so complicated ;)

Code:

private void damageAiControlledPlane(AiActor actorMain)
    {
        if (actorMain != null && actorMain is AiAircraft)
            if (isAiControlledPlane(actorMain as AiAircraft))
                    (actorMain as AiAircraft).Destroy();
    }


Smokeynz 03-18-2012 08:23 PM

ok tried the last alteration, but still seeing exceptions when the timer comes to run despawner.

if (actor is AiAircraft)
Timeout(despawntime, () =>
{
damageAiControlledPlane(actor);
});

i added a null check after the timeout but still problem exists

if (actor is AiAircraft)
Timeout(despawntime, () =>
{
if(actor != null) damageAiControlledPlane(actor);
});

basically I think the problem is the existance/ state of the object is different after aperiod than from when the timeout is called, ie the plane is just created.
When the destroy actually runs, the aircraft has already been detroyed by activivity or something else. despite anull check it throws an exception. So the null is not the right check, in the wrong place or other problem is causing issue.

However, I have noticed that the "on actor task completed" can have 1 ai interfere with another ai if the last path length crosses another ai path.
Which makes me wonder if there is some cross over in other areas.

any ideas anyone?

Smokeynz 03-18-2012 10:13 PM

Think this explains what I am seeing, 3. Don’t kill objects too soon.

http://codebetter.com/raymondlewalle...ses-in-vb-net/

But the problem for CLOD, the Onactorcreated creates an array of objects, actors which are part of play.
If you test those objects later, and they have been destroyed by play or despawning routines, they don't exist, so either the reference of "null" or the actor reference will throw an exception. For null there has to be an array member to state "are you null" .

So with that, incounting exceptions of objects with no reference, it may require double listing arrays. The game creates an internal onactorcreated, you might have to create a second array to test the actors condition upon.

Kinda a test of "are you alive" by comparing existance of that actor in both arrays. In play that actor meets its fate, is despawned or hangs around. In 2 of these you may incounter an object reference error if trying to clear the decks of clutter. With a second array "null" is not important, because you test against the array with array, not the "object actor" which may be automatically cleared from the onactorcreated array.

confused, i am.

if I am on the right track in this, we need a method to avoid object exception when dealing with object/actor tracking over the mission period. Considering that the base mission has potential to be 24/7

in summary

objects/actors can lose references to themselves so that even the "null" test has no reference. this thows an exception object without a reference.

salmo 03-19-2012 04:27 AM

SmokyNZ, I concur with you that there is something screwy with the actors that gives rise to null exceptions. I'm unsure what the cause is, but here's a code snippet from a script I'm working on. The CheckActors routine was working & keys.Count was returning the number of actor keys, then all of a sudden, it failed & now persistently returns zero even though there are actors in the mission.

Code:

public class Mission : AMission
{
private Dictionary<String, AiActor> allActors = new Dictionary<String, AiActor>();

    public override void OnPlaceEnter(Player player, AiActor actor, int placeIndex)
    {
CheckActors();
}

public void CheckActors()
{
        List<string> keys = new List<string>(allActors.Keys);
        sendChatMessageTo(-1, "Keys count=" + keys.Count.ToString(), null);
      // .......
    }

    private void sendChatMessageTo(int army, string msg, object[] parms)
    {  // send a chat message to all players in specified army (1=red; 2=blue)
        List<Player> Players = new List<Player>();
        // on Dedi the server:
        if (GamePlay.gpPlayer() != null)
        {
            if (GamePlay.gpPlayer().Army() == army || army == -1)
                Players.Add(GamePlay.gpPlayer());
        } //rest of the crowd
        if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
        {
            foreach (Player p in GamePlay.gpRemotePlayers())
            {
                if (p.Army() == army || army == -1)
                    Players.Add(p);
            }
        }
        if (Players != null && Players.Count > 0)
            GamePlay.gpLogServer(Players.ToArray(), msg, parms);
    }
}


FG28_Kodiak 03-19-2012 04:38 AM

@Smokeynz
Could i see the complete Errormessage, please?

Smokeynz 03-19-2012 06:04 AM

@Kodiak, I've sent you the mission so you can see the error in real time.
Spawn in and sit on deck, shift tab and monitor logs.(or export logs)
let it run for about 40~50mins and you will see lots of the errors about that time.


@salmo, yeah it is possible reference lists of players or any object can fall foul of the reference problem.

A google of the exception object missing reference yeilds plently of people suffering the same problem with C#

I found 2 links(1 posted above), where point 3 of above is what happens here(I believe). I'm a bit limited in experience to diagnose C# work arounds, once I have the syntax I can work it out, but get one thing wrong and it fails.

anyway key thing, any object that has a reference that can disappear can no longer be tested with null as null reference requires an existance. we asume that null means nothing, but it actually seems to mean nothing of something, not something of nothing.:rolleyes:

think i might go blow something up in bf3



smoke

FG28_Kodiak 03-19-2012 10:40 AM

Ok, the null exeption you get has nothing to do with your script, it's the underlying engine that causes this problem.

You get also an
=================================================
System.ArgumentOutOfRangeException:

Server stack trace:
bei System.ThrowHelper.ThrowArgumentOutOfRangeExceptio n()
bei Mission.TriggerProcessing(String shortName, Boolean active)
bei Mission.OnTrigger(Int32 missionNumber, String shortName, ...

for example you have a Array arr with 100 entries. so you can access the entries with arr[0]..arr[99] if you try arr[100] you will get this exeption.
In C# the first element of an Array has the index 0 and the last has count-1.

Smokeynz 03-19-2012 06:41 PM

Ok, cheers, will ignore main exception error

Out of range

So if I count the list entries first, then do count -1 as indexing range I should in theory avoid the out of range?


All times are GMT. The time now is 01:33 PM.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Copyright © 2007 Fulqrum Publishing. All rights reserved.