AntennaRelay: Formalized the circular network check to check for circles of arbitrary length.
AntennaRelay: Formalized the circular network check to check for circles of arbitrary length.

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -40,27 +40,27 @@
 	public class ARFlightController : MonoBehaviour
 	{
 		#region Fields
-		protected Dictionary<ConnectionStatus, string> connectionTextures;
-		protected Dictionary<ConnectionStatus, Texture> appLauncherTextures;
-
-		protected ARMapRenderer mapRenderer;
-
-		protected IButton toolbarButton;
-
-		protected ApplicationLauncherButton appLauncherButton;
-		protected Tools.DebugLogger log;
-
-		protected System.Diagnostics.Stopwatch updateTimer;
+		private Dictionary<ConnectionStatus, string> connectionTextures;
+		private Dictionary<ConnectionStatus, Texture> appLauncherTextures;
+
+		private ARMapRenderer mapRenderer;
+
+		private IButton toolbarButton;
+
+		private ApplicationLauncherButton appLauncherButton;
+		private Tools.DebugLogger log;
+
+		private System.Diagnostics.Stopwatch updateTimer;
 		#endregion
 
 		#region Properties
 		public ConnectionStatus currentConnectionStatus
 		{
 			get;
-			protected set;
-		}
-
-		protected string currentConnectionTexture
+			private set;
+		}
+
+		private string currentConnectionTexture
 		{
 			get
 			{
@@ -68,7 +68,7 @@
 			}
 		}
 
-		protected Texture currentAppLauncherTexture
+		private Texture currentAppLauncherTexture
 		{
 			get
 			{
@@ -92,7 +92,7 @@
 		public string lockID
 		{
 			get;
-			protected set;
+			private set;
 		}
 
 		public ControlTypes lockSet
@@ -118,7 +118,7 @@
 		#endregion
 
 		#region MonoBehaviour LifeCycle
-		protected void Awake()
+		private void Awake()
 		{
 			this.lockID = "ARConnectionRequired";
 
@@ -155,12 +155,12 @@
 			GameEvents.onVesselChange.Add(this.onVesselChange);
 		}
 
-		protected void Start()
+		private void Start()
 		{
 			this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
 		}
 
-		protected void FixedUpdate()
+		private void FixedUpdate()
 		{
 			if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
 			{
@@ -222,11 +222,11 @@
 			log.Print();
 		}
 
-		protected void Update()
-		{
-			if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > 125L)
-			{
-				this.updateTimer.Reset();
+		private void Update()
+		{
+			if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > 83L)
+			{
+				this.updateTimer.Restart();
 			}
 			else
 			{
@@ -245,15 +245,19 @@
 				{
 					vessel = FlightGlobals.Vessels[vIdx];
 
-					if (vessel == FlightGlobals.ActiveVessel)
+					if (vessel == null || vessel == FlightGlobals.ActiveVessel)
 					{
 						continue;
 					}
 
+					log.AppendFormat("Fetching best relay for vessel {0}", vessel);
+
 					relay = vessel.GetBestRelay();
 
 					if (relay != null)
 					{
+						log.AppendFormat("Finding nearest relay for best relay {0}", relay);
+
 						relay.FindNearestRelay();
 					}
 				}
@@ -298,7 +302,7 @@
 			log.Print();
 		}
 
-		protected void OnDestroy()
+		private void OnDestroy()
 		{
 			InputLockManager.RemoveControlLock(this.lockID);
 
@@ -326,13 +330,13 @@
 		#endregion
 
 		#region Event Handlers
-		protected void onSceneChangeRequested(GameScenes scene)
+		private void onSceneChangeRequested(GameScenes scene)
 		{
 			print("ARFlightController: Requesting Destruction.");
 			MonoBehaviour.Destroy(this);
 		}
 
-		protected void onVesselChange(Vessel vessel)
+		private void onVesselChange(Vessel vessel)
 		{
 			InputLockManager.RemoveControlLock(this.lockID);
 		}

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -167,6 +167,7 @@
 
 						if (vesselRelay == null)
 						{
+							log.AppendFormat("\n\tGot null relay for vessel {0}", vessel.vesselName);
 							continue;
 						}
 
@@ -206,7 +207,7 @@
 		{
 			this.Cleanup();
 
-			print("ARMapRenderer: Destroyed.");
+			this.Log("Destroyed");
 		}
 		#endregion
 
@@ -273,18 +274,21 @@
 			if (relay.KerbinDirect)
 			{
 				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
-				relay = null;
 			}
 			else
 			{
-				if (relay.targetRelay == null)
-				{
+				if (relay.targetRelay == null || relay.targetRelay.vessel == null)
+				{
+					this.LogError(
+						"SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!",
+						relay
+					);
+
 					renderer.enabled = false;
 					return;
 				}
 
 				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
-				relay = relay.targetRelay;
 			}
 
 			renderer.SetColors(thisColor, thisColor);

--- a/AntennaRelay.cs
+++ b/AntennaRelay.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
+
 using System;
 using System.Collections.Generic;
 using ToadicusTools;
@@ -37,9 +39,6 @@
 	/// </summary>
 	public class AntennaRelay
 	{
-		private static readonly System.Diagnostics.Stopwatch searchTimer = new System.Diagnostics.Stopwatch();
-		private const long millisecondsBetweenSearches = 125L;
-
 		// We don't have a Bard, so we'll hide Kerbin here.
 		private static CelestialBody _Kerbin;
 
@@ -59,8 +58,6 @@
 			}
 		}
 
-		private long lastSearch;
-
 		private bool canTransmit;
 		private bool isChecked;
 
@@ -169,11 +166,6 @@
 			if (!FlightGlobals.ready)
 			{
 				return;
-			}
-
-			if (!searchTimer.IsRunning)
-			{
-				searchTimer.Start();
 			}
 
 			Tools.DebugLogger log;
@@ -181,19 +173,6 @@
 			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)
 			{
@@ -203,8 +182,7 @@
 				return;
 			}
 
-			log.AppendFormat("{0}: Target search started at {1} ms ({2} ms since last search).",
-				this.ToString(), searchTime, timeSinceLast);
+			log.AppendFormat("{0}: Target search started).", this.ToString());
 
 			#if DEBUG
 			try {
@@ -212,8 +190,6 @@
 			// Set this vessel as checked, so that we don't check it again.
 			this.isChecked = true;
 
-			this.lastSearch = searchTime;
-
 			// Blank everything we're trying to find before the search.
 			this.firstOccludingBody = null;
 			this.bestOccludedRelay = null;
@@ -224,6 +200,7 @@
 			this.KerbinDirect = true;
 
 			CelestialBody bodyOccludingBestOccludedRelay = null;
+			IAntennaRelay needle;
 
 			double nearestRelaySqrDistance = double.PositiveInfinity;
 			double bestOccludedSqrDistance = double.PositiveInfinity;
@@ -236,7 +213,10 @@
 			 * against future finds.
 			 * */
 			Vessel potentialVessel;
-			IList<IAntennaRelay> vesselRelays;
+			IAntennaRelay potentialBestRelay;
+			CelestialBody fob;
+
+			// IList<IAntennaRelay> vesselRelays;
 			for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
 			{
 				log.AppendFormat("\nFetching vessel at index {0}", vIdx);
@@ -244,6 +224,7 @@
 				
 				if (potentialVessel == null)
 				{
+					Tools.PostErrorMessage("{0}: Skipping vessel at index {1} because it is null.", this, vIdx);
 					log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx);
 					log.Print();
 					return;
@@ -281,82 +262,56 @@
 				// 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");
+				log.Append("\n\tgetting best vessel relay");
+
+				potentialBestRelay = potentialVessel.GetBestRelay();
+				log.AppendFormat("\n\t\tgot best vessel relay {0}",
+					potentialBestRelay == null ? "null" : potentialBestRelay.ToString());
+
+				if (potentialBestRelay == null)
+				{
+					log.Append("\n\t\t...skipping null relay");
+					continue;
+				}
+
+				log.Append("\n\t\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.",
+					log.Append("\n\t\t...failed LOS check");
+
+					log.AppendFormat("\n\t\t\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);
+					log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
+					log.AppendFormat("\n\t\t\tbestOccludedSqrDistance: {0}", bestOccludedSqrDistance);
+					log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxTransmitSqrDistance);
 
 					if (
 						(potentialSqrDistance < bestOccludedSqrDistance) &&
-						(potentialSqrDistance < maxTransmitSqrDistance)
+						(potentialSqrDistance < maxTransmitSqrDistance) &&
+						potentialBestRelay.CanTransmit()
 					)
 					{
-						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(),
-							vesselRelays.Count,
-							potentialVessel
-						);
-
-						IAntennaRelay occludedRelay;
-						for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
-						{
-							occludedRelay = vesselRelays[rIdx];
-
-							log.AppendFormat(
-								"\n\t\t{0}: Checking candidate for bestOccludedRelay: {1}" +
-								"\n\t\tCanTransmit: {2}",
-								this.ToString(), occludedRelay, occludedRelay.CanTransmit()
-							);
-							
-							if (occludedRelay.CanTransmit())
-							{
-								this.bestOccludedRelay = occludedRelay;
-								bodyOccludingBestOccludedRelay = fob;
-								bestOccludedSqrDistance = potentialSqrDistance;
-
-								log.AppendFormat("\n\t{0}: Found new bestOccludedRelay: {1}" +
-									" (blocked by {2}; distance: {3} m)",
-									this.ToString(),
-									occludedRelay.ToString(),
-									fob,
-									potentialSqrDistance
-								);
-								break;
-							}
-						}
+						log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit");
+						log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay);
+
+						this.bestOccludedRelay = potentialBestRelay;
+						bodyOccludingBestOccludedRelay = fob;
+						bestOccludedSqrDistance = potentialSqrDistance;
+					}
+					else
+					{
+						log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
 					}
 					
-					log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
 					continue;
 				}
 
-				log.Append("\n\tpassed LOS check");
+				log.Append("\n\t\t...passed LOS check");
 
 				/*
 				 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
@@ -371,36 +326,50 @@
 					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];
-					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?
+				log.Append("\n\t\t...passed distance check");
+
+				if (potentialBestRelay.CanTransmit())
+				{
+					needle = potentialBestRelay;
+					bool isCircular = false;
+
+					while (needle != null)
+					{
+						if (needle.KerbinDirect)
+						{
+							break;
+						}
+
+						if (needle.targetRelay == null)
+						{
+							break;
+						}
+
+						if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
+						{
+							isCircular = true;
+							break;
+						}
+
+						needle = needle.targetRelay;
+					}
+
+					if (!isCircular)
+					{
 						nearestRelaySqrDistance = potentialSqrDistance;
-						this.nearestRelay = potentialRelay;
+						this.nearestRelay = potentialBestRelay;
 
 						log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
 							this.ToString(),
 							this.nearestRelay.ToString(),
 							Math.Sqrt(nearestRelaySqrDistance)
 						);
-						
-						break;
+					}
+					else
+					{
+						log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping",
+							potentialBestRelay
+						);
 					}
 				}
 			}
@@ -411,6 +380,15 @@
 			kerbinSqrDistance *= kerbinSqrDistance;
 
 			log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
+			log.AppendFormat(
+				"\n{0}: nearestRelay={1} ({2}m²)), bestOccludedRelay={3} ({4}m²), kerbinSqrDistance={5}m²)",
+				this,
+				this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+				nearestRelaySqrDistance,
+				this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
+				bestOccludedSqrDistance,
+				kerbinSqrDistance
+			);
 
 			// If we don't have LOS to Kerbin, focus on relays
 			if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio))
@@ -454,7 +432,7 @@
 							log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
 								nearestRelaySqrDistance, bestOccludedSqrDistance);
 							
-							this.targetRelay = nearestRelay;
+							this.targetRelay = this.nearestRelay;
 							this.firstOccludingBody = null;
 						}
 						// Otherwise, target the best occluded relay.
@@ -484,7 +462,7 @@
 								nearestRelaySqrDistance, kerbinSqrDistance);
 							
 							this.KerbinDirect = false;
-							this.targetRelay = nearestRelay;
+							this.targetRelay = this.nearestRelay;
 						}
 						// Otherwise, pick Kerbin.
 						else
@@ -575,7 +553,7 @@
 								log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
 									nearestRelaySqrDistance, bestOccludedSqrDistance);
 								
-								this.targetRelay = nearestRelay;
+								this.targetRelay = this.nearestRelay;
 								this.firstOccludingBody = null;
 							}
 							// Otherwise, target the best occluded relay.
@@ -605,7 +583,7 @@
 									nearestRelaySqrDistance, kerbinSqrDistance);
 								
 								this.KerbinDirect = false;
-								this.targetRelay = nearestRelay;
+								this.targetRelay = this.nearestRelay;
 							}
 							// Otherwise, pick Kerbin.
 							else
@@ -621,10 +599,7 @@
 				}
 			}
 
-			log.AppendFormat("{0}: Target search completed at {1} ms ({2} ms elapsed).",
-				this.ToString(), searchTimer.ElapsedMilliseconds, searchTimer.ElapsedMilliseconds - searchTime);;
-
-			log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
+			log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());
 			
 			#if DEBUG
 			} catch (Exception ex) {

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -83,6 +83,9 @@
 		/// </summary>
 		bool CanTransmit();
 
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
 		void FindNearestRelay();
 
 		/// <summary>

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -145,13 +145,13 @@
 				{
 					return base.vessel;
 				}
-				else if (this.part != null)
+				else if (this.part != null && this.part.vessel != null)
 				{
 					return this.part.vessel;
 				}
-
 				else
 				{
+					this.LogError("Vessel and/or part reference are null, returning null vessel.");
 					return null;
 				}
 			}
@@ -436,6 +436,9 @@
 			return this.relay.CanTransmit();
 		}
 
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
 		public void FindNearestRelay()
 		{
 			if (this.relay != null)
@@ -452,6 +455,11 @@
 		/// <param name="callback">Callback function</param>
 		public new void TransmitData(List<ScienceData> dataQueue, Callback callback)
 		{
+			this.LogDebug(
+				"TransmitData(List<ScienceData> dataQueue, Callback callback) called.  dataQueue.Count={0}",
+				dataQueue.Count
+			);
+
 			this.FindNearestRelay();
 
 			this.PreTransmit_SetPacketSize();
@@ -461,7 +469,20 @@
 			{
 				ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
 
-				base.TransmitData(dataQueue, callback);
+				this.LogDebug(
+					"CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}",
+					dataQueue.SPrint(),
+					callback == null ? "null" : callback.ToString()
+				);
+
+				if (callback == null)
+				{
+					base.TransmitData(dataQueue);
+				}
+				else
+				{
+					base.TransmitData(dataQueue, callback);
+				}
 			}
 			else
 			{
@@ -523,23 +544,25 @@
 
 				if (dataQueue.Count > 0)
 				{
-					StringBuilder msg = new StringBuilder();
-
-					msg.Append('[');
-					msg.Append(this.part.partInfo.title);
-					msg.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
-					msg.Append("Data to be discarded:\n");
+					StringBuilder sb = Tools.GetStringBuilder();
+
+					sb.Append('[');
+					sb.Append(this.part.partInfo.title);
+					sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
+					sb.Append("Data to be discarded:\n");
 
 					ScienceData data;
 					for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
 					{
 						data = dataQueue[dIdx];
-						msg.AppendFormat("\t{0}\n", data.title);
+						sb.AppendFormat("\t{0}\n", data.title);
 					}
 
-					ScreenMessages.PostScreenMessage(msg.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
-
-					Tools.PostDebugMessage(msg.ToString());
+					ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
+					Tools.PostDebugMessage(sb.ToString());
+
+					Tools.PutStringBuilder(sb);
 				}
 
 				this.PostCannotTransmitError();
@@ -559,6 +582,11 @@
 		/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
 		public new void TransmitData(List<ScienceData> dataQueue)
 		{
+			this.LogDebug(
+				"TransmitData(List<ScienceData> dataQueue) called, dataQueue.Count={0}",
+				dataQueue.Count
+			);
+
 			this.TransmitData(dataQueue, null);
 		}
 
@@ -637,14 +665,15 @@
 		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns>
 		public override string ToString()
 		{
-			StringBuilder msg = new StringBuilder();
-
-			msg.Append(this.part.partInfo.title);
+			StringBuilder sb = Tools.GetStringBuilder();
+			string msg;
+
+			sb.Append(this.part.partInfo.title);
 
 			if (vessel != null)
 			{
-				msg.Append(" on ");
-				msg.Append(vessel.vesselName);
+				sb.Append(" on ");
+				sb.Append(vessel.vesselName);
 			}
 			else if (
 				this.part != null &&
@@ -653,11 +682,15 @@
 				this.part.protoPartSnapshot.pVesselRef != null
 			)
 			{
-				msg.Append(" on ");
-				msg.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
-			}
-
-			return msg.ToString();
+				sb.Append(" on ");
+				sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
+			}
+
+			msg = sb.ToString();
+
+			Tools.PutStringBuilder(sb);
+
+			return msg;
 		}
 
 		// When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty.
@@ -714,12 +747,6 @@
 
 				base.packetResourceCost = this._basepacketResourceCost
 					* rangeFactor;
-
-				Tools.PostDebugMessage(
-					this,
-					"Pretransmit: packet cost set to {0} before throttle (rangeFactor = {1}).",
-					base.packetResourceCost,
-					rangeFactor);
 			}
 
 			base.packetResourceCost *= this.packetThrottle / 100f;
@@ -741,12 +768,6 @@
 				base.packetSize = Mathf.Min(
 					this._basepacketSize * rangeFactor,
 					this._basepacketSize * this.maxDataFactor);
-
-				Tools.PostDebugMessage(
-					this,
-					"Pretransmit: packet size set to {0} before throttle (rangeFactor = {1}).",
-					base.packetSize,
-					rangeFactor);
 			}
 
 			base.packetSize *= this.packetThrottle / 100f;
@@ -754,25 +775,30 @@
 
 		private string buildTransmitMessage()
 		{
-			StringBuilder message = new StringBuilder();
-
-			message.Append("[");
-			message.Append(base.part.partInfo.title);
-			message.Append("]: ");
-
-			message.Append("Beginning transmission ");
+			StringBuilder sb = Tools.GetStringBuilder();
+			string msg;
+
+			sb.Append("[");
+			sb.Append(base.part.partInfo.title);
+			sb.Append("]: ");
+
+			sb.Append("Beginning transmission ");
 
 			if (this.KerbinDirect)
 			{
-				message.Append("directly to Kerbin.");
+				sb.Append("directly to Kerbin.");
 			}
 			else
 			{
-				message.Append("via ");
-				message.Append(this.relay.targetRelay);
-			}
-
-			return message.ToString();
+				sb.Append("via ");
+				sb.Append(this.relay.targetRelay);
+			}
+
+			msg = sb.ToString();
+
+			Tools.PutStringBuilder(sb);
+
+			return msg;
 		}
 
 		#if DEBUG
@@ -783,47 +809,14 @@
 			PreTransmit_SetPacketSize ();
 			PreTransmit_SetPacketResourceCost ();
 
-			string msg = string.Format(
-				"'{0}'\n" + 
-				"_basepacketSize: {1}\n" +
-				"packetSize: {2}\n" +
-				"_basepacketResourceCost: {3}\n" +
-				"packetResourceCost: {4}\n" +
-				"maxTransmitDistance: {5}\n" +
-				"transmitDistance: {6}\n" +
-				"nominalRange: {7}\n" +
-				"CanTransmit: {8}\n" +
-				"DataRate: {9}\n" +
-				"DataResourceCost: {10}\n" +
-				"TransmitterScore: {11}\n" +
-				"targetRelay: {12}\n" +
-				"KerbinDirect: {13}\n" +
-				"Vessel ID: {14}",
-				this.name,
-				this._basepacketSize,
-				base.packetSize,
-				this._basepacketResourceCost,
-				base.packetResourceCost,
-				this.maxTransmitDistance,
-				this.transmitDistance,
-				this.nominalRange,
-				this.CanTransmit(),
-				this.DataRate,
-				this.DataResourceCost,
-				ScienceUtil.GetTransmitterScore(this),
-				this.relay.targetRelay == null ? "null" : this.relay.targetRelay.ToString(),
-				this.KerbinDirect,
-				this.vessel.id
-				);
-
-			Tools.PostLogMessage(msg);
+			DebugPartModule.DumpClassObject(this);
 		}
 
 		[KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)]
 		public void PrintAllVessels()
 		{
-			StringBuilder sb = new StringBuilder();
-
+			StringBuilder sb = Tools.GetStringBuilder();
+			
 			sb.Append("Dumping FlightGlobals.Vessels:");
 
 			Vessel vessel;
@@ -832,8 +825,10 @@
 				vessel = FlightGlobals.Vessels[i];
 				sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id);
 			}
-
+		    
 			Tools.PostDebugMessage(sb.ToString());
+
+			Tools.PutStringBuilder(sb);
 		}
 		 
 		[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)]

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -48,17 +48,24 @@
 		{
 			get
 			{
-				if (this.protoPart != null && this.protoPart.pVesselRef != null)
+				if (
+					this.protoPart != null &&
+					this.protoPart.pVesselRef != null &&
+					this.protoPart.pVesselRef.vesselRef != null
+				)
 				{
 					return this.protoPart.pVesselRef.vesselRef;
 				}
 				else
 				{
-					Tools.PostLogMessage("{0}: Could not fetch vessel!  {1}{2}{3}",
+					Tools.PostErrorMessage("{0}: Could not fetch vessel!  {1}{2}{3}",
 						this.ToString(),
-						this.protoPart == null ? "\n\tprotoPart=Null" : string.Empty,
-						this.protoPart != null && this.protoPart.pVesselRef == null ? "\n\tthis.protoPart.pVesselRef=Null" : string.Empty,
-						this.protoPart != null && this.protoPart.pVesselRef != null && this.protoPart.pVesselRef.vesselRef == null ? "\n\tthis.protoPart.pVesselRef.vesselRef=Null" : string.Empty
+						this.protoPart == null ? "\n\tprotoPart=null" : string.Empty,
+						this.protoPart != null && this.protoPart.pVesselRef == null ?
+							"\n\tthis.protoPart.pVesselRef=null" : string.Empty,
+						this.protoPart != null && this.protoPart.pVesselRef != null &&
+							this.protoPart.pVesselRef.vesselRef == null ?
+							"\n\tthis.protoPart.pVesselRef.vesselRef=null" : string.Empty
 					);
 					return null;
 				}
@@ -131,7 +138,7 @@
 		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns>
 		public override string ToString()
 		{
-			System.Text.StringBuilder sb = new System.Text.StringBuilder();
+			System.Text.StringBuilder sb = Tools.GetStringBuilder();
 
 			sb.Append(this.Title);
 
@@ -139,6 +146,8 @@
 			{
 				sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
 			}
+
+			Tools.PutStringBuilder(sb);
 
 			return sb.ToString();
 		}

--- a/RelayDatabase.cs
+++ b/RelayDatabase.cs
@@ -72,9 +72,6 @@
 		// Vessel.id-keyed hash table of part counts, used for caching
 		private Dictionary<Guid, int> vesselPartCountTable;
 
-		// Vessel.id-keyed hash table of booleans to track what vessels have been checked so far this time.
-		public Dictionary<Guid, bool> CheckedVesselsTable;
-
 		private int cacheHits;
 		private int cacheMisses;
 
@@ -117,9 +114,25 @@
 		// Remove a vessel from the cache, if it exists.
 		public void DirtyVessel(Vessel vessel)
 		{
+			#if DEBUG
+			Tools.PostDebugMessage("RelayDatabase: Dirtying cache for vessel {0} in frame {1}",
+				vessel, new System.Diagnostics.StackTrace().ToString());
+			#else
+			Tools.PostLogMessage("RelayDatabase: Dirtying cache for vessel {0}", vessel.vesselName);
+			#endif
+
 			this.relayDatabase.Remove(vessel.id);
 			this.vesselPartCountTable.Remove(vessel.id);
-			this.relayDatabase.Remove(vessel.id);
+			this.bestRelayTable.Remove(vessel.id);
+		}
+
+		public void ClearCache()
+		{
+			Tools.PostLogMessage("RelayDatabase: onSceneChange clearing entire cache.");
+
+			this.relayDatabase.Clear();
+			this.bestRelayTable.Clear();
+			this.vesselPartCountTable.Clear();
 		}
 
 		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
@@ -228,12 +241,35 @@
 		// Runs when the player requests a scene change, such as when changing vessels or leaving flight.
 		private void onSceneChange(GameScenes scene)
 		{
-			// If the active vessel is a real thing...
-			if (FlightGlobals.ActiveVessel != null)
-			{
-				// ... dirty its cache
-				this.onVesselEvent(FlightGlobals.ActiveVessel);
-			}
+			Tools.PostDebugMessage(
+				"RelayDatabase: caught onSceneChangeRequested in scene {0} to scene {1}.  ActiveVessel is {2}",
+				HighLogic.LoadedScene,
+				scene,
+				FlightGlobals.ActiveVessel == null ? "null" : FlightGlobals.ActiveVessel.vesselName
+			);
+
+			if (scene == GameScenes.FLIGHT)
+			{
+				if (scene == HighLogic.LoadedScene)
+				{
+					if (FlightGlobals.ActiveVessel != null)
+					{
+						Tools.PostDebugMessage("RelayDatabase: onSceneChange clearing {0} from cache.",
+							FlightGlobals.ActiveVessel.vesselName);
+
+						this.onVesselEvent(FlightGlobals.ActiveVessel);
+					}
+				}
+				else
+				{
+					this.ClearCache();
+				}
+			}
+		}
+
+		private void onGameLoaded(object data)
+		{
+			this.ClearCache();
 		}
 
 		// Runs when parts are undocked
@@ -394,7 +430,6 @@
 			this.relayDatabase = new Dictionary<Guid, List<IAntennaRelay>>();
 			this.bestRelayTable = new Dictionary<Guid, IAntennaRelay>();
 			this.vesselPartCountTable = new Dictionary<Guid, int>();
-			this.CheckedVesselsTable = new Dictionary<Guid, bool>();
 
 			this.cacheHits = 0;
 			this.cacheMisses = 0;
@@ -406,6 +441,7 @@
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange);
 			GameEvents.onPartCouple.Add(this.onFromPartToPartEvent);
 			GameEvents.onPartUndock.Add(this.onPartEvent);
+			GameEvents.onGameStateLoad.Add(this.onGameLoaded);
 		}
 
 		~RelayDatabase()
@@ -417,6 +453,7 @@
 			GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange);
 			GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent);
 			GameEvents.onPartUndock.Remove(this.onPartEvent);
+			GameEvents.onGameStateLoad.Remove(this.onGameLoaded);
 
 			Tools.PostDebugMessage(this.GetType().Name + " destroyed.");
 
@@ -432,7 +469,7 @@
 		#if DEBUG
 		public void Dump()
 		{
-			StringBuilder sb = new StringBuilder();
+			StringBuilder sb = Tools.GetStringBuilder();
 
 			sb.Append("Dumping RelayDatabase:");
 
@@ -452,6 +489,8 @@
 			}
 
 			Tools.PostDebugMessage(sb.ToString());
+
+			Tools.PutStringBuilder(sb);
 		}
 		#endif
 	}