public static void housekeeping()
        {
            try {
                // delete invalid segments
                List <ushort> segmentIdsToDelete = new List <ushort>();
                foreach (KeyValuePair <ushort, TrafficSegment> e in PrioritySegments)
                {
                    var segmentId = e.Key;
                    if (segmentId <= 0)
                    {
                        segmentIdsToDelete.Add(segmentId);
                        continue;
                    }

                    NetSegment segment = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId];
                    if (segment.m_flags == NetSegment.Flags.None || (!priorityNodes.Contains(segment.m_startNode) && !priorityNodes.Contains(segment.m_endNode)))
                    {
                        segmentIdsToDelete.Add(segmentId);
                    }
                }

                foreach (var sId in segmentIdsToDelete)
                {
                    Log.Warning("Housekeeping: Deleting segment " + sId);
                    PrioritySegments.Remove(sId);
                    TrafficLightsManual.RemoveSegmentLight(sId);
                }

                // delete invalid nodes
                List <ushort> nodeIdsToDelete = new List <ushort>();
                foreach (ushort nodeId in priorityNodes)
                {
                    NodeValidityState nodeState = NodeValidityState.Valid;
                    if (!isValidPriorityNode(nodeId, out nodeState))
                    {
                        if (nodeState != NodeValidityState.SimWithoutLight)
                        {
                            nodeIdsToDelete.Add(nodeId);
                        }

                        switch (nodeState)
                        {
                        case NodeValidityState.SimWithoutLight:
                            Log.Warning("Housekeeping: Re-adding traffic light at node " + nodeId);
                            Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].m_flags |= NetNode.Flags.TrafficLights;
                            break;

                        case NodeValidityState.Unused:
                            // delete traffic light simulation
                            Log.Warning("Housekeeping: RemoveNodeFromSimulation " + nodeId);
                            RemoveNodeFromSimulation(nodeId);
                            break;

                        default:
                            break;
                        }
                    }
                }

                foreach (var nId in nodeIdsToDelete)
                {
                    Log.Warning("Housekeeping: Deleting node " + nId);
                    RemovePrioritySegments(nId);
                }

                // add newly created segments to timed traffic lights
                foreach (KeyValuePair <ushort, TrafficLightsTimed> e in TrafficLightsTimed.TimedScripts)
                {
                    TrafficLightsTimed timedLights = e.Value;
                    ushort             nodeId      = e.Key;

                    timedLights.handleNewSegments();
                }
            } catch (Exception e) {
                Log.Warning($"Housekeeping failed: {e.Message}");
            }
        }
		private static bool isValidPriorityNode(ushort nodeId, out NodeValidityState nodeState) {
			nodeState = NodeValidityState.Valid;

			if (nodeId <= 0) {
				nodeState = NodeValidityState.Invalid;
				Log.Warning($"Housekeeping: Node {nodeId} is invalid!");
				return false;
			}

			NetManager netManager = Singleton<NetManager>.instance;

			if ((netManager.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None) {
				nodeState = NodeValidityState.Unused;
				Log.Warning($"Housekeeping: Node {nodeId} is unused!");
				return false; // node is unused
			}

			bool hasTrafficLight = (netManager.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;
			var nodeSim = TrafficLightSimulation.GetNodeSimulation(nodeId);
			if (nodeSim != null) {
				if (! Flags.mayHaveTrafficLight(nodeId)) {
					nodeState = NodeValidityState.IllegalSim;
					Log.Warning($"Housekeeping: Node {nodeId} has traffic light simulation but must not have a traffic light!");
					return false;
				}

				if (!hasTrafficLight) {
					// traffic light simulation is active but node does not have a traffic light
					nodeState = NodeValidityState.SimWithoutLight;
					Log.Warning($"Housekeeping: Node {nodeId} has traffic light simulation but no traffic light!");
					return false;
				} else {
					// check if all timed step segments are valid
					if (nodeSim.IsTimedLightActive()) {
						TimedTrafficLights timedLight = nodeSim.TimedLight;
						if (timedLight == null || timedLight.Steps.Count <= 0) {
							Log.Warning("Housekeeping: Timed light is null or no steps for node {nodeId}!");
							TrafficLightSimulation.RemoveNodeFromSimulation(nodeId, false);
							return false;
						}

						/*foreach (var segmentId in timedLight.Steps[0].segmentIds) {
							if (! IsPrioritySegment(nodeId, segmentId)) {
								Log.Warning("Housekeeping: Timed light - Priority segment has gone away!");
								RemoveNodeFromSimulation(nodeId);
								return false;
							}
						}*/
					}
					return true;
				}
			} else {
				byte numSegmentsWithSigns = 0;
				for (var s = 0; s < 8; s++) {
					var segmentId = netManager.m_nodes.m_buffer[nodeId].GetSegment(s);
					if (segmentId <= 0)
						continue;
					if (netManager.m_segments.m_buffer[segmentId].m_startNode != nodeId && netManager.m_segments.m_buffer[segmentId].m_endNode != nodeId)
						continue;

					SegmentEnd prioritySegment = GetPrioritySegment(nodeId, segmentId);
					if (prioritySegment == null) {
						continue;
					}

					// if node is a traffic light, it must not have priority signs
					if (hasTrafficLight && prioritySegment.Type != SegmentEnd.PriorityType.None) {
						Log.Warning($"Housekeeping: Node {nodeId}, Segment {segmentId} is a priority sign but node has a traffic light!");
						prioritySegment.Type = SegmentEnd.PriorityType.None;
					}

					// if a priority sign is set, everything is ok
					if (prioritySegment.Type != SegmentEnd.PriorityType.None) {
						++numSegmentsWithSigns;
					}
				}

				if (numSegmentsWithSigns > 0) {
					// add priority segments for newly created segments
					numSegmentsWithSigns += AddPriorityNode(nodeId);
				}

				bool ok = numSegmentsWithSigns >= 2;
				if (!ok) {
					Log.Warning($"Housekeeping: Node {nodeId} does not have valid priority segments!");
					nodeState = NodeValidityState.NoValidSegments;
				}
				return ok;
			}
		}
        private static bool isValidPriorityNode(ushort nodeId, out NodeValidityState nodeState)
        {
            nodeState = NodeValidityState.Valid;

            if (nodeId <= 0)
            {
                nodeState = NodeValidityState.Invalid;
                return(false);
            }

            var node = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId];

            if (node.m_flags == NetNode.Flags.None)
            {
                nodeState = NodeValidityState.Unused;
                return(false);                // node is unused
            }

            var nodeSim = GetNodeSimulation(nodeId);

            if (nodeSim != null)
            {
                if ((node.m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None)
                {
                    // traffic light simulation is active but node does not have a traffic light
                    nodeState = NodeValidityState.SimWithoutLight;
                    return(false);
                }
                else
                {
                    // check if all timed step segments are valid
                    if (nodeSim.FlagTimedTrafficLights && nodeSim.TimedTrafficLightsActive)
                    {
                        TrafficLightsTimed timedLight = TrafficLightsTimed.GetTimedLight(nodeId);
                        if (timedLight == null || timedLight.Steps.Count <= 0)
                        {
                            Log.Warning("Housekeeping: Timed light is null or no steps!");
                            RemoveNodeFromSimulation(nodeId);
                            return(false);
                        }

                        /*foreach (var segmentId in timedLight.Steps[0].segmentIds) {
                         *      if (! IsPrioritySegment(nodeId, segmentId)) {
                         *              Log.Warning("Housekeeping: Timed light - Priority segment has gone away!");
                         *              RemoveNodeFromSimulation(nodeId);
                         *              return false;
                         *      }
                         * }*/
                    }
                    return(true);
                }
            }
            else
            {
                bool ok = false;
                for (var s = 0; s < 8; s++)
                {
                    var segmentId = node.GetSegment(s);
                    if (segmentId <= 0)
                    {
                        continue;
                    }
                    NetSegment segment = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId];
                    if (segment.m_startNode != nodeId && segment.m_endNode != nodeId)
                    {
                        continue;
                    }

                    PrioritySegment prioritySegment = GetPrioritySegment(nodeId, segmentId);
                    if (prioritySegment == null)
                    {
                        continue;
                    }

                    // if node is a traffic light, it must not have priority signs
                    if ((node.m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None)
                    {
                        prioritySegment.Type = PrioritySegment.PriorityType.None;
                    }

                    // if a priority sign is set, everything is ok
                    if (prioritySegment.Type != PrioritySegment.PriorityType.None)
                    {
                        ok = true;
                        break;
                    }
                }

                if (!ok)
                {
                    nodeState = NodeValidityState.NoValidSegments;
                }
                return(ok);
            }
        }