ARFlightController: Renamed connectionTextures->toolbarTexturse to more aptly summarize its purpose.
ARFlightController: Renamed connectionTextures->toolbarTexturse to more aptly summarize its purpose.

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -2,7 +2,7 @@
 //
 // ARFlightController.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -40,35 +40,35 @@
 	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> toolbarTextures;
+		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
-		{
-			get
-			{
-				return this.connectionTextures[this.currentConnectionStatus];
-			}
-		}
-
-		protected Texture currentAppLauncherTexture
+			private set;
+		}
+
+		private string currentConnectionTexture
+		{
+			get
+			{
+				return this.toolbarTextures[this.currentConnectionStatus];
+			}
+		}
+
+		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";
 
@@ -126,11 +126,11 @@
 
 			this.updateTimer = new System.Diagnostics.Stopwatch();
 
-			this.connectionTextures = new Dictionary<ConnectionStatus, string>();
-
-			this.connectionTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection";
-			this.connectionTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal";
-			this.connectionTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon";
+			this.toolbarTextures = new Dictionary<ConnectionStatus, string>();
+
+			this.toolbarTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection";
+			this.toolbarTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal";
+			this.toolbarTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon";
 
 			this.appLauncherTextures = new Dictionary<ConnectionStatus, Texture>();
 
@@ -145,7 +145,7 @@
 			{
 				this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus");
 
-				this.toolbarButton.TexturePath = this.connectionTextures[ConnectionStatus.None];
+				this.toolbarButton.TexturePath = this.toolbarTextures[ConnectionStatus.None];
 				this.toolbarButton.Text = "AntennaRange";
 				this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT);
 				this.toolbarButton.Enabled = false;
@@ -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 > 16L)
+			{
+				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
@@ -2,7 +2,7 @@
 //
 // ARMapRenderer.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -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
@@ -2,7 +2,7 @@
 //
 // AntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -37,9 +37,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 +56,6 @@
 			}
 		}
 
-		private long lastSearch;
-
 		private bool canTransmit;
 		private bool isChecked;
 
@@ -169,11 +164,6 @@
 			if (!FlightGlobals.ready)
 			{
 				return;
-			}
-
-			if (!searchTimer.IsRunning)
-			{
-				searchTimer.Start();
 			}
 
 			Tools.DebugLogger log;
@@ -181,19 +171,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 +180,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 +188,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 +198,7 @@
 			this.KerbinDirect = true;
 
 			CelestialBody bodyOccludingBestOccludedRelay = null;
+			IAntennaRelay needle;
 
 			double nearestRelaySqrDistance = double.PositiveInfinity;
 			double bestOccludedSqrDistance = double.PositiveInfinity;
@@ -236,7 +211,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 +222,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 +260,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 +324,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 +378,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 +430,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 +460,7 @@
 								nearestRelaySqrDistance, kerbinSqrDistance);
 							
 							this.KerbinDirect = false;
-							this.targetRelay = nearestRelay;
+							this.targetRelay = this.nearestRelay;
 						}
 						// Otherwise, pick Kerbin.
 						else
@@ -575,7 +551,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 +581,7 @@
 									nearestRelaySqrDistance, kerbinSqrDistance);
 								
 								this.KerbinDirect = false;
-								this.targetRelay = nearestRelay;
+								this.targetRelay = this.nearestRelay;
 							}
 							// Otherwise, pick Kerbin.
 							else
@@ -621,10 +597,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/GameData/AntennaRange/AntennaRange.cfg
+++ b/GameData/AntennaRange/AntennaRange.cfg
@@ -2,7 +2,7 @@
 //
 // AntennaRange.cfg
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -2,7 +2,7 @@
 //
 // IAntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -2,7 +2,7 @@
 //
 // ModuleLimitedDataTransmitter.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -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;
 				}
 			}
@@ -205,11 +205,8 @@
 		/// </summary>
 		public double maxTransmitDistance
 		{
-			get
-			{
-				// TODO: Cache this in a way that doesn't break everything.
-				return Math.Sqrt(this.maxPowerFactor) * this.nominalRange;
-			}
+			get;
+			protected set;
 		}
 
 		/// <summary>
@@ -393,6 +390,8 @@
 			base.Fields.Load(node);
 
 			base.OnLoad (node);
+
+			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalRange;
 		}
 
 		/// <summary>
@@ -544,23 +543,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();
@@ -663,14 +664,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 &&
@@ -679,11 +681,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.
@@ -768,25 +774,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
@@ -797,47 +808,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;
@@ -846,8 +824,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/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
 //
 // AssemblyInfo.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -2,7 +2,7 @@
 //
 // ProtoAntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -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
@@ -2,7 +2,7 @@
 //
 // RelayDatabase.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -37,27 +37,8 @@
 
 namespace AntennaRange
 {
-	public class RelayDatabase
+	public class RelayDatabase : Singleton<RelayDatabase>
 	{
-		/*
-		 * Static members
-		 * */
-		// Singleton storage
-		private static RelayDatabase _instance;
-		// Gets the singleton
-		public static RelayDatabase Instance
-		{
-			get
-			{	
-				if (_instance == null)
-				{
-					_instance = new RelayDatabase();
-				}
-
-				return _instance;
-			}
-		}
-
 		/*
 		 * Instance members
 		 * */
@@ -72,9 +53,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 +95,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 +222,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 +411,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 +422,7 @@
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange);
 			GameEvents.onPartCouple.Add(this.onFromPartToPartEvent);
 			GameEvents.onPartUndock.Add(this.onPartEvent);
+			GameEvents.onGameStateLoad.Add(this.onGameLoaded);
 		}
 
 		~RelayDatabase()
@@ -417,6 +434,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 +450,7 @@
 		#if DEBUG
 		public void Dump()
 		{
-			StringBuilder sb = new StringBuilder();
+			StringBuilder sb = Tools.GetStringBuilder();
 
 			sb.Append("Dumping RelayDatabase:");
 
@@ -452,6 +470,8 @@
 			}
 
 			Tools.PostDebugMessage(sb.ToString());
+
+			Tools.PutStringBuilder(sb);
 		}
 		#endif
 	}

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -2,7 +2,7 @@
 //
 // Extensions.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,