Car hasn't been updated in 200 simulation frames. Destroying

My Player.log is full of the message “Car hasn’t been updated in 200 simulation frames. Destroying.” So I investigated.

Cars have a watchdog mechanism that destroys them if nobody is feeling responsible for them anymore. Normally, the lane they are on or the train or station they are currently in has the responsibility over these cars. So normally they would always update them and everything would be fine, but still some places in my settlement were notorious for eating cars every now and then.

The reason is this code segment at the beginning of LaneBase’s UpdateSimulationThread():

		if ((Old.GetSimulator().simulationFrame + GetID()) % 50 == 0L)
		{
			incomingCar?.UpdateWatchdog();
			outgoingCar?.UpdateWatchdog();
			for (int i = 0; i < carsNew.Count; i++)
			{
				carsNew[i].first.UpdateWatchdog();
			}
		}

The watchdog for cars on lanes is only updated every 50 frames. Now normally this wouldn’t be a problem with a 200 frames tolerance window for the watchdog, but the problem happens when cars switch lanes quite fast, as can happen especially around intersections. And an intersection is any place where at least 2 roads meet (the blue circles in building mode). A car may leave one lane shortly before that lane would have called UpdateWatchdog(). Then the new lane is responsible for triggering the watchdog update. But since the new lane has a different ID, the next watchdog update may take up to 50 frames to occur. And this is actually enough time for the car to leave that lane and enter a new one, without receiving any update to its watchdog. If this happens several times in a row, then the 200 frames can be exceeded even though the car was on proper lanes all the time and was indeed updated, but the watchdog update just never happened. Since this depends on the specific IDs of the lanes in sequence, the watchdog exceeding happens in the same spots on the map where the ID offsets align in an unfavorable way with the speed of the cars traveling through those nodes. Also, the nodes need to be close and the faster the cars can move (think: highways or super highways) the more likely. If you want to reproduce this, simply build this and watch it eat cars pretty frequently:

The fix is simple: Make sure that the watchdog is also updated whenever a car switches lanes. For me the easiest way to test if this would solve the problem was to patch LaneBase.RemoveOutgoingCar() so it would update the watchdog. The new method would need to look like this:

	public Car RemoveOutgoingCar()
	{
		if (HasOutgoingCar())
		{
			outgoingCar.UpdateWatchdog();
			Car result = outgoingCar;
			outgoingCar = null;
			return result;
		}
		return null;
	}

Now with this patch I still get cars being destroyed, especially after loading, but these cars are at position (0.00, 0.00, 0.00). So they have some other problems I still need to investigate. But it’s a different cause.


As an afterword, the watchdog is only a debugging tool anyway. In an InfraSpace that works smoothly, this feature is actually not necessary. But for now it’s useful to find bugs, but unfortunately this debugging tool has a bug itself that causes cars to be destroyed occasionally and seemingly (but not quite!) randomly.

1 Like

Maybe this bug gets fixed with a new release. If not, could you make it into a mod?

Short answer: yes.

Long answer: There are a couple of things that I think would make InfraSpace a better game and that I want to put into one or more mods. But some of these require changing things in the game that will likely not be the same anymore after the upcoming update and after the release. So I’m only practicing for now and trying to understand the code base. And I think you should fix bugs before developing new features, so I’m focusing on that for now. I also hope that those make it into the game even and I don’t have to put that into mods.
So you’ll have to wait at least for the next patch (probably next week the devs said) or maybe even for the release to get my attempt at bug fixes.

very good investigation, thank you. the fix is simple and makes sense, I’m putting it on the list for the update next week.

1 Like

My log files still have entries about cars getting destroyed. I identified 4 other cases, the first of which I have a pretty good lead on.

This happens to return trips just as they are starting, so on the first pathIndex. They are still on the side road at that point.

Why their watchdog is not updated anymore is because of LaneBase.TryPushOntoLane():

public virtual bool TryPushOntoLane(Car car)
	{
		if (!CanPushOntoLane())
		{
			return false;
		}
		if (!CheckCarPath(car))
		{
			return true;
		}
		incomingCar = car;
		if (CanBePartOfPath())
		{
			incomingCar.currentPathIndex++;
			incomingCar.currentHost = this;
		}
		return true;
	}

The line incomingCar = car; is overriding whatever the incomingCar is with the new one to push onto the lane. If there’s still a car in there, then the sideroad won’t update its watchdog anymore (and some other things will also stop working I guess). The place that should prevent this is LaneBase.CanPushOntoLane():

	public bool CanPushOntoLane()
	{
		if (WorldScripts.Inst.worldSettings.IsTrafficOnlyVisual() && incomingCar == null)
		{
			return true;
		}
		if (occupiedSpace + 600 > totalSpace || incomingCar != null)
		{
			return carsNew.Count == 0;
		}
		return true;
	}

However, there are some cases when this method can return true even though incomingCar is not null. In that case, when there is a subsequent TryPushOntoLane() (and these two are almost always in succession) then there will be a problem. With my current understanding of the code and the runtime thread states, I can’t say exactly why this happens, but it’s probably happening because a return trip gets put onto the side road and in the same frame just a little later a forward trip gets sent on the same side road. Potentially a concurrency issue is happening here.

A fix that I tried and that works reliable (100% during my tests) is to return false in CanPushOntoLane() if incomingCar != null. To be honest I don’t get why this method is not just reduced to this single check, because that seems to be all that matters? Something like:

	public bool CanPushOntoLane()
	{
		return (incomingCar == null);
	}

The second problem also deals with side roads and I’ll look into next. This happens for forward deliveries only and on their last pathIndex, so probably right when they turn into the side road or are delivering the good.

The third issue happens after loading. A lot of cars get destroyed whenever I load the game. In one save about 1000 of them. In another save of similar city size it’s only 100-200. It seems to be cars on IntersectionConnections don’t get hooked up properly after a save or something like that, but not sure. This definitely is a one-time effect after loading.

The fourth issue are return trips arriving at Gondolas. This should be easy to figure out later, but I won’t give Gondolas a high priority.

1 Like

Thanks!

I just added the fix for RemoveOutgoingCar() and the change to CanPushOntoLane().
I think the weird method body was because it grew historically. incomingCar used to not exist.

Will both get released with the Balancing Update on Friday.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.