From: Jonathan Bayer Date: Wed, 07 Sep 2016 11:11:28 +0000 Subject: Revert "updated for 1.1.3" X-Git-Url: http://git.toad.homelinux.net/projects/AntennaRange.git/commitdiff/81fa7ca --- Revert "updated for 1.1.3" This reverts commit c48037010f94740d81f5bc6a844e32294551eb1c. --- --- a/.gitattributes +++ b/.gitattributes @@ -4,8 +4,8 @@ # These files are text and should be normalized (convert crlf => lf) *.cs text diff=csharp *.cfg text -*.csproj text -*.sln text +*.csproj text eol=crlf +*.sln text eol=crlf # Images should be treated as binary # (binary is a macro for -text -diff) --- a/ARConfiguration.cs +++ b/ARConfiguration.cs @@ -4,7 +4,10 @@ // copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ using KSP; +using KSP.UI.Screens; using System; +using System.Collections.Generic; +using System.Reflection; using ToadicusTools.Extensions; using ToadicusTools.Text; using ToadicusTools.GUIUtils; @@ -31,6 +34,8 @@ private const string TRACKING_STATION_RANGES_KEY = "TRACKING_STATION_RANGES"; private const string RANGE_KEY = "range"; + private const string USE_TOOLBAR_KEY = "useToolbarIfAvailable"; + /// /// Indicates whether connections require line of sight. /// @@ -115,7 +120,19 @@ } } + /// + /// Gets a value indicating whether we should use Toolbar if available. + /// + /// true if we should use Toolbar if available; otherwise, false. + public static bool UseToolbarIfAvailable + { + get; + private set; + } + #pragma warning disable 1591 + + private static MethodInfo partLoader_CompilePartInfo; private bool showConfigWindow; private Rect configWindowPos; @@ -177,6 +194,8 @@ ARConfiguration.UpdateDelay = this.LoadConfigValue(UPDATE_DELAY_KEY, 16L); this.updateDelayStr = ARConfiguration.UpdateDelay.ToString(); + + ARConfiguration.UseToolbarIfAvailable = this.LoadConfigValue(USE_TOOLBAR_KEY, true); GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested); GameEvents.OnKSCFacilityUpgraded.Add(this.onFacilityUpgraded); @@ -215,6 +234,16 @@ this.trackingStationRanges.SPrint()); } + if (partLoader_CompilePartInfo == null) + { + partLoader_CompilePartInfo = typeof(PartLoader).GetMethod( + "CompilePartInfo", + BindingFlags.NonPublic | BindingFlags.Instance + ); + + this.Log("Fetched PartLoader.CompilePartInfo: {0}", partLoader_CompilePartInfo); + } + this.runOnce = true; this.LogDebug("Awake."); @@ -230,17 +259,24 @@ this.runOnce = false; this.SetKerbinRelayRange(); + this.updateModuleInfos(); } } public void OnGUI() { // Only runs once, if the Toolbar is available. - if (ToolbarManager.ToolbarAvailable) + if (ToolbarManager.ToolbarAvailable && ARConfiguration.UseToolbarIfAvailable) { if (this.toolbarButton == null) { this.LogDebug("Toolbar available; initializing toolbar button."); + + if (this.appLauncherButton != null) + { + ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton); + this.appLauncherButton = null; + } this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConfiguration"); this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.SPACECENTER); @@ -255,6 +291,12 @@ } else if (this.appLauncherButton == null && ApplicationLauncher.Ready) { + if (this.toolbarButton != null) + { + this.toolbarButton.Destroy(); + this.toolbarButton = null; + } + this.LogDebug("Toolbar available; initializing AppLauncher button."); this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication( @@ -325,6 +367,8 @@ { ARConfiguration.FixedPowerCost = fixedPowerCost; this.SaveConfigValue(FIXED_POWER_KEY, fixedPowerCost); + + this.updateModuleInfos(); } GUILayout.EndHorizontal(); @@ -336,6 +380,8 @@ { ARConfiguration.UseAdditiveRanges = useAdditive; this.SaveConfigValue(USE_ADDITIVE_KEY, useAdditive); + + this.updateModuleInfos(); } GUILayout.EndHorizontal(); @@ -353,6 +399,17 @@ GUILayout.BeginHorizontal(); + bool useToolbar = Layout.Toggle(ARConfiguration.UseToolbarIfAvailable, "Use Blizzy's Toolbar, if Available"); + if (useToolbar != ARConfiguration.UseToolbarIfAvailable) + { + ARConfiguration.UseToolbarIfAvailable = useToolbar; + this.SaveConfigValue(USE_TOOLBAR_KEY, useToolbar); + } + + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.Label("Update Delay", GUILayout.ExpandWidth(false)); this.updateDelayStr = GUILayout.TextField(this.updateDelayStr, 4, GUILayout.Width(40f)); @@ -430,6 +487,90 @@ { this.Log("Caught onFacilityUpgraded for {0} at level {1}", fac.id, lvl); this.SetKerbinRelayRange(); + + this.updateModuleInfos(); + } + } + + private void updateModuleInfos() + { + if (PartLoader.Instance != null && PartLoader.Instance.parts != null) + { + this.Log("Updating module infos in PartLoader"); + this.updateModuleInfos(PartLoader.Instance.parts); + } + + if (RDTestSceneLoader.Instance != null && RDTestSceneLoader.Instance.partsList != null) + { + this.Log("Updating module infos in RDTestSceneLoader"); + this.updateModuleInfos(RDTestSceneLoader.Instance.partsList); + } + } + + private void updateModuleInfos(List partsList) + { + if (partLoader_CompilePartInfo == null) + { + this.LogError("Cannot recompile part info; partLoader_CompilePartInfo not found."); + return; + } + + if (PartLoader.Instance == null) + { + this.LogError("Cannot recompile part info; PartLoader.Instance is null."); + return; + } + + // We need to go find all of the prefabs and update them, because Squad broke IModuleInfo. + AvailablePart availablePart; + Part partPrefab; + PartModule modulePrefab; + object[] compileArgs = new object[2]; + + this.Log("Updating module infos..."); + + for (int apIdx = 0; apIdx < partsList.Count; apIdx++) + { + availablePart = partsList[apIdx]; + + if (availablePart == null) + { + continue; + } + + partPrefab = availablePart.partPrefab; + + if (partPrefab == null || partPrefab.Modules == null) + { + continue; + } + + for (int pmIdx = 0; pmIdx < partPrefab.Modules.Count; pmIdx++) + { + modulePrefab = partPrefab.Modules[pmIdx]; + + if (modulePrefab == null) + { + continue; + } + + if (modulePrefab is IAntennaRelay) + { + this.Log("Found prefab IAntennaRelay {0}", modulePrefab); + + this.Log("Recompiling part and module info for {0}", availablePart.name); + + availablePart.moduleInfos.Clear(); + availablePart.resourceInfos.Clear(); + + compileArgs[0] = availablePart; + compileArgs[1] = partPrefab; + + partLoader_CompilePartInfo.Invoke(PartLoader.Instance, compileArgs); + + break; + } + } } } --- a/ARFlightController.cs +++ b/ARFlightController.cs @@ -29,6 +29,7 @@ #pragma warning disable 1591 using KSP; +using KSP.UI.Screens; using System; using System.Collections.Generic; using ToadicusTools.Extensions; @@ -149,7 +150,7 @@ this.appLauncherTextures[ConnectionStatus.Optimal] = GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIcon", false); - if (ToolbarManager.ToolbarAvailable) + if (ToolbarManager.ToolbarAvailable && ARConfiguration.UseToolbarIfAvailable) { this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus"); @@ -172,7 +173,7 @@ VesselCommand availableCommand; - if (ARConfiguration.RequireConnectionForControl) + if (ARConfiguration.RequireConnectionForControl && this.vessel != null) { availableCommand = this.vessel.CurrentCommand(); } @@ -258,7 +259,7 @@ { Vessel vessel; IAntennaRelay relay; - IAntennaRelay bestActiveRelay; + IAntennaRelay bestActiveRelay = null; IList activeVesselRelays; usefulRelays.Clear(); @@ -272,6 +273,14 @@ continue; } + switch (vessel.vesselType) + { + case VesselType.Debris: + case VesselType.Flag: + case VesselType.Unknown: + continue; + } + log.AppendFormat("\nFetching best relay for vessel {0}", vessel); relay = vessel.GetBestRelay(); @@ -290,21 +299,6 @@ { bestActiveRelay = RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel); - for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++) - { - relay = activeVesselRelays[rIdx]; - - // The best active relay will get checked with the other useful relays later. - if (relay == null || relay == bestActiveRelay) - { - continue; - } - - log.AppendFormat("\nFinding nearest relay for active vessel relay {0}", relay); - - relay.FindNearestRelay(); - } - log.AppendFormat("\n\tAdding best active vessel relay {0} to usefulRelays", bestActiveRelay); usefulRelays.Add(bestActiveRelay); @@ -323,6 +317,24 @@ log.AppendFormat("\n\tDoing target search for useful relay {0}", relay); + relay.FindNearestRelay(); + relay.RecalculateTransmissionRates(); + } + + // Very last, find routes for the non-best relays on the active vessel. + for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++) + { + relay = activeVesselRelays[rIdx]; + + // The best active relay will get checked with the other useful relays later. + if (relay == null || relay == bestActiveRelay) + { + continue; + } + + log.AppendFormat("\nFinding nearest relay for active vessel relay {0}", relay); + + relay.RecalculateTransmissionRates(); relay.FindNearestRelay(); } --- a/ARMapRenderer.cs +++ b/ARMapRenderer.cs @@ -100,7 +100,7 @@ this.timer = new System.Diagnostics.Stopwatch(); #endif #if DEBUG - this.log = PooledDebugLogger.Get(this); + this.log = PooledDebugLogger.Get(); #endif } @@ -122,7 +122,7 @@ log.Clear(); log.AppendFormat("OnPreCull.\n"); - +/* @ TODO: Fix log.AppendFormat("\tMapView: Draw3DLines: {0}\n" + "\tMapView.MapCamera.camera.fieldOfView: {1}\n" + "\tMapView.MapCamera.Distance: {2}\n", @@ -130,7 +130,7 @@ MapView.MapCamera.camera.fieldOfView, MapView.MapCamera.Distance ); - +*/ log.AppendLine("FlightGlobals ready and Vessels list not null."); IAntennaRelay relay; @@ -213,20 +213,22 @@ } LineRenderer renderer = this[relay.vessel.id]; - Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); + Vector3 start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); float lineWidth; float d = Screen.height / 2f + 0.01f; if (MapView.Draw3DLines) { - lineWidth = 0.005859375f * MapView.MapCamera.Distance; + lineWidth = 0.00833333333f * MapView.MapCamera.Distance; } else { - lineWidth = 2f; - - start = MapView.MapCamera.camera.WorldToScreenPoint(start); + lineWidth = 3f; + + // TODO: No idea if this substitution is right. + // start = MapView.MapCamera.camera.WorldToScreenPoint(start); + start = PlanetariumCamera.Camera.WorldToScreenPoint(start); start.z = start.z >= 0f ? d : -d; } @@ -241,7 +243,7 @@ relayStart = timer.ElapsedMilliseconds; #endif - Vector3d nextPoint; + Vector3 nextPoint; renderer.enabled = true; @@ -278,6 +280,17 @@ return; } + switch (relay.targetRelay.vessel.vesselType) + { + case VesselType.Debris: + case VesselType.Flag: + case VesselType.Unknown: + renderer.enabled = false; + return; + default: + break; + } + nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D()); } @@ -285,7 +298,9 @@ if (!MapView.Draw3DLines) { - nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint); + // TODO: No idea if this substitution is right. + // nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint); + nextPoint = PlanetariumCamera.Camera.WorldToScreenPoint(nextPoint); nextPoint.z = nextPoint.z >= 0f ? d : -d; } @@ -307,6 +322,12 @@ while (enumerator.MoveNext()) { lineRenderer = enumerator.Current; + + if (lineRenderer == null) + { + continue; + } + lineRenderer.enabled = false; if (freeObjects) --- a/AntennaRange.csproj +++ b/AntennaRange.csproj @@ -80,6 +80,7 @@ + @@ -93,7 +94,12 @@ ..\_KSPAssemblies\UnityEngine.dll - False + + + ..\_KSPAssemblies\KSPUtil.dll + + + ..\_KSPAssemblies\UnityEngine.UI.dll --- a/AntennaRelay.cs +++ b/AntennaRelay.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using ToadicusTools.DebugTools; using ToadicusTools.Extensions; +using UnityEngine; namespace AntennaRange { @@ -157,6 +158,66 @@ } /// + /// Gets the current link resource rate in EC/MiT. + /// + /// The current link resource rate in EC/MiT. + public virtual RelayDataCost CurrentLinkCost + { + get + { + return this.moduleRef?.CurrentLinkCost ?? RelayDataCost.Infinity; + } + set + { + throw new NotImplementedException( + string.Format( + "{0} must not assign CurrentLinkCost. This is probably a bug.", + this.GetType().FullName + ) + ); + } + } + + /// + /// Gets the current network link cost back to Kerbin, in EC/MiT. + /// + /// The current network link cost back to Kerbin, in EC/MiT. + public virtual RelayDataCost CurrentNetworkLinkCost + { + get + { + RelayDataCost cost = new RelayDataCost(); + + IAntennaRelay relay = this.moduleRef; + + ushort iters = 0; + while (relay != null) + { + cost += relay.CurrentLinkCost; + + if (relay.KerbinDirect) + { + break; + } + + iters++; + + if (iters > 255) + { + this.LogError("Bailing out of AntennaRelay.CurrentNetworkLinkCost because it looks like " + + "we're stuck in an infinite loop. This is probably a bug."); + + break; + } + + relay = relay.targetRelay; + } + + return cost; + } + } + + /// /// Gets or sets the link status. /// public virtual ConnectionStatus LinkStatus @@ -191,6 +252,131 @@ public virtual bool CanTransmit() { return this.canTransmit; + } + + /// + /// Recalculates the transmission rates. + /// + public void RecalculateTransmissionRates() + { + if (!this.canTransmit) { + this.moduleRef.CurrentLinkCost = RelayDataCost.Infinity; + return; + } + + RelayDataCost cost = this.GetPotentialLinkCost(this.CurrentLinkSqrDistance, this.NominalLinkSqrDistance); + + this.moduleRef.CurrentLinkCost = cost; + } + + /// + /// Gets the potential link cost, in EC/MiT. + /// + /// The potential link cost, in EC/MiT. + /// Square of the current distance to the target + /// Square of the nominal range to the target. + public RelayDataCost GetPotentialLinkCost(double currentSqrDistance, double nominalSqrDistance) + { + RelayDataCost linkCost = new RelayDataCost(); + + float rangeFactor = (float)(nominalSqrDistance / currentSqrDistance); + + RelayDataCost baseCost = this.moduleRef?.BaseLinkCost ?? RelayDataCost.Infinity; + + if (ARConfiguration.FixedPowerCost) + { + linkCost.PacketResourceCost = baseCost.PacketResourceCost; + + linkCost.PacketSize = Mathf.Min( + baseCost.PacketSize * rangeFactor, + baseCost.PacketSize * this.moduleRef.MaxDataFactor + ); + } + else + { + if (currentSqrDistance > nominalSqrDistance) + { + linkCost.PacketSize = baseCost.PacketSize; + linkCost.PacketResourceCost = baseCost.PacketResourceCost / rangeFactor; + } + else + { + linkCost.PacketSize = Mathf.Min( + baseCost.PacketSize * rangeFactor, + baseCost.PacketSize * this.moduleRef.MaxDataFactor + ); + linkCost.PacketResourceCost = baseCost.PacketResourceCost; + } + } + + linkCost.PacketResourceCost *= this.moduleRef.PacketThrottle / 100f; + linkCost.PacketSize *= this.moduleRef.PacketThrottle / 100f; + + return linkCost; + } + + /// + /// Gets the potential link cost, in EC/MiT. + /// + /// The potential link cost, in EC/MiT. + /// Potential target relay. + public RelayDataCost GetPotentialLinkCost(IAntennaRelay potentialTarget) + { + if (potentialTarget == null) + { + return RelayDataCost.Infinity; + } + + double currentSqrDistance = this.SqrDistanceTo(potentialTarget); + + if (currentSqrDistance > this.MaxLinkSqrDistanceTo(potentialTarget)) + { + return RelayDataCost.Infinity; + } + + double nominalSqrDistance; + if (ARConfiguration.UseAdditiveRanges) + { + nominalSqrDistance = this.nominalTransmitDistance * potentialTarget.nominalTransmitDistance; + } + else + { + nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; + } + + return GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance); + } + + /// + /// Gets the potential link cost, in EC/MiT. + /// + /// The potential link cost, in EC/MiT. + /// Potential target Body + public RelayDataCost GetPotentialLinkCost(CelestialBody body) + { + if (body == null || body != Kerbin) + { + return RelayDataCost.Infinity; + } + + double currentSqrDistance = this.SqrDistanceTo(body); + + if (currentSqrDistance > this.MaxLinkSqrDistanceTo(body)) + { + return RelayDataCost.Infinity; + } + + double nominalSqrDistance; + if (ARConfiguration.UseAdditiveRanges) + { + nominalSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange; + } + else + { + nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; + } + + return GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance); } /// @@ -242,15 +428,12 @@ CelestialBody bodyOccludingBestOccludedRelay = null; IAntennaRelay needle; - double nearestRelaySqrQuotient = double.PositiveInfinity; - double bestOccludedSqrQuotient = double.PositiveInfinity; - - double potentialSqrDistance; - double maxLinkSqrDistance; - double potentialSqrQuotient; - - double kerbinSqrDistance; - double kerbinSqrQuotient; + RelayDataCost cheapestRelayRate = RelayDataCost.Infinity; + RelayDataCost cheapestOccludedRelayRate = RelayDataCost.Infinity; + + RelayDataCost potentialRelayRate; + + RelayDataCost kerbinRelayRate = this.GetPotentialLinkCost(Kerbin); bool isCircular; int iterCount; @@ -305,24 +488,16 @@ #endif // Find the distance from here to the vessel... - log.Append("\n\tgetting distance to potential vessel"); - potentialSqrDistance = this.SqrDistanceTo(potentialBestRelay); - log.Append("\n\tgetting best vessel relay"); - - log.Append("\n\tgetting max link distance to potential relay"); - - if (ARConfiguration.UseAdditiveRanges) - { - maxLinkSqrDistance = this.maxTransmitDistance * potentialBestRelay.maxTransmitDistance; - } - else - { - maxLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; - } - - log.AppendFormat("\n\tmax link distance: {0}", maxLinkSqrDistance); - - potentialSqrQuotient = potentialSqrDistance / maxLinkSqrDistance; + log.Append("\n\tgetting cost to potential vessel"); + potentialRelayRate = potentialBestRelay.CurrentNetworkLinkCost + + this.GetPotentialLinkCost(potentialBestRelay); + + log.AppendFormat( + "\n\tpotentialRelayRate = {0} ({1} + {2})", + potentialRelayRate, + potentialBestRelay.CurrentNetworkLinkCost, + this.GetPotentialLinkCost(potentialBestRelay) + ); #if BENCH startLOSVesselTicks = performanceTimer.ElapsedTicks; @@ -350,22 +525,21 @@ log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.", this.ToString(), potentialBestRelay); - log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance); - log.AppendFormat("\n\t\t\tbestOccludedSqrQuotient: {0}", bestOccludedSqrQuotient); - log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxLinkSqrDistance); + log.AppendFormat("\n\t\t\tpotentialRelayRate: {0}", potentialRelayRate); + log.AppendFormat("\n\t\t\tcheapestOccludedRelayRate: {0}", cheapestOccludedRelayRate); if ( - (potentialSqrQuotient < bestOccludedSqrQuotient) && - (potentialSqrQuotient <= 1d) && + (potentialRelayRate < cheapestRelayRate) && + this.IsInRangeOf(potentialBestRelay) && potentialBestRelay.CanTransmit() ) { - log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit"); + log.Append("\n\t\t...vessel is cheapest and in range and potentialBestRelay can transmit"); log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay); this.bestOccludedRelay = potentialBestRelay; bodyOccludingBestOccludedRelay = fob; - bestOccludedSqrQuotient = potentialSqrQuotient; + cheapestOccludedRelayRate = potentialRelayRate; } else { @@ -392,12 +566,16 @@ /* * ...so that we can skip the vessel if it is further away than a vessel we've already checked. * */ - if (potentialSqrQuotient > nearestRelaySqrQuotient) + if (potentialRelayRate > cheapestRelayRate) { - log.AppendFormat("\n\t{0}: Relay {1} discarded because it is farther than another the nearest relay.", + log.AppendFormat( + "\n\t{0}: Relay {1} discarded because it is more expensive than the cheapest relay." + + "\n\t\t({2}, {3} > {4})", this.ToString(), - potentialBestRelay + potentialBestRelay, + this.nearestRelay == null ? "NULL" : this.nearestRelay.ToString(), + potentialRelayRate, cheapestRelayRate ); continue; } @@ -461,13 +639,13 @@ if (!isCircular) { - nearestRelaySqrQuotient = potentialSqrQuotient; + cheapestRelayRate = potentialRelayRate; this.nearestRelay = potentialBestRelay; - log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m²)", + log.AppendFormat("\n\t{0}: found new cheapest relay {1} ({2} EC/MiT)", this.ToString(), this.nearestRelay.ToString(), - Math.Sqrt(nearestRelaySqrQuotient) + cheapestRelayRate ); } else @@ -496,29 +674,15 @@ CelestialBody bodyOccludingKerbin = null; - kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius; - kerbinSqrDistance *= kerbinSqrDistance; - - if (ARConfiguration.UseAdditiveRanges) - { - kerbinSqrQuotient = kerbinSqrDistance / - (this.maxTransmitDistance * ARConfiguration.KerbinRelayRange); - } - else - { - kerbinSqrQuotient = kerbinSqrDistance / - (this.maxTransmitDistance * this.maxTransmitDistance); - } - log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name); log.AppendFormat( - "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinSqrDistance={5}m²)", + "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinRelayRate={5} EC/MiT)", this, this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, + cheapestRelayRate, this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), - bestOccludedSqrQuotient, - kerbinSqrDistance + cheapestOccludedRelayRate, + kerbinRelayRate ); #if BENCH @@ -536,13 +700,12 @@ #endif log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName); - // nearestRelaySqrDistance will be infinity if all relays are occluded or none exist. - // Therefore, this will only be true if a valid relay is in range. - if (nearestRelaySqrQuotient <= 1d) - { - log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", - this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, 1d); + // If we're in range of the "nearest" (actually cheapest) relay, use it. + if (this.IsInRangeOf(this.nearestRelay)) + { + log.AppendFormat("\n\t\tCan transmit to nearby relay {0}).", + this.nearestRelay == null ? "null" : this.nearestRelay.ToString() + ); this.KerbinDirect = false; this.canTransmit = true; @@ -551,27 +714,27 @@ // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway else { - log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", - this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, 1d); + log.AppendFormat("\n\t\tCan't transmit to nearby relay {0}.", + this.nearestRelay == null ? "null" : this.nearestRelay.ToString() + ); this.canTransmit = false; - // If the best occluded relay is closer than Kerbin, check it against the nearest relay. - // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe - if (bestOccludedSqrQuotient < kerbinSqrQuotient) - { - log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", - bestOccludedRelay, kerbinSqrDistance); + // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay. + // Since cheapestOccludedRelayRate is infinity if there are no occluded relays, this is safe + if (cheapestOccludedRelayRate < kerbinRelayRate) + { + log.AppendFormat("\n\t\t\tBest occluded relay is cheaper than Kerbin ({0} < {1})", + cheapestOccludedRelayRate, kerbinRelayRate); this.KerbinDirect = false; - // If the nearest relay is closer than the best occluded relay, pick it. - // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. - if (nearestRelaySqrQuotient < bestOccludedSqrQuotient) + // If the nearest relay is cheaper than the best occluded relay, pick it. + // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. + if (cheapestRelayRate < cheapestOccludedRelayRate) { - log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", - nearestRelaySqrQuotient, bestOccludedSqrQuotient); + log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.", + cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = this.nearestRelay; this.firstOccludingBody = null; @@ -579,36 +742,37 @@ // Otherwise, target the best occluded relay. else { - log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", - nearestRelaySqrQuotient, bestOccludedSqrQuotient); + log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", + cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = bestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay; } } - // Otherwise, check Kerbin against the nearest relay. - // Since we have LOS, blank the first occluding body. + // Otherwise, check Kerbin against the "nearest" (cheapest) relay. else { - log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", - bestOccludedRelay, kerbinSqrDistance); + log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})", + cheapestOccludedRelayRate, kerbinRelayRate); - // If the nearest relay is closer than Kerbin, pick it. - // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. - if (nearestRelaySqrQuotient < kerbinSqrQuotient) + // If the "nearest" (cheapest) relay is cheaper than Kerbin, pick it. + // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. + if (cheapestRelayRate < kerbinRelayRate) { - log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", - nearestRelaySqrQuotient, kerbinSqrQuotient); - + log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.", + cheapestRelayRate, kerbinRelayRate); + + // Since we have LOS, blank the first occluding body. + this.firstOccludingBody = null; + this.KerbinDirect = false; - this.firstOccludingBody = null; this.targetRelay = this.nearestRelay; } // Otherwise, pick Kerbin. else { - log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", - nearestRelaySqrQuotient, kerbinSqrQuotient); + log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", + cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.firstOccludingBody = bodyOccludingKerbin; @@ -626,21 +790,21 @@ log.AppendFormat("\n\tKerbin is in LOS."); - // If the nearest relay is closer than Kerbin and in range, transmit to it. - if (nearestRelaySqrQuotient <= 1d) - { - log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", - this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, 1d); + // If the nearest relay is in range, we can transmit. + if (this.IsInRangeOf(this.nearestRelay)) + { + log.AppendFormat("\n\t\tCan transmit to nearby relay {0} (in range).", + this.nearestRelay == null ? "null" : this.nearestRelay.ToString() + ); this.canTransmit = true; // If the nearestRelay is closer than Kerbin, use it. - if (nearestRelaySqrQuotient < kerbinSqrQuotient) + if (cheapestRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, kerbinSqrQuotient); + cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = false; this.targetRelay = this.nearestRelay; @@ -650,7 +814,7 @@ { log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, kerbinSqrQuotient); + cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.targetRelay = null; @@ -659,15 +823,14 @@ // If the nearest relay is out of range, we still need to check on Kerbin. else { - log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", - this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), - nearestRelaySqrQuotient, 1d); + log.AppendFormat("\n\t\tCheapest relay {0} is out of range.", + this.nearestRelay == null ? "null" : this.nearestRelay.ToString() + ); // If Kerbin is in range, use it. - if (kerbinSqrQuotient <= 1d) - { - log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).", - kerbinSqrQuotient, 1d); + if (this.IsInRangeOf(Kerbin)) + { + log.AppendFormat("\n\t\t\tCan transmit to Kerbin (in range)."); this.canTransmit = true; this.KerbinDirect = true; @@ -677,26 +840,25 @@ // Kerbin and bestOccludedRelay else { - log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).", - kerbinSqrQuotient, 1d); + log.AppendFormat("\n\t\t\tCan't transmit to Kerbin (out of range)."); this.canTransmit = false; - // If the best occluded relay is closer than Kerbin, check it against the nearest relay. + // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay. // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe - if (bestOccludedSqrQuotient < kerbinSqrQuotient) + if (cheapestOccludedRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", - bestOccludedRelay, kerbinSqrDistance); + cheapestOccludedRelayRate, kerbinRelayRate); this.KerbinDirect = false; // If the nearest relay is closer than the best occluded relay, pick it. - // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. - if (nearestRelaySqrQuotient < bestOccludedSqrQuotient) + // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. + if (cheapestRelayRate < cheapestOccludedRelayRate) { - log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", - nearestRelaySqrQuotient, bestOccludedSqrQuotient); + log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.", + cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = this.nearestRelay; this.firstOccludingBody = null; @@ -704,8 +866,8 @@ // Otherwise, target the best occluded relay. else { - log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", - nearestRelaySqrQuotient, bestOccludedSqrQuotient); + log.AppendFormat("\n\t\t\t\t...and cheaper than the cheapest relay ({0} >= {1}), so picking it.", + cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = bestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay; @@ -715,17 +877,17 @@ // Since we have LOS, blank the first occluding body. else { - log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", - bestOccludedRelay, kerbinSqrDistance); + log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})", + cheapestOccludedRelayRate, kerbinRelayRate); this.firstOccludingBody = null; - // If the nearest relay is closer than Kerbin, pick it. - // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. - if (nearestRelaySqrQuotient < kerbinSqrQuotient) + // If the nearest relay is cheaper than Kerbin, pick it. + // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. + if (cheapestRelayRate < kerbinRelayRate) { - log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", - nearestRelaySqrQuotient, kerbinSqrQuotient); + log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.", + cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = false; this.targetRelay = this.nearestRelay; @@ -733,8 +895,8 @@ // Otherwise, pick Kerbin. else { - log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", - nearestRelaySqrQuotient, kerbinSqrQuotient); + log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", + cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.targetRelay = null; @@ -882,6 +1044,7 @@ /// as an public AntennaRelay(IAntennaRelay module) { + this.KerbinDirect = true; this.moduleRef = module; #if BENCH --- a/GameData/AntennaRange/AntennaRange.cfg +++ b/GameData/AntennaRange/AntennaRange.cfg @@ -37,8 +37,10 @@ // maxDataFactor: The multipler on packetSize that defines the maximum data bandwidth of the antenna. // -@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2] +@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech] { + @TechRequired = start + @MODULE[ModuleDataTransmitter] { @name = ModuleLimitedDataTransmitter @@ -59,7 +61,7 @@ } } -@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2] +@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech] { @MODULE[ModuleDataTransmitter] { @@ -81,7 +83,7 @@ } } -@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2] +@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech] { @MODULE[ModuleDataTransmitter] { @@ -104,7 +106,7 @@ } } -@PART[HighGainAntenna]:FOR[AntennaRange]:NEEDS[AsteroidDay,!RemoteTech] +@PART[HighGainAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech] { @TechRequired = electronics @description = Repurposed for medium range probes, the HG-55 provdes high speed directional data transmission. --- a/IAntennaRelay.cs +++ b/IAntennaRelay.cs @@ -47,6 +47,42 @@ IAntennaRelay targetRelay { get; } /// + /// Gets the current link resource rate in EC/MiT. + /// + /// The current link resource rate in EC/MiT. + RelayDataCost CurrentLinkCost { get; set; } + + /// + /// Gets the base link resource rate in EC/MiT. + /// + /// The base link resource rate in EC/MiT. + RelayDataCost BaseLinkCost { get; } + + /// + /// Gets the packet throttle. + /// + /// The packet throttle in range [0..100]. + float PacketThrottle { get; } + + /// + /// Gets the max data factor. + /// + /// The max data factor. + float MaxDataFactor { get; } + + /// + /// Gets the data resource cost in EC/MiT. + /// + /// The data resource cost in EC/MiT. + double DataResourceCost { get; } + + /// + /// Gets the current network resource rate in EC/MiT. + /// + /// The current network resource rate in EC/MiT. + RelayDataCost CurrentNetworkLinkCost { get; } + + /// /// Gets a value indicating whether this Relay is communicating /// directly with Kerbin. /// @@ -56,7 +92,6 @@ /// The link distance, in meters, at which this relay behaves nominally. /// double NominalLinkSqrDistance { get; } - /// /// The link distance, in meters, beyond which this relay cannot operate. @@ -100,9 +135,19 @@ bool CanTransmit(); /// + /// Recalculates the transmission rates. + /// + void RecalculateTransmissionRates(); + + /// /// Finds the nearest relay. /// void FindNearestRelay(); + + /// + /// Recalculates the max range; useful for making sure we're using additive ranges when enabled. + /// + void RecalculateMaxRange(); /// /// Returns a that represents the current . --- a/ModuleLimitedDataTransmitter.cs +++ b/ModuleLimitedDataTransmitter.cs @@ -52,24 +52,12 @@ : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay, IModuleInfo { private const string tooltipSkinName = "PartTooltipSkin"; - private static GUISkin partTooltipSkin; - private static GUIStyle partTooltipBodyStyle; - private static GUIStyle partTooltipHeaderStyle; - - // Stores the packetResourceCost as defined in the .cfg file. - private float _basepacketResourceCost; - - // Stores the packetSize as defined in the .cfg file. - private float _basepacketSize; // Every antenna is a relay. private AntennaRelay relay; // Sometimes we will need to communicate errors; this is how we do it. private ScreenMessage ErrorMsg; - - // Used in module info panes for part tooltips in the editor and R&D - private GUIContent moduleInfoContent; /// /// When additive ranges are enabled, the distance from Kerbin at which the antenna will perform exactly as @@ -184,9 +172,62 @@ else { this.LogError("Vessel and/or part reference are null, returning null vessel."); + #if DEBUG && VERBOSE this.LogError(new System.Diagnostics.StackTrace().ToString()); + #endif return null; } + } + } + + /// + /// Gets the current link resource rate in EC/MiT. + /// + /// The current link resource rate in EC/MiT. + public RelayDataCost CurrentLinkCost + { + get + { + return new RelayDataCost(this.packetResourceCost, this.packetSize); + } + set + { + this.packetResourceCost = value.PacketResourceCost; + this.packetSize = value.PacketSize; + } + } + + /// + /// Gets the base link resource rate in EC/MiT. + /// + /// The base link resource rate in EC/MiT. + public RelayDataCost BaseLinkCost + { + get; + private set; + } + + /// + /// Gets the packet throttle. + /// + /// The packet throttle in range [0..100]. + public float PacketThrottle + { + get + { + return this.packetThrottle; + } + } + + /// + /// Gets the max data factor. + /// + /// The max data factor. + public float MaxDataFactor + { + get + { + return this.maxDataFactor; } } @@ -359,8 +400,6 @@ { get { - this.PreTransmit_SetPacketSize(); - if (this.CanTransmit()) { return this.packetSize; @@ -380,8 +419,6 @@ { get { - this.PreTransmit_SetPacketResourceCost(); - if (this.CanTransmit()) { return this.packetResourceCost; @@ -390,6 +427,23 @@ { return float.PositiveInfinity; } + } + } + + /// + /// Gets the current network resource rate in EC/MiT. + /// + /// The current network resource rate in EC/MiT. + public RelayDataCost CurrentNetworkLinkCost + { + get + { + if (this.relay == null) + { + return RelayDataCost.Infinity; + } + + return this.relay.CurrentNetworkLinkCost; } } @@ -415,20 +469,17 @@ // Build ALL the objects. public ModuleLimitedDataTransmitter () : base() { - this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT); + this.ErrorMsg = new ScreenMessage("", 4f, ScreenMessageStyle.UPPER_LEFT); this.packetThrottle = 100f; } + #if DEBUG /// /// PartModule OnAwake override; runs at Unity Awake. /// public override void OnAwake() { base.OnAwake(); - - this._basepacketSize = base.packetSize; - this._basepacketResourceCost = base.packetResourceCost; - this.moduleInfoContent = new GUIContent(); this.LogDebug("{0} loaded:\n" + "packetSize: {1}\n" + @@ -438,12 +489,13 @@ "maxDataFactor: {5}\n", this, base.packetSize, - this._basepacketResourceCost, + this.packetResourceCost, this.nominalTransmitDistance, this.maxPowerFactor, this.maxDataFactor ); } + #endif /// /// PartModule OnStart override; runs at Unity Start. @@ -451,9 +503,10 @@ /// State. public override void OnStart (StartState state) { + this.BaseLinkCost = new RelayDataCost(base.packetResourceCost, base.packetSize); + this.RecalculateMaxRange(); + base.OnStart (state); - - this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance; if (state >= StartState.PreLaunch) { @@ -481,7 +534,8 @@ base.OnLoad (node); - this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance; + this.BaseLinkCost = new RelayDataCost(base.packetResourceCost, base.packetSize); + this.RecalculateMaxRange(); } /// @@ -504,6 +558,9 @@ // HACK: Currently hacks around Squad's extraneous layout box, see KSPModders issue #5118 private void drawTooltipWidget(Rect rect) { + /* + * Removed all this because Squad doesn't even call it anymore. + * this.moduleInfoContent.text = this.GetInfo(); if (partTooltipSkin == null) @@ -545,7 +602,7 @@ GUILayout.Space(height - orgHeight - partTooltipBodyStyle.padding.bottom - partTooltipBodyStyle.padding.top - 2f * (partTooltipBodyStyle.margin.bottom + partTooltipBodyStyle.margin.top) - ); + );*/ } /// @@ -563,16 +620,12 @@ { using (PooledStringBuilder sb = PooledStringBuilder.Get()) { - string text; - - sb.Append(base.GetInfo()); - if (ARConfiguration.UseAdditiveRanges) { - sb.AppendFormat("Nominal Range to Kerbin: {0:S3}m\n", + sb.AppendFormat("Nominal Range to Kerbin: {0:S3}m\n", Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange) ); - sb.AppendFormat("Maximum Range to Kerbin: {0:S3}m", + sb.AppendFormat("Maximum Range to Kerbin: {0:S3}m\n", Math.Sqrt( this.nominalTransmitDistance * Math.Sqrt(this.maxPowerFactor) * ARConfiguration.KerbinRelayRange @@ -581,13 +634,69 @@ } else { - sb.AppendFormat("Nominal Range: {0:S3}m\n", this.nominalTransmitDistance); - sb.AppendFormat("Maximum Range: {0:S3}m", this.maxTransmitDistance); - } - - text = sb.ToString(); - - return text; + sb.AppendFormat("Nominal Range: {0:S3}m\n", this.nominalTransmitDistance); + sb.AppendFormat("Maximum Range: {0:S3}m\n", this.maxTransmitDistance); + } + + sb.AppendLine(); + + sb.AppendFormat("Nominal Packet Size: {0:S2}iT\n", this.BaseLinkCost.PacketSize * 1000000f); + sb.AppendFormat( + "Nominal Data Rate: {0:S2}iT/sec\n", + this.BaseLinkCost.PacketSize / this.packetInterval * 1000000f + ); + + sb.AppendLine(); + + sb.AppendFormat("Within Nominal Range...\n...Maximum Speedup: {0:P0}\n", this.maxDataFactor); + + if (ARConfiguration.FixedPowerCost) + { + sb.AppendLine(); + + sb.AppendFormat( + "Outside Nominal Range...\n...Maximum Slowdown: {0:P1}\n", + 1f / this.maxPowerFactor + ); + + sb.AppendLine(); + + sb.AppendFormat( + "Packet Cost: {0:0.0#} {1}\n", + this.BaseLinkCost.PacketResourceCost, + this.requiredResource == "ElectricCharge" ? "EC" : this.requiredResource + ); + sb.AppendFormat( + "Power Drain: {0:0.0#} {1}/s\n", + this.BaseLinkCost.PacketResourceCost / this.packetInterval, + this.requiredResource == "ElectricCharge" ? "EC" : this.requiredResource + ); + } + else + { + sb.AppendLine(); + + sb.AppendFormat( + "Nominal Packet Cost: {0:0.0#} {1}\n", + this.BaseLinkCost.PacketResourceCost, + this.requiredResource == "ElectricCharge" ? "EC" : this.requiredResource + ); + sb.AppendFormat( + "Nominal Power Drain: {0:0.0#} {1}/s\n", + this.BaseLinkCost.PacketResourceCost / this.packetInterval, + this.requiredResource == "ElectricCharge" ? "EC" : this.requiredResource + ); + + sb.AppendLine(); + + sb.AppendFormat( + "Outside Nominal Range...\n...Maximum Power Drain: {0:0.0#} {1}/s\n", + this.BaseLinkCost.PacketResourceCost / this.packetInterval * this.maxPowerFactor, + this.requiredResource == "ElectricCharge" ? "EC" : this.requiredResource + ); + } + + return sb.ToString(); } } @@ -622,6 +731,17 @@ } /// + /// Recalculates the transmission rates. + /// + public void RecalculateTransmissionRates() + { + if (this.relay != null) + { + this.relay.RecalculateTransmissionRates(); + } + } + + /// /// Finds the nearest relay. /// public void FindNearestRelay() @@ -637,37 +757,23 @@ /// returns false. /// /// List of to transmit. - /// Callback function - public new void TransmitData(List dataQueue, Callback callback) + public new void TransmitData(List dataQueue) { this.LogDebug( "TransmitData(List dataQueue, Callback callback) called. dataQueue.Count={0}", dataQueue.Count ); - this.FindNearestRelay(); - - this.PreTransmit_SetPacketSize(); - this.PreTransmit_SetPacketResourceCost(); - if (this.CanTransmit()) { ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT); this.LogDebug( "CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}", - dataQueue.SPrint(), - callback == null ? "null" : callback.ToString() + dataQueue.SPrint() ); - if (callback == null) - { - base.TransmitData(dataQueue); - } - else - { - base.TransmitData(dataQueue, callback); - } + base.TransmitData(dataQueue); } else { @@ -760,31 +866,11 @@ } /// - /// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit - /// returns false. - /// - /// List of to transmit. - public new void TransmitData(List dataQueue) - { - this.LogDebug( - "TransmitData(List dataQueue) called, dataQueue.Count={0}", - dataQueue.Count - ); - - this.TransmitData(dataQueue, null); - } - - /// /// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit /// returns false. /// public new void StartTransmission() { - this.FindNearestRelay(); - - PreTransmit_SetPacketSize (); - PreTransmit_SetPacketResourceCost (); - this.LogDebug( "distance: " + this.CurrentLinkSqrDistance + " packetSize: " + this.packetSize @@ -859,6 +945,19 @@ } /// + /// Recalculates the max range; useful for making sure we're using additive ranges when enabled. + /// + public void RecalculateMaxRange() + { + this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance; + + #if DEBUG + this.Log("Recalculated max range: sqrt({0}) * {1} = {2}", + this.maxPowerFactor, this.nominalTransmitDistance, this.maxTransmitDistance); + #endif + } + + /// /// Returns a that represents the current . /// /// A that represents the current . @@ -868,7 +967,14 @@ { string msg; - sb.Append(this.part.partInfo.title); + if (this.part != null && this.part.partInfo != null) + { + sb.Append(this.part.partInfo.title); + } + else + { + sb.Append(this.GetType().Name); + } if (vessel != null) { @@ -926,47 +1032,7 @@ this.LogDebug(this.ErrorMsg.message); - ScreenMessages.PostScreenMessage(this.ErrorMsg, false); - } - - // Before transmission, set packetResourceCost. Per above, packet cost increases with the square of - // distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point - // transmission fails (see CanTransmit). - private void PreTransmit_SetPacketResourceCost() - { - if (ARConfiguration.FixedPowerCost || this.CurrentLinkSqrDistance <= this.NominalLinkSqrDistance) - { - base.packetResourceCost = this._basepacketResourceCost; - } - else - { - float rangeFactor = (float)(this.CurrentLinkSqrDistance / this.NominalLinkSqrDistance); - - base.packetResourceCost = this._basepacketResourceCost * rangeFactor; - } - - base.packetResourceCost *= this.packetThrottle / 100f; - } - - // Before transmission, set packetSize. Per above, packet size increases with the inverse square of - // distance. packetSize maxes out at _basepacketSize * maxDataFactor. - private void PreTransmit_SetPacketSize() - { - if (!ARConfiguration.FixedPowerCost && this.CurrentLinkSqrDistance >= this.NominalLinkSqrDistance) - { - base.packetSize = this._basepacketSize; - } - else - { - float rangeFactor = (float)(this.NominalLinkSqrDistance / this.CurrentLinkSqrDistance); - - base.packetSize = Mathf.Min( - this._basepacketSize * rangeFactor, - this._basepacketSize * this.maxDataFactor - ); - } - - base.packetSize *= this.packetThrottle / 100f; + ScreenMessages.PostScreenMessage(this.ErrorMsg); } private string buildTransmitMessage() @@ -1002,8 +1068,8 @@ [KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)] public void DebugInfo() { - PreTransmit_SetPacketSize (); - PreTransmit_SetPacketResourceCost (); + if (this.relay != null) + this.relay.RecalculateTransmissionRates(); DebugPartModule.DumpClassObject(this); } @@ -1011,27 +1077,26 @@ [KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)] public void PrintAllVessels() { - StringBuilder sb = Tools.GetStringBuilder(); - - sb.Append("Dumping FlightGlobals.Vessels:"); - - Vessel vessel; - for (int i = 0; i < FlightGlobals.Vessels.Count; i++) - { - vessel = FlightGlobals.Vessels[i]; - sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id); - } + using (PooledStringBuilder sb = PooledStringBuilder.Get()) + { + sb.Append("Dumping FlightGlobals.Vessels:"); + + Vessel vessel; + for (int i = 0; i < FlightGlobals.Vessels.Count; i++) + { + vessel = FlightGlobals.Vessels[i]; + sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id); + } - Tools.PostDebugMessage(sb.ToString()); - - Tools.PutStringBuilder(sb); + ToadicusTools.Logging.PostDebugMessage(sb.ToString()); + } } - [KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)] + /*[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)] public void DumpRelayDB() { RelayDatabase.Instance.Dump(); - } + }*/ #endif } } --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -39,7 +39,7 @@ // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion("1.10.1.*")] +[assembly: AssemblyVersion("1.11.3.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] --- a/ProtoAntennaRelay.cs +++ b/ProtoAntennaRelay.cs @@ -73,6 +73,69 @@ } /// + /// Gets the base link resource rate in EC/MiT. + /// + /// The base link resource rate in EC/MiT. + public RelayDataCost BaseLinkCost + { + get; + private set; + } + + /// + /// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas + /// to be scored in terms of joules/byte + /// + public double DataResourceCost + { + get + { + if (this.CanTransmit()) + { + return this.moduleRef.DataResourceCost; + } + else + { + return float.PositiveInfinity; + } + } + } + + /// + /// Gets the packet throttle. + /// + /// The packet throttle in range [0..100]. + public float PacketThrottle + { + get + { + if (this.moduleRef == null) + { + return float.NaN; + } + + return this.moduleRef.PacketThrottle; + } + } + + /// + /// Gets the max data factor. + /// + /// The max data factor. + public float MaxDataFactor + { + get + { + if (this.moduleRef == null) + { + return float.NaN; + } + + return this.moduleRef.MaxDataFactor; + } + } + + /// /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. /// public override double nominalTransmitDistance @@ -133,6 +196,17 @@ } /// + /// Recalculates the max range; useful for making sure we're using additive ranges when enabled. + /// + public void RecalculateMaxRange() + { + if (this.moduleRef != null) + { + this.moduleRef.RecalculateMaxRange(); + } + } + + /// /// Returns a that represents the current . /// /// A that represents the current . @@ -162,6 +236,8 @@ this.protoPart = pps; this.Log("constructed ({0})", this.GetType().Name); + + this.RecalculateMaxRange(); } } } --- /dev/null +++ b/RelayDataCost.cs @@ -1,1 +1,259 @@ - +// AntennaRange +// +// RelayLinkCost.cs +// +// Copyright © 2016, toadicus +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +using System; +using UnityEngine; + +namespace AntennaRange +{ + /// + /// A struct representing the cost of sending data through a relay. + /// + public struct RelayDataCost : IComparable, IComparable + { + /// + /// A RelayDataCost object representing infinitely high cost. + /// + public static readonly RelayDataCost Infinity = new RelayDataCost(float.PositiveInfinity, 0f); + + /// Left + /// Right + public static RelayDataCost operator+ (RelayDataCost one, RelayDataCost two) + { + RelayDataCost gcd, lcd; + + if (one.PacketSize > two.PacketSize) { + gcd = one; + lcd = two; + } + else + { + gcd = two; + lcd = one; + } + + if (lcd.PacketSize != 0f) + { + float mul = gcd.PacketSize / lcd.PacketSize; + + lcd.PacketSize *= mul; + lcd.PacketResourceCost *= mul; + } + + return new RelayDataCost(gcd.PacketResourceCost + lcd.PacketResourceCost, gcd.PacketSize + lcd.PacketSize); + } + + /// RelayDataCost to be negated + public static RelayDataCost operator- (RelayDataCost only) + { + return new RelayDataCost(-only.PacketResourceCost, only.PacketSize); + } + + /// Left. + /// Right. + public static RelayDataCost operator- (RelayDataCost left, RelayDataCost right) + { + return left + -right; + } + + /// Left. + /// Right. + public static bool operator> (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) > 0); + } + + /// Left. + /// Right. + public static bool operator>= (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) >= 0); + } + + /// Left. + /// Right. + public static bool operator== (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) == 0); + } + + /// Left. + /// Right. + public static bool operator<= (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) <= 0); + } + + /// Left. + /// Right. + public static bool operator< (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) < 0); + } + + /// Left. + /// Right. + public static bool operator!= (RelayDataCost left, RelayDataCost right) + { + return (left.CompareTo(right) != 0); + } + + /// + /// The resource cost of a packet, in EC/packet + /// + public float PacketResourceCost; + + /// + /// The data capacity of a packet, MiT/packet + /// + public float PacketSize; + + /// + /// Gets the resource cost per unit data, in EC/MiT + /// + /// The resource cost per unit data, in EC/MiT + public double ResourceCostPerData + { + get + { + if (this.PacketSize == 0f || float.IsInfinity(this.PacketResourceCost)) + { + return double.PositiveInfinity; + } + + return (double)this.PacketResourceCost / (double)this.PacketSize; + } + } + + /// + /// Initializes a new instance of the struct. + /// + /// resource cost of a packet, in EC/packet + /// data capacity of a packet, MiT/packet + public RelayDataCost(float cost, float size) + { + this.PacketResourceCost = cost; + this.PacketSize = size; + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return string.Format("{0} EC/MiT", this.ResourceCostPerData); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + if (obj is RelayDataCost) + { + return ((RelayDataCost)obj == this); + } + else + { + return false; + } + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. + public override int GetHashCode() + { + int hash = 137; + + hash = (hash * 61) + this.PacketResourceCost.GetHashCode(); + hash = (hash * 61) + this.PacketSize.GetHashCode(); + + return hash; + } + + /// + /// Compares this RelayDataCost to another object. Throws NotImplementedException for objects + /// that are not RelayDataCost objects + /// + /// -1 if this is less than o, 0 if this equals o, 1 if this is greater than o + /// Another object + public int CompareTo(object o) + { + if (o is RelayDataCost) + { + return this.CompareTo((RelayDataCost)o); + } + + throw new NotImplementedException( + string.Format( + "Cannot compare {0} to foreign type {1}", + this.GetType().Name, + o.GetType().Name + ) + ); + } + + /// + /// Compares this RelayDataCost to another object. Throws NotImplementedException for objects + /// that are not RelayDataCost objects + /// + /// -1 if this is less than o, 0 if this equals o, 1 if this is greater than o + /// Another RelayDataCost + public int CompareTo(RelayDataCost o) + { + int val; + + if (this.ResourceCostPerData > o.ResourceCostPerData) + { + val = 1; + } + else if (this.ResourceCostPerData < o.ResourceCostPerData) + { + val = -1; + } + else + { + val = 0; + } + + #if DEBUG + Debug.LogErrorFormat("RelayLinkCost comparing {0} to {1}, returning {2}", this, o, val); + #endif + + return val; + } + } +} + + --- a/RelayDatabase.cs +++ b/RelayDatabase.cs @@ -419,6 +419,8 @@ GameEvents.onVesselWasModified.Add(this.onVesselEvent); GameEvents.onVesselChange.Add(this.onVesselEvent); GameEvents.onVesselDestroy.Add(this.onVesselEvent); + GameEvents.onVesselGoOnRails.Add(this.onVesselEvent); + GameEvents.onVesselGoOffRails.Add(this.onVesselEvent); GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange); GameEvents.onPartCouple.Add(this.onFromPartToPartEvent); GameEvents.onPartUndock.Add(this.onPartEvent); @@ -431,6 +433,8 @@ GameEvents.onVesselWasModified.Remove(this.onVesselEvent); GameEvents.onVesselChange.Remove(this.onVesselEvent); GameEvents.onVesselDestroy.Remove(this.onVesselEvent); + GameEvents.onVesselGoOnRails.Remove(this.onVesselEvent); + GameEvents.onVesselGoOffRails.Add(this.onVesselEvent); GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange); GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent); GameEvents.onPartUndock.Remove(this.onPartEvent); @@ -450,28 +454,27 @@ #if DEBUG public void Dump() { - StringBuilder sb = Tools.GetStringBuilder(); - - sb.Append("Dumping RelayDatabase:"); - - var dbEnum = this.relayDatabase.GetEnumerator(); - IList vesselRelays; - while (dbEnum.MoveNext()) - { - sb.AppendFormat("\nVessel {0}:", dbEnum.Current.Key); - - vesselRelays = dbEnum.Current.Value; - IAntennaRelay relay; - for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) - { - relay = vesselRelays[rIdx]; - sb.AppendFormat("\n\t{0}", relay.ToString()); - } - } - - Logging.PostDebugMessage(sb.ToString()); - - Tools.PutStringBuilder(sb); + using (ToadicusTools.Text.PooledStringBuilder sb = ToadicusTools.Text.PooledStringBuilder.Get()) + { + sb.Append("Dumping RelayDatabase:"); + + var dbEnum = this.relayDatabase.GetEnumerator(); + IList vesselRelays; + while (dbEnum.MoveNext()) + { + sb.AppendFormat("\nVessel {0}:", dbEnum.Current.Key); + + vesselRelays = dbEnum.Current.Value; + IAntennaRelay relay; + for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) + { + relay = vesselRelays[rIdx]; + sb.AppendFormat("\n\t{0}", relay.ToString()); + } + } + + Logging.PostDebugMessage(sb.ToString()); + } } #endif } --- a/RelayExtensions.cs +++ b/RelayExtensions.cs @@ -141,6 +141,79 @@ public static double SqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) { return relayOne.vessel.sqrDistanceTo(relayTwo.vessel); + } + + /// + /// Returns the square of the maximum link range between two relays. + /// + /// The maximum link range between two relays. + /// Relay one. + /// Relay two. + public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) + { + if (ARConfiguration.UseAdditiveRanges) + { + return relayOne.maxTransmitDistance * relayTwo.maxTransmitDistance; + } + else + { + return relayOne.maxTransmitDistance * relayOne.maxTransmitDistance; + } + } + + /// + /// Returns the square of the maximum link range between a relay and Kerbin. + /// + /// The maximum link range between a relay and Kerbin. + /// Relay one. + /// A CelestialBody (must be Kerbin). + public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, CelestialBody body) + { + if (body != AntennaRelay.Kerbin) + { + return 0d; + } + + if (ARConfiguration.UseAdditiveRanges) + { + return relayOne.maxTransmitDistance * ARConfiguration.KerbinRelayRange; + } + else + { + return relayOne.maxTransmitDistance * relayOne.maxTransmitDistance; + } + } + + /// + /// Determines if relayOne is in range of the specified relayTwo. + /// + /// true if relayOne is in range of the specifie relayTwo; otherwise, false. + /// Relay one. + /// Relay two. + public static bool IsInRangeOf(this AntennaRelay relayOne, IAntennaRelay relayTwo) + { + if (relayOne == null || relayTwo == null) + { + return false; + } + + return relayOne.SqrDistanceTo(relayTwo) <= relayOne.MaxLinkSqrDistanceTo(relayTwo); + } + + /// + /// Determines if relayOne is in range of the specified body. + /// + /// true if relayOne is in range of the specified body; otherwise, false. + /// Relay one. + /// Body. + public static bool IsInRangeOf(this AntennaRelay relayOne, CelestialBody body) + { + if (relayOne == null || body == null) + { + return false; + } + + return relayOne.SqrDistanceTo(body) <= relayOne.MaxLinkSqrDistanceTo(body); } ///