Log in to reply
 

IndexOutOfRangeException with vehicle.Occupants



  • Hi people. I'm having a problem with a mod I'm making. I have a script that spawns enemy peds around the player, both on foot and in vehicles. These peds and vehicles should despawn if they get too far from the player or they are destroyed. Vehicles also should despawn if they are empty. I'm using this function that is executed OnTick (1 second interval):

    private void ClearLists()
    {
    	Ped current;
    	Vehicle currentv;
    
    	for (int i = Enemy1Vehicles.Count - 1; i >= 0; i--)
    	{
    		currentv = Enemy1Vehicles.ElementAt(i);
    		Ped[] currentoc = currentv.Occupants;
    		if (((currentv != null && currentv.Exists()) && (currentoc == null || currentoc.Length == 0 || !currentv.IsAlive || World.GetDistance(currentv.Position, Game.Player.Character.Position) > maxRange) || MostWantedEnabled == false))
    		{
    			if (currentv.CurrentBlip.Exists()) currentv.CurrentBlip.Remove();
    			Enemy1Vehicles.Remove(currentv);
    			currentv.MarkAsNoLongerNeeded();
    		}
    	}
    
    	for (int i = Enemy1Peds.Count - 1; i >= 0; i--)
    	{
    		current = Enemy1Peds.ElementAt(i);
    		if (((current != null && current.Exists()) && (!current.IsAlive || World.GetDistance(current.Position, Game.Player.Character.Position) > maxRange) || MostWantedEnabled == false))
    		{
    			if (current.CurrentBlip.Exists()) current.CurrentBlip.Remove();
    			Enemy1Peds.Remove(current);
    			current.MarkAsNoLongerNeeded();
    		}
    	}
    }
    

    However, sometimes while I'm playing I get an IndexOutOfRangeException at "currentoc = currentv.Occupants". Looks like the function is trying to access an index that is out of range, but that's part of ScriptHookV. I think it may be related to the peds that are inside, maybe doing MarkAsNoLongerNeeded on some of them gives that error, but I tried a few things and nothing works. Any idea what could be happening?


  • MODERATOR

    Took the liberty to put your code in a more readable shape. Use wrap your code with 3 backticks (```) before and after the code, to put it in a code block.

    Code things:

    Have you set all spawned vehicles as mission entities? They can despawn if not.

    That for loop. Got a specific reason for iterating from max-1 to 0? If not, use a range-based for loop. It would look like this:

    foreach(vehicle in Enemy1Vehicles) {
      // things happen
    }
    

    You wouldn't need to manage Ped and Vehicle in that function any more.

    That if statement. It's rather big. Think of splitting it up in more manageable chunks. Things that are strange:

    • currentoc == null: I wouldn't expect currentv.Occupants to return a nullptr. If you look at the source, this doesn't seem to be able to happen ;)
    • You're checking if it's occupied? Putting that explicitly helps debugging later on.
    • Same with distance.

    You're removing an item from a list while you're iterating through that list. That's not a good idea. Since you're removing everything anyway, empty the list when you're done handling the contents. This is where it probably crashes.

    I applied my suggestions, hopefully I guessed your intentions right :p

        private void ClearLists()
        {
            foreach (var vehicle in Enemy1Vehicles)
            {
                bool hasOccupants = vehicle.Occupants.Length > 0;
                bool isInRange = World.GetDistance(vehicle.Position, Game.Player.Character.Position) < maxRange;
                if (vehicle.Exists() && (!hasOccupants || !vehicle.IsAlive || !isInRange) || !MostWantedEnabled)
                {
                    if (vehicle.CurrentBlip.Exists()) vehicle.CurrentBlip.Remove();
                    vehicle.MarkAsNoLongerNeeded();
                }
            }
            Enemy1Vehicles.Clear();
    
            foreach (var ped in Enemy1Peds)
            {
                bool isInRange = World.GetDistance(ped.Position, Game.Player.Character.Position) < maxRange;
                if (ped.Exists() && (!ped.IsAlive || !isInRange) || !MostWantedEnabled)
                {
                    if (ped.CurrentBlip.Exists()) ped.CurrentBlip.Remove();
                    ped.MarkAsNoLongerNeeded();
                }
            }
            Enemy1Peds.Clear();
        }
    


  • Oh, I couldn't find a button to add a "code" tag or something like that, I noticed part of the code was formatted automatically, but didn't know the three backticks did that. Thanks.

    Anyway, I iterate backwards because I'm deleting some items from the list. I've heard that iterating forward makes it skip some elements because all the following items are shifted when one of them gets removed, and that can end in a IndexOutOfRangeException. If I iterate backwards, that shouldn't happen, as it only shifts the elements that are after the one I remove. The error doesn't seem to be related to that loop tho, because the exception points to "currentv.Occupants". It seems to be something internal to that function.

    The "currentoc == null" was something I added to try to fix the problem. I read the source code later and noticed it always returns a list, even if it's empty. I forgot to remove it after that.

    Is there any other way to check if the vehicle is occupied? I couldn't find any other SHVDN function related to that. In fact, I only need to know if the vehicle is empty.

    The loop is not meant to remove all the items from the list (unless "MostWantedEnabled" is false, which clears everything from the mod). It should only remove those peds that are dead, out of range or empty vehicles. I can't clear the list while the mod is active, I need it to track the peds.

    About the peds and vehicles, I haven't set them as mission entities, I didn't know that was necessary. I thought the game kept them in memory until I mark them as no longer needed. In fact, If I don't despawn them manually, I can see their blips moving around the map even kilometers away from me. Maybe it's because I spawned them instead of picking a naturally spawned ped.

    I'll try to reorganize the loops, maybe the error is related to the order in which I mark peds and vehicles as no longer needed, but it's weird. Thanks anyway.



  • I replaced the line vehicle.Occupants in my script with this function:

    private bool IsVehicleEmpty(Vehicle v)
            {
                int nv = Function.Call<int>(Hash.GET_VEHICLE_NUMBER_OF_PASSENGERS, v);
                bool dv = Function.Call<bool>(Hash.IS_VEHICLE_SEAT_FREE, v, -1);
                return (nv == 0 && dv);
            }
    

    And the error is gone. I think it's a problem with the implementation of get_Occupants(), which doesn't work in this particular case for some reason. Thanks!


Log in to reply
 

Looks like your connection to GTA5-Mods.com Forums was lost, please wait while we try to reconnect.