static ConnectionInfo OtherComms(Vessel v, KAntennaInfo antenna, HashSet <Guid> avoid_inf_recursion) { // hard-coded transmission rate and cost const double ext_rate = 0.064; const double ext_cost = 0.1; // if RemoteTech is present and enabled if (RemoteTech.Enabled()) { return(RemoteTech.Connected(v.id) ? new ConnectionInfo(LinkStatus.direct_link, ext_rate, ext_cost) : new ConnectionInfo(LinkStatus.no_link)); } // if CommNet is enabled else if (Features.KCommNet) { return(v.connection != null && v.connection.IsConnected ? new ConnectionInfo(LinkStatus.direct_link, ext_rate * v.connection.SignalStrength, ext_cost) : new ConnectionInfo(LinkStatus.no_link)); } // the simple stupid signal system else { return(new ConnectionInfo(LinkStatus.direct_link, ext_rate, ext_cost)); } }
public static ConnectionInfo connection(Vessel v) { // hard-coded transmission rate and cost const double ext_rate = 0.064; const double ext_cost = 0.1; // if RemoteTech is present and enabled if (RemoteTech.Enabled()) { if (RemoteTech.Connected(v.id) && !RemoteTech.ConnectedToKSC(v.id)) { return(new ConnectionInfo(LinkStatus.indirect_link, ext_rate, ext_cost)); } else if (RemoteTech.ConnectedToKSC(v.id)) { return(new ConnectionInfo(LinkStatus.direct_link, ext_rate, ext_cost)); } else { return(new ConnectionInfo(LinkStatus.no_link)); } } // if CommNet is enabled else if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { return(v.connection != null && v.connection.IsConnected ? new ConnectionInfo(LinkStatus.direct_link, ext_rate * v.connection.SignalStrength, ext_cost) : new ConnectionInfo(LinkStatus.no_link)); } // the simple stupid signal system else { return(new ConnectionInfo(LinkStatus.direct_link, ext_rate, ext_cost)); } }
public IEnumerator NetworkInitialized() { yield return(new WaitForSeconds(2)); Communications.NetworkInitialized = true; RemoteTech.Startup(); }
private void Init(Vessel v, bool powered, bool storm) { // are we connected if (RemoteTech.Connected(v.id)) { linked = RemoteTech.ConnectedToKSC(v.id); status = RemoteTech.TargetsKSC(v.id) ? (int)LinkStatus.direct_link : (int)LinkStatus.indirect_link; target_name = status == (int)LinkStatus.direct_link ? Lib.Ellipsis("DSN: " + (RemoteTech.NameTargetsKSC(v.id) ?? ""), 20) : Lib.Ellipsis(RemoteTech.NameFirstHopToKSC(v.id) ?? "", 20); Guid[] controlPath = null; if (linked) { controlPath = RemoteTech.GetCommsControlPath(v.id); } // Get the lowest rate in ControlPath if (controlPath != null) { // Get rate from the firstHop, each Hop will do the same logic, then we will have the lowest rate for the path if (controlPath.Length > 0) { double dist = RemoteTech.GetCommsDistance(v.id, controlPath[0]); strength = 1 - (dist / Math.Max(RemoteTech.GetCommsMaxDistance(v.id, controlPath[0]), 1)); strength = Math.Pow(strength, Settings.DataRateDampingExponentRT); // If using relay, get the lowest rate if (status != (int)LinkStatus.direct_link) { Vessel target = FlightGlobals.FindVessel(controlPath[0]); ConnectionInfo ci = target.KerbalismData().Connection; strength *= ci.strength; rate = Math.Min(ci.rate, rate * strength); } else { rate *= strength; } } control_path = new List <string[]>(); Guid i = v.id; foreach (Guid id in controlPath) { var name = Lib.Ellipsis(RemoteTech.GetSatelliteName(i) + " \\ " + RemoteTech.GetSatelliteName(id), 35); var value = Lib.HumanReadablePerc(Math.Ceiling((1 - (RemoteTech.GetCommsDistance(i, id) / RemoteTech.GetCommsMaxDistance(i, id))) * 10000) / 10000, "F2"); var tooltip = "Distance: " + Lib.HumanReadableDistance(RemoteTech.GetCommsDistance(i, id)) + "\nMax Distance: " + Lib.HumanReadableDistance(RemoteTech.GetCommsMaxDistance(i, id)); control_path.Add(new string[] { name, value, tooltip }); i = id; } } } // is loss of connection due to a blackout else if (RemoteTech.GetCommsBlackout(v.id)) { status = storm ? (int)LinkStatus.storm : (int)LinkStatus.plasma; } }
// return true if the vessel is subject to a signal blackout public static bool Blackout(Vessel v) { if (!RemoteTech.Enabled()) { return(false); } return(Cache.VesselInfo(v).blackout); }
bool render_vessel(Panel p, Vessel v) { // get vessel info vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) return false; // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (filtered() && vd.group != filter) return false; // get resource handler vessel_resources resources = ResourceCache.Get(v); // get vessel crew List<ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.header ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, Styles.ScaleStringLength(((page == MonitorPage.data || page == MonitorPage.log || selected_id == Guid.Empty) && !Lib.IsFlight()) ? 50 : 30)), "</b> <size=", Styles.ScaleInteger(9).ToString(), "><color=#cccccc>", Lib.Ellipsis(body_name, Styles.ScaleStringLength(8)), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator indicator_problems(p, v, vi, crew); // battery indicator indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) indicator_supplies(p, v, vi); // reliability indicator if (Features.Reliability) indicator_reliability(p, v, vi); // signal indicator if (RemoteTech.Enabled() || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) indicator_signal(p, v, vi); // done return true; }
public static void RTFailureHandler(Part part, string type, bool failure) { foreach (PartModule m in part.FindModulesImplementing <PartModule>()) { if (RemoteTech.IsAntenna(m)) { RemoteTech.SetBroken(m, failure); } } }
public void Start() { Icons.Initialize(); // set up the icon textures RemoteTech.EnableInSPC(); // allow RemoteTech Core to run in the Space Center // Set the loaded trigger to false, this we will load a new // settings after selecting a save game. This is necessary // for switching between saves without shutting down the KSP // instance. //Settings.Instance.SettingsLoaded = false; }
public void Start() { // reset the save game initialized flag Kerbalism.IsSaveGameInitDone = false; // things in here will be only called once per KSP launch, after loading // nearly everything is available at this point, including the Kopernicus patched bodies. if (!Kerbalism.IsCoreMainMenuInitDone) { Kerbalism.IsCoreMainMenuInitDone = true; } // things in here will be called every the player goes to the main menu RemoteTech.EnableInSPC(); // allow RemoteTech Core to run in the Space Center }
public static void Update(Vessel v, Vessel_Info vi, VesselData vd, double elapsed_s) { // do nothing if signal mechanic is disabled if (!Features.Signal && !Features.KCommNet && !RemoteTech.Enabled()) { return; } // get connection info ConnectionInfo conn = vi.connection; // maintain and send messages // - do not send messages for vessels without an antenna // - do not send messages during/after solar storms // - do not send messages for EVA kerbals if (conn.status != LinkStatus.no_antenna && !v.isEVA && v.situation != Vessel.Situations.PRELAUNCH) { if (!vd.msg_signal && !conn.linked) { vd.msg_signal = true; if (vd.cfg_signal && conn.status != LinkStatus.blackout) { string subtext = "Data transmission disabled"; if (vi.crew_count == 0) { switch (Settings.UnlinkedControl) { case UnlinkedCtrl.none: subtext = "Remote control disabled"; break; case UnlinkedCtrl.limited: subtext = "Limited control available"; break; } } Message.Post(Severity.warning, Lib.BuildString("Signal lost with <b>", v.vesselName, "</b>"), subtext); } } else if (vd.msg_signal && conn.linked) { vd.msg_signal = false; if (vd.cfg_signal && !Storm.JustEnded(v, elapsed_s)) { var path = conn.path; Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> signal is back"), path.Count == 0 ? "We got a direct link with the space center" : Lib.BuildString("Relayed by <b>", path[path.Count - 1].vesselName, "</b>")); } } } }
public static void update(Vessel v, vessel_info vi, VesselData vd, double elapsed_s) { // do nothing if signal mechanic is disabled if (!HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet && !RemoteTech.Enabled()) { return; } // get connection info ConnectionInfo conn = vi.connection; // maintain and send messages // - do not send messages for vessels without an antenna // - do not send messages during/after solar storms // - do not send messages for EVA kerbals if (conn.status != LinkStatus.no_antenna && !v.isEVA && v.situation != Vessel.Situations.PRELAUNCH) { if (!vd.msg_signal && !conn.linked) { vd.msg_signal = true; if (vd.cfg_signal && conn.status != LinkStatus.blackout) { string subtext = "Data transmission disabled"; if (vi.crew_count == 0) { switch (Settings.UnlinkedControl) { case UnlinkedCtrl.none: subtext = Localizer.Format("#KERBALISM_UI_noctrl"); break; case UnlinkedCtrl.limited: subtext = Localizer.Format("#KERBALISM_UI_limitedcontrol"); break; } } Message.Post(Severity.warning, Lib.BuildString(Localizer.Format("#KERBALISM_UI_signallost"), " <b>", v.vesselName, "</b>"), subtext); } } else if (vd.msg_signal && conn.linked) { vd.msg_signal = false; if (vd.cfg_signal && !Storm.JustEnded(v, elapsed_s)) { var path = conn.path; Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> ", Localizer.Format("#KERBALISM_UI_signalback")), path.Count == 0 ? Localizer.Format("#KERBALISM_UI_directlink") : Lib.BuildString(Localizer.Format("#KERBALISM_UI_relayby"), " <b>", path[path.Count - 1].vesselName, "</b>")); } } } }
// constructor /// <summary> Creates a <see cref="ConnectionInfo"/> object for the specified vessel from it's antenna modules</summary> public ConnectionInfo(Vessel v, bool powered, bool storm) { // set RemoteTech powered and storm state if (RemoteTech.Enabled) { RemoteTech.SetPoweredDown(v.id, !powered); RemoteTech.SetCommsBlackout(v.id, storm); } // return no connection if there is no ec left if (!powered) { // hysteresis delay if ((DB.Vessel(v).hyspos_signal >= 5.0)) { DB.Vessel(v).hyspos_signal = 5.0; DB.Vessel(v).hysneg_signal = 0.0; return; } DB.Vessel(v).hyspos_signal += 0.1; } else { // hysteresis delay DB.Vessel(v).hysneg_signal += 0.1; if (!(DB.Vessel(v).hysneg_signal >= 5.0)) { return; } DB.Vessel(v).hysneg_signal = 5.0; DB.Vessel(v).hyspos_signal = 0.0; } rate = 0.0; internal_cost = 0.0; external_cost = 0.0; // CommNet or simple signal system if (!RemoteTech.Enabled) { List <ModuleDataTransmitter> transmitters; // if vessel is loaded if (v.loaded) { // find transmitters transmitters = v.FindPartModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ModuleDeployableAntenna animation = t.part.FindModuleImplementing <ModuleDeployableAntenna>(); ModuleAnimateGeneric animationGeneric = t.part.FindModuleImplementing <ModuleAnimateGeneric>(); if (animation != null) { // only include data rate and ec cost if transmitter is extended if (animation.deployState == ModuleDeployablePart.DeployState.EXTENDED) { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } else if (animationGeneric != null) { // only include data rate and ec cost if transmitter is extended if (animationGeneric.animSpeed > 0) { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; transmitters = part_prefab.FindModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ProtoPartModuleSnapshot m = p.FindModule("ModuleDeployableAntenna") ?? p.FindModule("ModuleAnimateGeneric"); if (m != null) { // only include data rate and ec cost if transmitter is extended string deployState = Lib.Proto.GetString(m, "deployState"); float animSpeed = Lib.Proto.GetFloat(m, "animSpeed"); if (deployState == "EXTENDED" || animSpeed > 0) { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } } // if CommNet is enabled if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { if (v.connection != null) { // force CommNet update of unloaded vessels if (!v.loaded) { Lib.ReflectionValue(v.connection, "unloadedDoOnce", true); } // are we connected to DSN or control station(can be another vessel with 3 or more crew in CommNet) if (v.connection.IsConnected) { linked = true; status = v.connection.ControlPath.First.hopType == HopType.Home ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = v.connection.SignalStrength; if (status != LinkStatus.direct_link) { Vessel firstHop = Lib.CommNodeToVessel(v.Connection.ControlPath.First.end); // Get rate from the firstHop, each Hop will do the same logic, then we will have the min rate for whole path rate = Math.Min(Cache.VesselInfo(FlightGlobals.FindVessel(firstHop.id)).connection.rate, rate); } rate *= strength * PreferencesBasic.Instance.transmitFactor; target_name = Lib.Ellipsis(Localizer.Format(v.connection.ControlPath.First.end.displayName).Replace("Kerbin", "DSN"), 20); } // is loss of connection due to plasma blackout else if (Lib.ReflectionValue <bool>(v.connection, "inPlasma")) // calling InPlasma causes a StackOverflow :( { status = LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; } } // no connection else { rate = 0.0; internal_cost = 0.0; external_cost = 0.0; } return; } // the simple stupid always connected signal system linked = true; status = LinkStatus.direct_link; strength = 1; // 100 % target_name = "DSN: KSC"; return; } // RemoteTech signal system else { // if vessel is loaded if (v.loaded) { // find transmitters foreach (Part p in v.parts) { foreach (PartModule m in p.Modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include ec cost if transmitter is active if (Lib.ReflectionValue <bool>(m, "IsRTActive")) { rate += Lib.ReflectionValue <float>(m, "RTPacketSize") / Lib.ReflectionValue <float>(m, "RTPacketInterval"); external_cost += m.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; int index = 0; // module index foreach (ProtoPartModuleSnapshot m in p.modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active skip if index is out of range if (Lib.Proto.GetBool(m, "IsRTActive") && index < part_prefab.Modules.Count) { // get module prefab PartModule pm = part_prefab.Modules.GetModule(index); if (pm != null) { external_cost += pm.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; // only include data rate if vessel is connected float?packet_size = Lib.SafeReflectionValue <float>(pm, "RTPacketSize"); // workaround for old savegames if (packet_size == null) { Lib.Debug("Old SaveGame PartModule ModuleRTAntenna for part {0} on unloaded vessel {1}, using default values as a workaround", p.partName, v.vesselName); rate += 6.6666; // 6.67 Mb/s } else { rate += (float)packet_size / Lib.ReflectionValue <float>(pm, "RTPacketInterval"); } } else { Lib.Debug("Could not find PartModule ModuleRTAntenna for part {0} on unloaded vessel {1}, using default values as a workaround", p.partName, v.vesselName); rate += 6.6666; // 6.67 Mb/s in 100% factor external_cost += 0.025; // 25 W/s } } } index++; } } } // are we connected if (RemoteTech.Connected(v.id)) { linked = RemoteTech.ConnectedToKSC(v.id); status = RemoteTech.TargetsKSC(v.id) ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = RemoteTech.GetSignalDelay(v.id); target_name = status == LinkStatus.direct_link ? Lib.Ellipsis("DSN: " + (RemoteTech.NameTargetsKSC(v.id) ?? ""), 20) : Lib.Ellipsis(RemoteTech.NameFirstHopToKSC(v.id) ?? "", 20); if (linked) { controlPath = RemoteTech.GetCommsControlPath(v.id); } // Get the smaller rate of the path if (controlPath != null) { // Get rate from the firstHop, each Hop will do the same logic, then we will have the min rate for whole path if (controlPath.Length > 0) { rate = Math.Min(Cache.VesselInfo(FlightGlobals.FindVessel(controlPath[0])).connection.rate, rate); } } rate *= PreferencesBasic.Instance.transmitFactor; } // is loss of connection due to a blackout else if (RemoteTech.GetCommsBlackout(v.id)) { status = storm ? LinkStatus.storm : LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; } else { // no connection rate = 0.0; internal_cost = 0.0; external_cost = 0.0; } } }
/// <summary> /// Shows the Network status, ControlPath, Signal strength /// </summary> public static void ConnMan(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get info from the cache Vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel, leave the panel empty if (!vi.is_valid) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(40)), " <color=#cccccc>CONNECTION MANAGER</color>")); p.Width(Styles.ScaleWidthFloat(365.0f)); p.paneltype = Panel.PanelType.connection; // time-out simulation if (p.Timeout(vi)) { return; } // draw ControlPath section p.AddSection("CONTROL PATH"); if (vi.connection.linked) { if (RemoteTech.Enabled) { if (vi.connection.controlPath != null) { Guid i = v.id; foreach (Guid id in vi.connection.controlPath) { p.AddContent( Lib.Ellipsis(RemoteTech.GetSatelliteName(i) + " \\ " + RemoteTech.GetSatelliteName(id), 35), Lib.HumanReadablePerc(Math.Ceiling((1 - (RemoteTech.GetCommsDistance(i, id) / RemoteTech.GetCommsMaxDistance(i, id))) * 10000) / 10000, "F2"), "\nDistance: " + Lib.HumanReadableRange(RemoteTech.GetCommsDistance(i, id)) + "\nMax Distance: " + Lib.HumanReadableRange(RemoteTech.GetCommsMaxDistance(i, id))); i = id; } } } if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { foreach (CommLink link in v.connection.ControlPath) { double antennaPower = link.end.isHome ? link.start.antennaTransmit.power + link.start.antennaRelay.power : link.start.antennaTransmit.power; double signalStrength = 1 - ((link.start.position - link.end.position).magnitude / Math.Sqrt(antennaPower * link.end.antennaRelay.power)); signalStrength = (3 - (2 * signalStrength)) * Math.Pow(signalStrength, 2); p.AddContent( Lib.Ellipsis(Localizer.Format(link.end.name).Replace("Kerbin", "DSN"), 35), Lib.HumanReadablePerc(Math.Ceiling(signalStrength * 10000) / 10000, "F2"), "\nDistance: " + Lib.HumanReadableRange((link.start.position - link.end.position).magnitude) + "\nMax Distance: " + Lib.HumanReadableRange(Math.Sqrt((link.start.antennaTransmit.power + link.start.antennaRelay.power) * link.end.antennaRelay.power)) ); } } } else { p.AddContent("<i>no connection</i>", string.Empty); } }
// constructor /// <summary> Creates a <see cref="ConnectionInfo"/> object for the specified vessel from it's antenna modules</summary> public ConnectionInfo(Vessel v, bool powered, bool storm) { // set RemoteTech powered and storm state if (RemoteTech.Enabled) { RemoteTech.SetPoweredDown(v.id, !powered); RemoteTech.SetCommsBlackout(v.id, storm); } // return no connection if there is no ec left if (!powered) { // hysteresis delay if ((DB.Vessel(v).hyspos_signal >= 5.0)) { DB.Vessel(v).hyspos_signal = 5.0; DB.Vessel(v).hysneg_signal = 0.0; return; } DB.Vessel(v).hyspos_signal += 0.1; } else { // hysteresis delay DB.Vessel(v).hysneg_signal += 0.1; if (!(DB.Vessel(v).hysneg_signal >= 5.0)) { return; } DB.Vessel(v).hysneg_signal = 5.0; DB.Vessel(v).hyspos_signal = 0.0; } // CommNet or simple signal system if (!RemoteTech.Enabled) { List <ModuleDataTransmitter> transmitters; // if vessel is loaded if (v.loaded) { // find transmitters transmitters = v.FindPartModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ModuleDeployableAntenna animation = t.part.FindModuleImplementing <ModuleDeployableAntenna>(); if (animation != null) { // only include data rate and ec cost if transmitter is extended if (animation.deployState == ModuleDeployablePart.DeployState.EXTENDED) { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; transmitters = part_prefab.FindModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ProtoPartModuleSnapshot m = p.FindModule("ModuleDeployableAntenna"); if (m != null) { // only include data rate and ec cost if transmitter is extended string deployState = Lib.Proto.GetString(m, "deployState"); if (deployState == "EXTENDED") { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } } // if CommNet is enabled if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { // are we connected to DSN if (v.connection != null) { if (v.connection.IsConnected) { linked = true; status = v.connection.ControlPath.First.hopType == CommNet.HopType.Home ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = v.connection.SignalStrength; rate = rate * strength; target_name = Lib.Ellipsis(Localizer.Format(v.connection.ControlPath.First.end.displayName).Replace("Kerbin", "DSN"), 20); return; } // is loss of connection due to plasma blackout else if (Lib.ReflectionValue <bool>(v.connection, "inPlasma")) // calling InPlasma causes a StackOverflow :( { status = LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } } // no connection rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } // the simple stupid always connected signal system linked = true; status = LinkStatus.direct_link; strength = 1; // 100 % target_name = "DSN: KSC"; return; } // RemoteTech signal system else { // if vessel is loaded if (v.loaded) { // find transmitters foreach (Part p in v.parts) { foreach (PartModule m in p.Modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active if (Lib.ReflectionValue <bool>(m, "IsRTActive")) { rate += (Lib.ReflectionValue <float>(m, "RTPacketSize") / Lib.ReflectionValue <float>(m, "RTPacketInterval")); external_cost += m.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; int index = 0; // module index foreach (ProtoPartModuleSnapshot m in p.modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active skip if index is out of range if (Lib.Proto.GetBool(m, "IsRTActive") && index < part_prefab.Modules.Count) { // get module prefab PartModule pm = part_prefab.Modules.GetModule(index); if (pm != null) { rate += (Lib.ReflectionValue <float>(pm, "RTPacketSize") / Lib.ReflectionValue <float>(pm, "RTPacketInterval")); external_cost += pm.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; } else { Lib.DebugLog(String.Format("ConnectionInfo: Could not find PartModule ModuleRTAntenna for part {0} on unloaded vessel {1}, using default values as a workaround", p.partName, v.vesselName)); rate += 6.6666; // 6.67 Mb/s external_cost += 0.025; // 25 W/s } } } index++; } } } // are we connected if (RemoteTech.Connected(v.id)) { linked = RemoteTech.ConnectedToKSC(v.id); status = RemoteTech.TargetsKSC(v.id) ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = RemoteTech.GetSignalDelay(v.id); target_name = status == LinkStatus.direct_link ? Lib.Ellipsis("DSN: " + (RemoteTech.NameTargetsKSC(v.id) ?? ""), 20): Lib.Ellipsis(RemoteTech.NameFirstHopToKSC(v.id) ?? "", 20); return; } // is loss of connection due to a blackout else if (RemoteTech.GetCommsBlackout(v.id)) { status = storm ? LinkStatus.storm : LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } // no connection rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } }
public AntennaInfoRT(Vessel v, bool powered, bool storm) { RemoteTech.SetPoweredDown(v.id, !powered); RemoteTech.SetCommsBlackout(v.id, storm); // if vessel is loaded, don't calculate ec, RT already handle it. if (v.loaded) { // find transmitters foreach (Part p in v.parts) { foreach (PartModule m in p.Modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { ec += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active if (Lib.ReflectionValue <bool>(m, "IsRTActive")) { rate += (Lib.ReflectionValue <float>(m, "RTPacketSize") / Lib.ReflectionValue <float>(m, "RTPacketInterval")); } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; foreach (ProtoPartModuleSnapshot m in p.modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { ec += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active if (Lib.Proto.GetBool(m, "IsRTActive")) { bool mFound = false; // get all modules in prefab foreach (PartModule pm in part_prefab.Modules) { if (pm.moduleName == m.moduleName) { mFound = true; ModuleResource mResource = pm.resHandler.inputResources.Find(r => r.name == "ElectricCharge"); float? packet_size = Lib.SafeReflectionValue <float>(pm, "RTPacketSize"); float? packet_Interval = Lib.SafeReflectionValue <float>(pm, "RTPacketInterval"); // workaround for old savegames if (mResource == null || packet_size == null || packet_Interval == null) { Lib.LogDebugStack("Old SaveGame PartModule ModuleRTAntenna for part '{0}' on unloaded vessel '{1}', using default values as a workaround", Lib.LogLevel.Message, p.partName, v.vesselName); Lib.LogDebugStack("ElectricCharge isNull: '{0}', RTPacketSize isNull: '{1}', RTPacketInterval isNull: '{2}'", Lib.LogLevel.Message, mResource == null, packet_size == null, packet_Interval == null); rate += 6.6666; // 6.67 Mb/s in 100% factor ec += 0.025; // 25 W/s } else { rate += (float)packet_size / (float)packet_Interval; ec += mResource.rate; } } } if (!mFound) { Lib.LogDebugStack("Could not find PartModule ModuleRTAntenna for part {0} on unloaded vessel {1}, using default values as a workaround", Lib.LogLevel.Message, p.partName, v.vesselName); rate += 6.6666; // 6.67 Mb/s in 100% factor ec += 0.025; // 25 W/s } } } } } } Init(v, powered, storm); }
// constructor /// <summary> Creates a <see cref="ConnectionInfo"/> object for the specified vessel from it's antenna modules</summary> public ConnectionInfo(Vessel v, bool powered, bool storm) { // set RemoteTech powered and storm state if (RemoteTech.Enabled) { RemoteTech.SetPoweredDown(v.id, !powered); RemoteTech.SetCommsBlackout(v.id, storm); } // return no connection if there is no ec left if (!powered) { // hysteresis delay if (DB.Vessel(v).hyspos_signal >= 5.0) { DB.Vessel(v).hyspos_signal = 5.0; DB.Vessel(v).hysneg_signal = 0.0; return; } DB.Vessel(v).hyspos_signal += 0.1; } else { // hysteresis delay DB.Vessel(v).hysneg_signal += 0.1; if (DB.Vessel(v).hysneg_signal < 5.0) { return; } DB.Vessel(v).hysneg_signal = 5.0; DB.Vessel(v).hyspos_signal = 0.0; } // if CommNet is enabled if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { AntennaInfoCommNet antennaInfo = new AntennaInfoCommNet(v); if (v.connection != null) { // force CommNet update of unloaded vessels if (!v.loaded) { Lib.ReflectionValue(v.connection, "unloadedDoOnce", true); } // are we connected to DSN if (v.connection.IsConnected) { ec = antennaInfo.ec; rate = antennaInfo.rate * PreferencesBasic.Instance.transmitFactor; linked = true; status = v.connection.ControlPath.First.hopType == CommNet.HopType.Home ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = v.connection.SignalStrength; rate *= strength; target_name = Lib.Ellipsis(Localizer.Format(v.connection.ControlPath.First.end.displayName).Replace("Kerbin", "DSN"), 20); if (status != LinkStatus.direct_link) { Vessel firstHop = Lib.CommNodeToVessel(v.Connection.ControlPath.First.end); // Get rate from the firstHop, each Hop will do the same logic, then we will have the min rate for whole path rate = Math.Min(Cache.VesselInfo(FlightGlobals.FindVessel(firstHop.id)).connection.rate, rate); } } // is loss of connection due to plasma blackout else if (Lib.ReflectionValue <bool>(v.connection, "inPlasma")) // calling InPlasma causes a StackOverflow :( { status = LinkStatus.plasma; } } // if nothing has changed, no connection return; } // RemoteTech signal system else if (RemoteTech.Enabled) { AntennaInfoRT antennaInfo = new AntennaInfoRT(v); // are we connected if (RemoteTech.Connected(v.id)) { ec = antennaInfo.ec; rate = antennaInfo.rate * PreferencesBasic.Instance.transmitFactor; linked = RemoteTech.ConnectedToKSC(v.id); status = RemoteTech.TargetsKSC(v.id) ? LinkStatus.direct_link : LinkStatus.indirect_link; target_name = status == LinkStatus.direct_link ? Lib.Ellipsis("DSN: " + (RemoteTech.NameTargetsKSC(v.id) ?? ""), 20) : Lib.Ellipsis(RemoteTech.NameFirstHopToKSC(v.id) ?? "", 20); if (linked) { controlPath = RemoteTech.GetCommsControlPath(v.id); } // Get the lowest rate in ControlPath if (controlPath != null) { // Get rate from the firstHop, each Hop will do the same logic, then we will have the lowest rate for the path if (controlPath.Length > 0) { double dist = RemoteTech.GetCommsDistance(v.id, controlPath[0]); strength = 1 - (dist / Math.Max(RemoteTech.GetCommsMaxDistance(v.id, controlPath[0]), 1)); // If using relay, get the lowest rate if (status != LinkStatus.direct_link) { Vessel target = FlightGlobals.FindVessel(controlPath[0]); strength *= Cache.VesselInfo(target).connection.strength; rate = Math.Min(Cache.VesselInfo(target).connection.rate, rate * strength); } else { rate *= strength; } } } } // is loss of connection due to a blackout else if (RemoteTech.GetCommsBlackout(v.id)) { status = storm ? LinkStatus.storm : LinkStatus.plasma; } // if nothing has changed, no connection return; } // the simple stupid always connected signal system else { AntennaInfoCommNet antennaInfo = new AntennaInfoCommNet(v); ec = antennaInfo.ec * 0.16; // Consume 16% of the stock ec. Workaround for drain consumption with CommNet, ec consumption turns similar of RT rate = antennaInfo.rate * PreferencesBasic.Instance.transmitFactor; linked = true; status = LinkStatus.direct_link; strength = 1; // 100 % target_name = "DSN: KSC"; } }
void FixedUpdate() { // remove control locks in any case Misc.clearLocks(); // do nothing if paused if (Lib.IsPaused()) { return; } // maintain elapsed_s, converting to double only once // and detect warp blending double fixedDeltaTime = TimeWarp.fixedDeltaTime; if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon) { warp_blending = 0; } else { ++warp_blending; } elapsed_s = fixedDeltaTime; // evict oldest entry from vessel cache Cache.update(); // store info for oldest unloaded vessel double last_time = 0.0; Vessel last_v = null; vessel_info last_vi = null; VesselData last_vd = null; vessel_resources last_resources = null; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // get vessel info from the cache vessel_info vi = Cache.VesselInfo(v); // set locks for active vessel if (v.isActiveVessel) { Misc.setLocks(v, vi); } // maintain eva dead animation and helmet state if (v.loaded && v.isEVA) { EVA.update(v); } // keep track of rescue mission kerbals, and gift resources to their vessels on discovery if (v.loaded && vi.is_vessel) { // manage rescue mission mechanics Misc.manageRescueMission(v); } // do nothing else for invalid vessels if (!vi.is_valid) { continue; } // get vessel data from db VesselData vd = DB.Vessel(v); // get resource cache vessel_resources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { // show belt warnings Radiation.beltWarnings(v, vi, vd); // update storm data Storm.update(v, vi, vd, elapsed_s); //handle RemoteTech stuff RemoteTech.update(v, vi, vd, elapsed_s); Communications.update(v, vi, vd, elapsed_s); // consume ec for transmission, and transmit science data Science.update(v, vi, vd, resources, elapsed_s); // apply rules Profile.Execute(v, vi, vd, resources, elapsed_s); // apply deferred requests resources.Sync(v, elapsed_s); // call automation scripts vd.computer.automate(v, vi, resources); // remove from unloaded data container unloaded.Remove(vi.id); } // if unloaded else { // get unloaded data, or create an empty one unloaded_data ud; if (!unloaded.TryGetValue(vi.id, out ud)) { ud = new unloaded_data(); unloaded.Add(vi.id, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vi = vi; last_vd = vd; last_resources = resources; } } } // if the oldest unloaded vessel was selected if (last_v != null) { // show belt warnings Radiation.beltWarnings(last_v, last_vi, last_vd); // update storm data Storm.update(last_v, last_vi, last_vd, last_time); //handle RemoteTech stuff RemoteTech.update(last_v, last_vi, last_vd, last_time); Communications.update(last_v, last_vi, last_vd, last_time); // consume ec for transmission, and transmit science Science.update(last_v, last_vi, last_vd, last_resources, last_time); // apply rules Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time); // simulate modules in background Background.update(last_v, last_vi, last_vd, last_resources, last_time); // apply deferred requests last_resources.Sync(last_v, last_time); // call automation scripts last_vd.computer.automate(last_v, last_vi, last_resources); // remove from unloaded data container unloaded.Remove(last_vi.id); } // update storm data for one body per-step if (storm_bodies.Count > 0) { storm_bodies.ForEach(k => k.time += elapsed_s); storm_data sd = storm_bodies[storm_index]; Storm.update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; } }
public static void config(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get info from the cache vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel, leave the panel empty if (!vi.is_valid) { return; } // set metadata p.title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " <color=#cccccc>VESSEL CONFIG</color>")); p.width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.config; // time-out simulation if (p.timeout(vi)) { return; } // get data from db VesselData vd = DB.Vessel(v); // toggle rendering string tooltip; if (Features.Reliability) { p.section("RENDERING"); } if (Features.Reliability) { tooltip = "Highlight failed components"; p.content("highlight malfunctions", string.Empty, tooltip); p.icon(vd.cfg_highlights ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_highlights)); } // toggle messages p.section("MESSAGES"); tooltip = "Receive a message when\nElectricCharge level is low"; p.content("battery", string.Empty, tooltip); p.icon(vd.cfg_ec ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_ec)); if (Features.Supplies) { tooltip = "Receive a message when\nsupply resources level is low"; p.content("supply", string.Empty, tooltip); p.icon(vd.cfg_supply ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_supply)); } if (RemoteTech.Enabled() || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { tooltip = "Receive a message when signal is lost or obtained"; p.content("signal", string.Empty, tooltip); p.icon(vd.cfg_signal ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_signal)); } if (Features.Reliability) { tooltip = "Receive a message\nwhen a component fail"; p.content("reliability", string.Empty, tooltip); p.icon(vd.cfg_malfunction ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_malfunction)); } if (Features.SpaceWeather) { tooltip = "Receive a message\nduring CME events"; p.content("storm", string.Empty, tooltip); p.icon(vd.cfg_storm ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_storm)); } if (Features.Automation) { tooltip = "Receive a message when\nscripts are executed"; p.content("script", string.Empty, tooltip); p.icon(vd.cfg_script ? Icons.toggle_green : Icons.toggle_red, tooltip, () => p.toggle(ref vd.cfg_script)); } }
// apply type-specific hacks to enable/disable the module void Apply(bool b) { switch (type) { case "ProcessController": if (b) { foreach (PartModule m in modules) { Lib.SetResourceFlow(part, (m as ProcessController).resource, false); } } break; case "ModuleDeployableSolarPanel": case "ModuleDeployableRadiator": if (b) { part.FindModelComponents <Animation>().ForEach(k => k.Stop()); } break; case "ModuleLight": if (b) { foreach (PartModule m in modules) { ModuleLight light = m as ModuleLight; if (light.animationName.Length > 0) { new Animator(part, light.animationName).Still(0.0f); } else { part.FindModelComponents <Light>().ForEach(k => k.enabled = false); } } } break; case "ModuleEngines": case "ModuleEnginesFX": if (b) { foreach (PartModule m in modules) { (m as ModuleEngines).Shutdown(); } } break; case "ModuleEnginesRF": if (b) { foreach (PartModule m in modules) { Lib.ReflectionCall(m, "Shutdown"); } } break; case "ModuleScienceExperiment": if (b) { foreach (PartModule m in modules) { (m as ModuleScienceExperiment).SetInoperable(); } } break; } if (RemoteTech.Enabled) { foreach (PartModule m in part.FindModulesImplementing <PartModule>()) { if (RemoteTech.IsAntenna(m)) { RemoteTech.SetBroken(m, b); } } } }
bool Render_Vessel(Panel p, Vessel v) { // get vessel info Vessel_Info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) { return(false); } if (!Lib.IsVessel(v)) { return(false); } // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (Filtered() && vd.group != filter) { return(false); } // get resource handler Vessel_Resources resources = ResourceCache.Get(v); // get vessel crew List <ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.SetHeader ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, 20), "</b> <size=9><color=#cccccc>", Lib.Ellipsis(body_name, 8), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator Indicator_Problems(p, v, vi, crew); // battery indicator Indicator_EC(p, v, vi); // supply indicator if (Features.Supplies) { Indicator_Supplies(p, v, vi); } // reliability indicator if (Features.Reliability) { Indicator_Reliability(p, v, vi); } // signal indicator if (Features.Signal || Features.KCommNet || RemoteTech.Enabled()) { Indicator_Signal(p, v, vi); } // done return(true); }
void indicator_signal(Panel p, Vessel v, vessel_info vi) { ConnectionInfo conn = vi.connection; if (RemoteTech.Enabled()) { double signal_delay = RemoteTech.GetShortestSignalDelay(v.id); string signal_str = ""; if (signal_delay < Double.Epsilon) { signal_str = "none"; } else { signal_str = KSPUtil.dateTimeFormatter.PrintTimeStampCompact(signal_delay, false, false); } string tooltip_rt = Lib.BuildString( "<align=left />", "connected\t<b>", conn.linked ? "yes" : "no", "</b>\n", "delay\t\t<b>", conn.linked ? signal_str : "no connection", "</b>\n", "rate\t\t<b>", Lib.HumanReadableDataRate(vi.connection.rate), "</b>" ); Texture image_rt = Icons.signal_red; if (RemoteTech.Connected(v.id)) image_rt = Icons.signal_white; if (RemoteTech.Connected(v.id) && !RemoteTech.ConnectedToKSC(v.id)) image_rt = Icons.signal_yellow; if (vi.blackout || RemoteTech.GetCommsBlackout(v.id)) { image_rt = Icons.signal_red; tooltip_rt += "\n\n<color=red><i>Blackout</i></color>"; } p.icon(image_rt, tooltip_rt); return; } // target name string target_str = string.Empty; switch (vi.connection.status) { case LinkStatus.direct_link: target_str = ("DSN"); break; case LinkStatus.indirect_link: target_str = vi.connection.path[vi.connection.path.Count - 1].vesselName; break; default: target_str = "none"; break; } // transmitted label, content and tooltip string comms_label = vi.relaying.Length == 0 ? "transmitting" : "relaying"; string comms_str = vi.connection.linked ? "telemetry" : "nothing"; string comms_tooltip = string.Empty; if (vi.relaying.Length > 0) { ExperimentInfo exp = Science.experiment(vi.relaying); comms_str = exp.name; comms_tooltip = exp.fullname; } else if (vi.transmitting.Length > 0) { ExperimentInfo exp = Science.experiment(vi.transmitting); comms_str = exp.name; comms_tooltip = exp.fullname; } string tooltip = Lib.BuildString ( "<align=left />", "connected\t<b>", vi.connection.linked ? "yes" : "no", "</b>\n", "rate\t\t<b>", Lib.HumanReadableDataRate(vi.connection.rate), "</b>\n", "target\t\t<b>", target_str, "</b>\n", comms_label, "\t<b>", comms_str, "</b>" ); Texture image = Icons.signal_red; switch (conn.status) { case LinkStatus.direct_link: image = vi.connection.rate > 0.005 ? Icons.signal_white : Icons.signal_yellow; break; case LinkStatus.indirect_link: image = vi.connection.rate > 0.005 ? Icons.signal_white : Icons.signal_yellow; tooltip += "\n\n<color=yellow>Signal relayed</color>"; break; case LinkStatus.no_link: image = Icons.signal_red; break; case LinkStatus.no_antenna: image = Icons.signal_red; tooltip += "\n\n<color=red>No antenna</color>"; break; case LinkStatus.blackout: image = Icons.signal_red; tooltip += "\n\n<color=red><i>Blackout</i></color>"; break; } p.icon(image, tooltip); }