Committing all this noise even though it's sorta broken.
Committing all this noise even though it's sorta broken.

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -26,6 +26,8 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#define DEBUG
+
 #pragma warning disable 1591
 
 using KSP;
@@ -40,11 +42,9 @@
 	{
 		#region Fields
 		private Dictionary<Guid, LineRenderer> vesselLineRenderers;
-		private Dictionary<Guid, bool> vesselFrameCache;
 
 		#pragma warning disable 414
 		private bool dumpBool;
-		private Color lastColor;
 		private Color thisColor;
 		#pragma warning restore 414
 		#endregion
@@ -90,9 +90,13 @@
 			if (ARConfiguration.PrettyLines)
 			{
 				this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
-				this.vesselFrameCache = new Dictionary<Guid, bool>();
-			}
-		}
+			}
+		}
+
+		#if DEBUG
+		private System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
+		#endif
+		private Tools.DebugLogger log = Tools.DebugLogger.New(typeof(ARMapRenderer));
 
 		private void OnPreCull()
 		{
@@ -103,10 +107,15 @@
 				return;
 			}
 
-			Tools.DebugLogger log = Tools.DebugLogger.New(this);
+			#if DEBUG
+			timer.Restart();
+			long start;
+			#endif
 
 			try
 			{
+				log.Clear();
+
 				log.AppendFormat("OnPreCull.\n");
 
 				log.AppendFormat("\tMapView: Draw3DLines: {0}\n" +
@@ -117,10 +126,6 @@
 					MapView.MapCamera.Distance
 				);
 
-				this.vesselFrameCache.Clear();
-
-				log.AppendLine("vesselFrameCache cleared.");
-
 				if (FlightGlobals.ready && FlightGlobals.Vessels != null)
 				{
 					log.AppendLine("FlightGlobals ready and Vessels list not null.");
@@ -129,19 +134,15 @@
 					{
 						Vessel vessel = FlightGlobals.Vessels[i];
 
+						#if DEBUG
+						log.AppendFormat("\nStarting check for vessel {0} at {1}ms", vessel, timer.ElapsedMilliseconds);
+						#endif
+
 						if (vessel == null)
 						{
-							log.AppendFormat("Skipping vessel {0} altogether because it is null.\n");
+							log.AppendFormat("\n\tSkipping vessel {0} altogether because it is null.", vessel);
 							continue;
 						}
-
-						if (this.vesselFrameCache.TryGetValue(vessel.id, out dumpBool))
-						{
-							log.AppendFormat("Skipping vessel {0} because it's already been processed this frame.");
-							continue;
-						}
-
-						log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName);
 
 						switch (vessel.vesselType)
 						{
@@ -149,27 +150,50 @@
 							case VesselType.EVA:
 							case VesselType.Unknown:
 							case VesselType.SpaceObject:
-								log.AppendFormat("\tDiscarded because vessel is of invalid type {0}\n",
+								log.AppendFormat("\n\tDiscarded because vessel is of invalid type {0}",
 									vessel.vesselType);
 								continue;
 						}
 
+						log.AppendFormat("\n\tChecking vessel {0}.", vessel.vesselName);
+
+						#if DEBUG
+						start = timer.ElapsedMilliseconds;
+						#endif
+
 						IAntennaRelay vesselRelay = vessel.GetBestRelay();
+
+						#if DEBUG
+						log.AppendFormat("\n\tGot best relay {0} for vessel {1} in {2} ms",
+							vesselRelay, vessel, timer.ElapsedMilliseconds - start);
+						#endif
 
 						if (vesselRelay != null)
 						{
+							#if DEBUG
+							start = timer.ElapsedMilliseconds;
+							#endif
+
 							this.SetRelayVertices(vesselRelay);
+
+							#if DEBUG
+							log.AppendFormat("\n\tSet relay vertices for {0} in {1}ms",
+								vessel, timer.ElapsedMilliseconds - start);
+							#endif
 						}
 					}
 				}
 			}
-			catch (Exception)
-			{
+			catch (Exception ex)
+			{
+				this.LogError("Caught {0}: {1}\n{2}\n", ex.GetType().Name, ex.ToString(), ex.StackTrace.ToString());
 				this.Cleanup();
 			}
 			#if DEBUG
 			finally
 			{
+				log.AppendFormat("\n\tOnPreCull finished in {0}ms\n", timer.ElapsedMilliseconds);
+
 				log.Print();
 			}
 			#endif
@@ -184,9 +208,12 @@
 		#endregion
 
 		#region Utility
+		#if DEBUG
+		private long relayStart;
+		#endif
 		private void SetRelayVertices(IAntennaRelay relay)
 		{
-			lastColor = default(Color);
+			log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay);
 
 			LineRenderer renderer = this[relay.vessel.id];
 			Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
@@ -213,62 +240,59 @@
 
 			int idx = 0;
 
-			while (relay != null)
-			{
-				Vector3d nextPoint;
-
-				renderer.enabled = true;
-
-				if (!relay.CanTransmit())
-				{
-					thisColor = Color.red;
+			relayStart = timer.ElapsedMilliseconds;
+
+			Vector3d nextPoint;
+
+			renderer.enabled = true;
+
+			if (!relay.CanTransmit())
+			{
+				thisColor = Color.red;
+			}
+			else
+			{
+				if (relay.transmitDistance < relay.nominalTransmitDistance)
+				{
+					thisColor = Color.green;
 				}
 				else
 				{
-					if (relay.transmitDistance < relay.nominalTransmitDistance)
-					{
-						thisColor = Color.green;
-					}
-					else
-					{
-						thisColor = Color.yellow;
-					}
-				}
-
-				if (lastColor != default(Color) && thisColor != lastColor)
-				{
-					break;
-				}
-
-				lastColor = thisColor;
-				renderer.SetColors(thisColor, thisColor);
-
-				this.vesselFrameCache[relay.vessel.id] = true;
-
-				if (relay.KerbinDirect)
-				{
-					nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
-					relay = null;
-				}
-				else
-				{
-					if (relay.targetRelay == null)
-					{
-						return;
-					}
-
-					nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
-					relay = relay.targetRelay;
-				}
-
-				if (!MapView.Draw3DLines)
-				{
-					nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
-					nextPoint.z = nextPoint.z >= 0f ? d : -d;
-				}
-
-				renderer.SetPosition(++idx, nextPoint);
-			}
+					thisColor = Color.yellow;
+				}
+			}
+
+			if (relay.KerbinDirect)
+			{
+				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+				relay = null;
+			}
+			else
+			{
+				if (relay.targetRelay == null)
+				{
+					renderer.enabled = false;
+					return;
+				}
+
+				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
+				relay = relay.targetRelay;
+			}
+
+			renderer.SetColors(thisColor, thisColor);
+
+			if (!MapView.Draw3DLines)
+			{
+				nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
+				nextPoint.z = nextPoint.z >= 0f ? d : -d;
+			}
+
+			idx++;
+
+			renderer.SetVertexCount(idx + 1);
+			renderer.SetPosition(idx, nextPoint);
+
+			log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart);
 		}
 
 		private void Cleanup()
@@ -286,11 +310,6 @@
 				}
 				this.vesselLineRenderers.Clear();
 			}
-
-			if (this.vesselFrameCache != null && this.vesselFrameCache.Count > 0)
-			{
-				this.vesselFrameCache.Clear();
-			}
 		}
 		#endregion
 	}

--- a/AntennaRange.csproj
+++ b/AntennaRange.csproj
@@ -80,6 +80,7 @@
     <Compile Include="ARConfiguration.cs" />
     <Compile Include="ARFlightController.cs" />
     <Compile Include="ARMapRenderer.cs" />
+    <Compile Include="VesselCache.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -28,7 +28,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange
@@ -38,8 +37,8 @@
 	/// </summary>
 	public class AntennaRelay
 	{
-		private static readonly System.Diagnostics.Stopwatch searchTimer = new System.Diagnostics.Stopwatch();
-		private const long millisecondsBetweenSearches = 125L;
+		public static readonly System.Diagnostics.Stopwatch searchTimer = new System.Diagnostics.Stopwatch();
+		public const long millisecondsBetweenSearches = 125L;
 
 		// We don't have a Bard, so we'll hide Kerbin here.
 		private static CelestialBody _Kerbin;
@@ -63,6 +62,7 @@
 		private long lastSearch;
 
 		private bool canTransmit;
+		private bool isChecked;
 
 		private IAntennaRelay nearestRelay;
 		private IAntennaRelay bestOccludedRelay;
@@ -111,7 +111,7 @@
 		{
 			get
 			{
-				this.FindNearestRelay();
+				//this.FindNearestRelay();
 
 				if (this.KerbinDirect || this.targetRelay == null)
 				{
@@ -159,7 +159,7 @@
 		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
 		public virtual bool CanTransmit()
 		{
-			this.FindNearestRelay();
+			//this.FindNearestRelay();
 			return this.canTransmit;
 		}
 
@@ -169,25 +169,14 @@
 		/// <returns>The nearest relay or null, if no relays in range.</returns>
 		private void FindNearestRelay()
 		{
+			if (!FlightGlobals.ready)
+			{
+				return;
+			}
+
 			if (!searchTimer.IsRunning)
 			{
 				searchTimer.Start();
-			}
-
-			long searchTime = searchTimer.ElapsedMilliseconds;
-			long timeSinceLast = searchTime - this.lastSearch;
-
-			if (timeSinceLast < Math.Max(millisecondsBetweenSearches, UnityEngine.Time.smoothDeltaTime))
-			{
-				return;
-			}
-
-			this.lastSearch = searchTime;
-
-			// Skip vessels that have already been checked for a nearest relay this pass.
-			if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(this.vessel.id))
-			{
-				return;
 			}
 
 			Tools.DebugLogger log;
@@ -195,11 +184,38 @@
 			log = Tools.DebugLogger.New(this);
 			#endif
 
+			long searchTime = searchTimer.ElapsedMilliseconds;
+			long timeSinceLast = searchTime - this.lastSearch;
+
+			if (timeSinceLast < millisecondsBetweenSearches)
+			{
+				log.AppendFormat(
+					"{0}: Target search skipped because it's not time to search again yet ({1} - {2}) < {3})",
+					this, searchTime, this.lastSearch, millisecondsBetweenSearches
+				);
+				log.Print();
+				return;
+			}
+
+			// Skip vessels that have already been checked for a nearest relay this pass.
+			if (this.isChecked)
+			{
+				log.AppendFormat("{0}: Target search skipped because our vessel has been checked already this search.",
+					this);
+				log.Print();
+				return;
+			}
+
 			log.AppendFormat("{0}: Target search started at {1} ms ({2} ms since last search).",
 				this.ToString(), searchTime, timeSinceLast);
-			
+
+			#if DEBUG
+			try {
+			#endif
 			// Set this vessel as checked, so that we don't check it again.
-			RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
+			this.isChecked = true;
+
+			this.lastSearch = searchTime;
 
 			// Blank everything we're trying to find before the search.
 			this.firstOccludingBody = null;
@@ -226,8 +242,24 @@
 			IList<IAntennaRelay> vesselRelays;
 			for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
 			{
+				log.AppendFormat("\nFetching vessel at index {0}", vIdx);
 				potentialVessel = FlightGlobals.Vessels[vIdx];
+				
+				if (potentialVessel == null)
+				{
+					log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx);
+					log.Print();
+					return;
+				}
+				#if DEBUG
+				else
+				{
+					log.AppendFormat("\n\tGot vessel {0}", potentialVessel);
+				}
+				#endif
+
 				// Skip vessels of the wrong type.
+				log.Append("\n\tchecking vessel type");
 				switch (potentialVessel.vesselType)
 				{
 					case VesselType.Debris:
@@ -235,43 +267,62 @@
 					case VesselType.EVA:
 					case VesselType.SpaceObject:
 					case VesselType.Unknown:
+							log.Append("\n\tSkipping because vessel is the wrong type.");
 						continue;
 					default:
 						break;
 				}
-
+				
+				log.Append("\n\tchecking if vessel is this vessel");
 				// Skip vessels with the wrong ID
 				if (potentialVessel.id == vessel.id)
 				{
+					log.Append("\n\tSkipping because vessel is this vessel.");
 					continue;
 				}
 
 				// Find the distance from here to the vessel...
+				log.Append("\n\tgetting distance to potential vessel");
 				double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
+				log.Append("\n\tgetting vessel relays");
 				vesselRelays = potentialVessel.GetAntennaRelays();
+					log.AppendFormat("\n\t\tvesselRelays: {0}",
+						vesselRelays == null ? "null" : vesselRelays.Count.ToString());
 
 				CelestialBody fob = null;
-
+				
+				log.Append("\n\tdoing LOS check");
 				// Skip vessels to which we do not have line of sight.
 				if (
 					ARConfiguration.RequireLineOfSight &&
 					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
 				)
 				{
+					log.Append("\n\tfailed LOS check");
 					this.firstOccludingBody = fob;
-
+					
 					log.AppendFormat("\n\t{0}: Vessel {1} not in line of sight.",
 						this.ToString(), potentialVessel.vesselName);
+					
+					log.AppendFormat("\n\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
+					log.AppendFormat("\n\t\tbestOccludedSqrDistance: {0}", bestOccludedSqrDistance);
+					log.AppendFormat("\n\t\tmaxTransmitSqrDistance: {0}", maxTransmitSqrDistance);
 
 					if (
 						(potentialSqrDistance < bestOccludedSqrDistance) &&
 						(potentialSqrDistance < maxTransmitSqrDistance)
 					)
 					{
-						
+						log.Append("\n\t\t...vessel is close enough to check for occluded relays");
+						log.AppendFormat("\n\t\tthis: {0}", this);
+						log.AppendFormat("\n\t\tpotentialVessel: {0}",
+							potentialVessel == null ? "null" : potentialVessel.ToString());
+						log.AppendFormat("\n\t\tvesselRelays: {0}",
+							vesselRelays == null ? "null" : vesselRelays.ToString());
+
 						log.AppendFormat("\n\t\t{0}: Checking {1} relays on occluded vessel {2}.",
 							this.ToString(),
-							potentialVessel.GetAntennaRelays().Count(),
+							vesselRelays.Count,
 							potentialVessel
 						);
 
@@ -303,9 +354,12 @@
 							}
 						}
 					}
-
+					
+					log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
 					continue;
 				}
+
+				log.Append("\n\tpassed LOS check");
 
 				/*
 				 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
@@ -320,25 +374,35 @@
 					continue;
 				}
 
+				log.Append("\n\tpassed distance check");
+
 				IAntennaRelay potentialRelay;
 				for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
 				{
+					log.AppendFormat("\n\t\tfetching vessel relay at index {0}", rIdx);
 					potentialRelay = vesselRelays[rIdx];
-
-					if (potentialRelay.CanTransmit() && potentialRelay.targetRelay != this)
+					log.AppendFormat("\n\t\tgot relay {0}", potentialRelay == null ? "null" : potentialRelay.ToString());
+
+					if (potentialRelay == null)
+					{
+						log.Append("\n\t\t...skipping null relay");
+						continue;
+					}
+
+					if (
+						potentialRelay.CanTransmit() &&
+						(potentialRelay.targetRelay == null || potentialRelay.targetRelay.vessel != this.vessel))
 					{
 						// @TODO: Moved this here from outside the loop; why was it there?
 						nearestRelaySqrDistance = potentialSqrDistance;
 						this.nearestRelay = potentialRelay;
 
-						if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-						{
-							log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
-								this.ToString(),
-								this.nearestRelay.ToString(),
-								Math.Sqrt(nearestRelaySqrDistance)
-							);
-						}
+						log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
+							this.ToString(),
+							this.nearestRelay.ToString(),
+							Math.Sqrt(nearestRelaySqrDistance)
+						);
+						
 						break;
 					}
 				}
@@ -564,12 +628,20 @@
 				this.ToString(), searchTimer.ElapsedMilliseconds, searchTimer.ElapsedMilliseconds - searchTime);;
 
 			log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
-
+			
+			#if DEBUG
+			} catch (Exception ex) {
+				log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
+				UnityEngine.Application.Quit();
+			} finally {
+			#endif
 			log.Print(false);
-
+			#if DEBUG
+			}
+			#endif
 			// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
 			// used next time.
-			RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
+			this.isChecked = false;
 		}
 
 		/// <summary>
@@ -593,6 +665,7 @@
 		public AntennaRelay(IAntennaRelay module)
 		{
 			this.moduleRef = module;
+			this.isChecked = false;
 		}
 	}
 }

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -29,7 +29,6 @@
 using KSP;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Text;
 using ToadicusTools;
 using UnityEngine;

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -28,7 +28,6 @@
 
 using KSP;
 using System;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange

--- a/RelayDatabase.cs
+++ b/RelayDatabase.cs
@@ -71,6 +71,8 @@
 		// Vessel.id-keyed hash table of part counts, used for caching
 		protected Dictionary<Guid, int> vesselPartCountTable;
 
+		protected Dictionary<Guid, BestVesselRelayCache> bestRelayTable;
+
 		// Vessel.id-keyed hash table of booleans to track what vessels have been checked so far this time.
 		public Dictionary<Guid, bool> CheckedVesselsTable;
 
@@ -178,6 +180,29 @@
 			{
 				this.vesselPartCountTable.Remove(vessel.id);
 			}
+		}
+
+		public void SetBestVesselRelay(Vessel vessel, BestVesselRelayCache relay)
+		{
+			this.bestRelayTable[vessel.id] = relay;
+		}
+
+		public bool TryGetBestVesselRelay(Vessel vessel, out BestVesselRelayCache relay)
+		{
+			return this.bestRelayTable.TryGetValue(vessel.id, out relay);
+		}
+
+		public BestVesselRelayCache GetBestVesselRelay(Vessel vessel)
+		{
+			BestVesselRelayCache relayCache;
+			if (this.TryGetBestVesselRelay(vessel, out relayCache))
+			{
+				return relayCache;
+			}
+
+			throw new ArgumentOutOfRangeException(
+				string.Format("RelayDatabase: Vessel {0} not in best vessel relay table.", vessel)
+			);
 		}
 
 		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
@@ -362,6 +387,7 @@
 			this.relayDatabase = new Dictionary<Guid, List<IAntennaRelay>>();
 			this.vesselPartCountTable = new Dictionary<Guid, int>();
 			this.CheckedVesselsTable = new Dictionary<Guid, bool>();
+			this.bestRelayTable = new Dictionary<Guid, BestVesselRelayCache>();
 
 			this.cacheHits = 0;
 			this.cacheMisses = 0;
@@ -422,6 +448,21 @@
 		}
 		#endif
 	}
+
+	public class BestVesselRelayCache
+	{
+		public Vessel vessel;
+		public IAntennaRelay relay;
+		public long timeStamp;
+
+		public BestVesselRelayCache() {}
+		public BestVesselRelayCache(Vessel _vessel, IAntennaRelay _relay, long _timeStamp)
+		{
+			this.vessel = _vessel;
+			this.relay = _relay;
+			this.timeStamp = _timeStamp;
+		}
+	}
 }
 
 

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -28,7 +28,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange
@@ -167,7 +166,25 @@
 		/// <param name="vessel">This <see cref="Vessel"/></param>
 		public static IAntennaRelay GetBestRelay(this Vessel vessel)
 		{
-			IAntennaRelay bestRelay = null;
+			BestVesselRelayCache relayCache;
+
+			if (RelayDatabase.Instance.TryGetBestVesselRelay(vessel, out relayCache))
+			{
+				if (
+					AntennaRelay.searchTimer.ElapsedMilliseconds - relayCache.timeStamp <
+					AntennaRelay.millisecondsBetweenSearches
+				)
+				{
+					return relayCache.relay;
+				}
+			}
+			else
+			{
+				relayCache = new BestVesselRelayCache();
+				relayCache.vessel = vessel;
+				RelayDatabase.Instance.SetBestVesselRelay(vessel, relayCache);
+			}
+
 			double bestScore = double.PositiveInfinity;
 			double relayScore = double.NaN;
 
@@ -181,11 +198,13 @@
 				if (relayScore < bestScore)
 				{
 					bestScore = relayScore;
-					bestRelay = relay;
-				}
-			}
-
-			return bestRelay;
+					relayCache.relay = relay;
+				}
+			}
+
+			relayCache.timeStamp = AntennaRelay.searchTimer.ElapsedMilliseconds;
+
+			return relayCache.relay;
 		}
 	}
 

file:b/VesselCache.cs (new)
--- /dev/null
+++ b/VesselCache.cs
@@ -1,1 +1,16 @@
+// AntennaRange © 2015 toadicus
+//
+// This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a
+// copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/
+using System;
 
+namespace AntennaRange
+{
+	public class Relay
+	{
+		public Vessel vessel;
+		public IAntennaRelay targetRelay;
+	}
+}
+
+