RelayDatabase: Added a new API method to dirty a vessel's cache from outside. Added cache hit/miss tracking. Added some debug-only events.
RelayDatabase: Added a new API method to dirty a vessel's cache from outside. Added cache hit/miss tracking. Added some debug-only events.

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -318,7 +318,7 @@
 					"{0}: {1} on {2} cannot transmit: {3}",
 					this.GetType().Name,
 					this.part.partInfo.title,
-					this.vessel.name,
+					this.vessel.vesselName,
 					Enum.GetName(typeof(PartStates), partState)
 				));
 				return false;
@@ -424,7 +424,7 @@
 			if (vessel != null)
 			{
 				msg.Append(" on ");
-				msg.Append(vessel.name);
+				msg.Append(vessel.vesselName);
 			}
 
 			return msg.ToString();

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -76,7 +76,7 @@
 					"{0}: {1} on {2} cannot transmit: {3}",
 					this.GetType().Name,
 					this.title,
-					this.vessel.name,
+					this.vessel.vesselName,
 					Enum.GetName(typeof(PartStates), partState)
 				));
 				return false;
@@ -104,6 +104,14 @@
 			this.protoPart = pps;
 			this.vessel = pps.pVesselRef.vesselRef;
 		}
+
+		~ProtoAntennaRelay()
+		{
+			Tools.PostDebugMessage(string.Format(
+				"{0}: destroyed",
+				this.ToString()
+			));
+		}
 	}
 }
 

--- a/RelayDatabase.cs
+++ b/RelayDatabase.cs
@@ -7,6 +7,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 using UnityEngine;
 
 namespace AntennaRange
@@ -45,6 +46,12 @@
 		// Vessel.id-keyed hash table of part counts, used for caching
 		protected 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;
+
+		protected int cacheHits;
+		protected int cacheMisses;
+
 		/*
 		 * Properties
 		 * */
@@ -58,12 +65,19 @@
 				{
 					// ...Generate an entry for this vessel.
 					this.AddVessel(vessel);
+					this.cacheMisses++;
 				}
 				// If our part count disagrees with the vessel's part count...
-				if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count)
+				else if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count)
 				{
 					// ...Update the our vessel in the cache
 					this.UpdateVessel(vessel);
+					this.cacheMisses++;
+				}
+				// Otherwise, it's a hit
+				else
+				{
+					this.cacheHits++;
 				}
 
 				// Return the Part-hashed table of relays for this vessel
@@ -79,13 +93,13 @@
 		public bool AddVessel(Vessel vessel)
 		{
 			// If this vessel is already here...
-			if (relayDatabase.ContainsKey(vessel.id))
+			if (this.ContainsKey(vessel))
 			{
 				// ...post an error
 				Debug.LogWarning(string.Format(
 					"{0}: Cannot add vessel '{1}' (id: {2}): Already in database.",
 					this.GetType().Name,
-					vessel.name,
+					vessel.vesselName,
 					vessel.id
 				));
 
@@ -110,12 +124,12 @@
 		public void UpdateVessel(Vessel vessel)
 		{
 			// Squak if the database doesn't have the vessel
-			if (!relayDatabase.ContainsKey(vessel.id))
+			if (!this.ContainsKey(vessel))
 			{
 				throw new InvalidOperationException(string.Format(
-					"{0}: Update called vessel '{1}' (id: {2}) not in database: vessel will be added.",
+					"{0}: Update called for vessel '{1}' (id: {2}) not in database: vessel will be added.",
 					this.GetType().Name,
-					vessel.name,
+					vessel.vesselName,
 					vessel.id
 				));
 			}
@@ -128,6 +142,19 @@
 			this.vesselPartCountTable[vessel.id] = vessel.Parts.Count;
 		}
 
+		// Remove a vessel from the cache, if it exists.
+		public void DirtyVessel(Vessel vessel)
+		{
+			if (this.relayDatabase.ContainsKey(vessel.id))
+			{
+				this.relayDatabase.Remove(vessel.id);
+			}
+			if (this.vesselPartCountTable.ContainsKey(vessel.id))
+			{
+				this.vesselPartCountTable.Remove(vessel.id);
+			}
+		}
+
 		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
 		public bool ContainsKey(Guid key)
 		{
@@ -141,24 +168,24 @@
 		}
 
 		// Runs when a vessel is modified (or when we switch to one, to catch docking events)
-		public void onVesselWasModified(Vessel vessel)
+		public void onVesselEvent(Vessel vessel)
 		{
 			// If we have this vessel in our cache...
 			if (this.ContainsKey(vessel))
 			{
 				// If our part counts disagree (such as if a part has been added or broken off,
 				// or if we've just docked or undocked)...
-				if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count)
+				if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count || vessel.loaded)
 				{
 					Tools.PostDebugMessage(string.Format(
-						"{0}: dirtying cache for vessel '{1}' (id: {2}).",
+						"{0}: dirtying cache for vessel '{1}' ({2}).",
 						this.GetType().Name,
-						vessel.name,
+						vessel.vesselName,
 						vessel.id
 					));
 
 					// Dirty the cache (real vessels will never have negative part counts)
-					this.vesselPartCountTable[vessel.id] = -1;
+					this.DirtyVessel(vessel);
 				}
 			}
 		}
@@ -170,8 +197,24 @@
 			if (FlightGlobals.ActiveVessel != null)
 			{
 				// ... dirty its cache
-				this.onVesselWasModified(FlightGlobals.ActiveVessel);
-			}
+				this.onVesselEvent(FlightGlobals.ActiveVessel);
+			}
+		}
+
+		// Runs when parts are undocked
+		public void onPartEvent(Part part)
+		{
+			if (part != null && part.vessel != null)
+			{
+				this.onVesselEvent(part.vessel);
+			}
+		}
+
+		// Runs when parts are coupled, as in docking
+		public void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data)
+		{
+			this.onPartEvent(data.from);
+			this.onPartEvent(data.to);
 		}
 
 		// Produce a Part-hashed table of relays for the given vessel
@@ -183,7 +226,7 @@
 			Tools.PostDebugMessage(string.Format(
 				"{0}: Getting antenna relays from vessel {1}.",
 				"IAntennaRelay",
-				vessel.name
+				vessel.vesselName
 			));
 
 			// If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
@@ -191,7 +234,7 @@
 				Tools.PostDebugMessage(string.Format(
 					"{0}: vessel {1} is loaded, searching for modules in loaded parts.",
 					"IAntennaRelay",
-					vessel.name
+					vessel.vesselName
 				));
 
 				// Loop through the Parts in the Vessel...
@@ -217,7 +260,7 @@
 				Tools.PostDebugMessage(string.Format(
 					"{0}: vessel {1} is not loaded, searching for modules in prototype parts.",
 					this.GetType().Name,
-					vessel.name
+					vessel.vesselName
 				));
 
 				// Loop through the ProtoPartModuleSnapshots in the Vessel...
@@ -267,9 +310,10 @@
 			}
 
 			Tools.PostDebugMessage(string.Format(
-				"{0}: vessel '{1}' has {2} transmitters.",
+				"{0}: vessel '{1}' ({2}) has {3} transmitters.",
 				"IAntennaRelay",
-				vessel.name,
+				vessel.vesselName,
+				vessel.id,
 				relays.Count
 			));
 		}
@@ -278,24 +322,63 @@
 		protected RelayDatabase()
 		{
 			// Initialize the databases
-			relayDatabase =	new Dictionary<Guid, Dictionary<int, IAntennaRelay>>();
-			vesselPartCountTable = new Dictionary<Guid, int>();
+			this.relayDatabase = new Dictionary<Guid, Dictionary<int, IAntennaRelay>>();
+			this.vesselPartCountTable = new Dictionary<Guid, int>();
+			this.CheckedVesselsTable = new Dictionary<Guid, bool>();
+
+			this.cacheHits = 0;
+			this.cacheMisses = 0;
 
 			// Subscribe to some events
-			GameEvents.onVesselWasModified.Add(this.onVesselWasModified);
-			GameEvents.onVesselChange.Add(this.onVesselWasModified);
+			GameEvents.onVesselWasModified.Add(this.onVesselEvent);
+			GameEvents.onVesselChange.Add(this.onVesselEvent);
+			GameEvents.onVesselDestroy.Add(this.onVesselEvent);
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange);
+			GameEvents.onPartCouple.Add(this.onFromPartToPartEvent);
+			GameEvents.onPartUndock.Add(this.onPartEvent);
 		}
 
 		~RelayDatabase()
 		{
 			// Unsubscribe from the events
-			GameEvents.onVesselWasModified.Remove(this.onVesselWasModified);
-			GameEvents.onVesselChange.Remove(this.onVesselWasModified);
+			GameEvents.onVesselWasModified.Remove(this.onVesselEvent);
+			GameEvents.onVesselChange.Remove(this.onVesselEvent);
+			GameEvents.onVesselDestroy.Remove(this.onVesselEvent);
 			GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange);
+			GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent);
+			GameEvents.onPartUndock.Remove(this.onPartEvent);
 
 			Tools.PostDebugMessage(this.GetType().Name + " destroyed.");
-		}
+
+			KSPLog.print(string.Format(
+				"{0} destructed.  Cache hits: {1}, misses: {2}, hit rate: {3:P1}",
+				this.GetType().Name,
+				this.cacheHits,
+				this.cacheMisses,
+				(float)this.cacheHits / (float)(this.cacheMisses + this.cacheHits)
+			));
+		}
+
+		#if DEBUG
+		public void Dump()
+		{
+			StringBuilder sb = new StringBuilder();
+
+			sb.Append("Dumping RelayDatabase:");
+
+			foreach (Guid id in this.relayDatabase.Keys)
+			{
+				sb.AppendFormat("\nVessel {0}:", id);
+
+				foreach (IAntennaRelay relay in this.relayDatabase[id].Values)
+				{
+					sb.AppendFormat("\n\t{0}", relay.ToString());
+				}
+			}
+
+			Tools.PostDebugMessage(sb.ToString());
+		}
+		#endif
 	}
 }