public static link_data Link(Vessel v, Vector3d position, antenna_data antenna, bool blackout, HashSet <Guid> avoid_inf_recursion) { // assume linked if signal mechanic is disabled if (!Kerbalism.features.signal) { return(new link_data(true, link_status.direct_link, double.MaxValue)); } // if it has no antenna if (antenna.range <= double.Epsilon) { return(new link_data(false, link_status.no_antenna, 0.0)); } // if there is a storm and the vessel is inside a magnetosphere if (blackout) { return(new link_data(false, link_status.no_link, 0.0)); } // store raytracing data Vector3d dir; double dist; bool visible; // raytrace home body visible = Sim.RaytraceBody(v, position, FlightGlobals.GetHomeBody(), out dir, out dist); dist = visible && antenna.range > dist ? dist : double.MaxValue; // if directly linked if (antenna.range > dist) { return(new link_data(true, link_status.direct_link, dist)); } // for each other vessel foreach (Vessel w in FlightGlobals.Vessels) { // do not test with itself if (v == w) { continue; } // skip vessels already in this chain if (avoid_inf_recursion.Contains(w.id)) { continue; } // get vessel from the cache // note: safe because we are avoiding infinite recursion vessel_info wi = Cache.VesselInfo(w); // skip invalid vessels if (!wi.is_valid) { continue; } // skip non-relays and non-linked relays if (wi.antenna.relay_range <= double.Epsilon || !wi.link.linked) { continue; } // raytrace the other vessel visible = Sim.RaytraceVessel(v, w, position, wi.position, out dir, out dist); dist = visible && antenna.range > dist ? dist : double.MaxValue; // if indirectly linked // note: relays with no EC have zero relay_range // note: avoid relay loops if (antenna.range > dist && wi.antenna.relay_range > dist && !wi.link.path.Contains(v)) { // create indirect link data link_data link = new link_data(wi.link); // update the link data and return it link.status = link_status.indirect_link; link.distance = dist; //< store distance of last link link.path.Add(w); return(link); } } // no link return(new link_data(false, link_status.no_link, 0.0)); }
// build the visibility caches void BuildVisibility() { // get home body CelestialBody home = FlightGlobals.GetHomeBody(); // build direct visibility cache direct_visibility_cache.Clear(); foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // get antenna data antenna_data ad = antennas[v.id]; // raytrace home body Vector3d dir; double dist = 0.0; bool visible = Sim.RaytraceBody(v, home, out dir, out dist); dist = Math.Abs(dist); //< avoid problem below water level // store in visibility cache // note: we store distance & visibility flag at the same time direct_visibility_cache.Add(v.id, visible && dist < ad.range ? dist : 0.0); } // build indirect visibility cache indirect_visibility_cache.Clear(); foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // get antenna data antenna_data v_ad = antennas[v.id]; // for each vessel foreach (Vessel w in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(w)) { continue; } // do not test with itself if (v == w) { continue; } // do not compute visibility when both vessels have a direct link // rationale: optimization, the indirect visibility it never used in this case if (direct_visibility_cache[v.id] > double.Epsilon && direct_visibility_cache[w.id] > double.Epsilon) { continue; } // generate merged guid Guid id = Lib.CombineGuid(v.id, w.id); // avoid raycasting the same pair twice if (indirect_visibility_cache.ContainsKey(id)) { continue; } // get antenna data antenna_data w_ad = antennas[w.id]; // raytrace the vessel Vector3d dir; double dist = 0.0; bool visible = Sim.RaytraceVessel(v, w, out dir, out dist); // store visibility in cache // note: we store distance & visibility flag at the same time // note: relay visibility is asymmetric, done at link build time indirect_visibility_cache.Add(id, visible && dist < Math.Min(v_ad.range, w_ad.range) ? dist : 0.0); } } }
public static ConnectionInfo connection(Vessel v, Vector3d position, AntennaInfo antenna, bool blackout, HashSet <Guid> avoid_inf_recursion) { // if signal mechanic is disabled, use RemoteTech/CommNet/S4 if (!Features.Signal) { return(OtherComms(v)); } // if it has no antenna if (antenna.no_antenna) { return(new ConnectionInfo(LinkStatus.no_antenna)); } // if there is a storm and the vessel is inside a magnetosphere if (blackout) { return(new ConnectionInfo(LinkStatus.blackout)); } // store raytracing data Vector3d dir; double dist; bool visible; // store other data double rate; List <ConnectionInfo> connections = new List <ConnectionInfo>(); // raytrace home body visible = Sim.RaytraceBody(v, position, FlightGlobals.GetHomeBody(), out dir, out dist); // get rate rate = antenna.direct_rate(dist); // if directly linked if (visible && rate > Settings.ControlRate) { ConnectionInfo conn = new ConnectionInfo(LinkStatus.direct_link, rate, antenna.direct_cost); connections.Add(conn); } // for each other vessel foreach (Vessel w in FlightGlobals.Vessels) { // do not test with itself if (v == w) { continue; } // skip vessels already in this chain if (avoid_inf_recursion.Contains(w.id)) { continue; } // get vessel from the cache // - when: // . cache is empty (eg: new savegame loaded) // - we avoid single-tick wrong paths arising from this situation: // . vessel A is directly linked // . vessel B is indirectly linked through A // . cache is cleared (after loading a savegame) // . cache of A is computed // . in turn, cache of B is computed ignoring A (and stored) // . until cache of B is re-computed, B will result incorrectly not linked // - in this way: // . cache of A is computed // . in turn, cache of B is computed ignoring A (but not stored) // . cache of B is then computed correctly // . do not degenerate into O(N^3) by using non-optimal path vessel_info wi; if (!Cache.HasVesselInfo(w, out wi)) { if (connections.Count > 0) { continue; } else { wi = new vessel_info(w, Lib.VesselID(w), 0); } } // skip invalid vessels if (!wi.is_valid) { continue; } // skip non-relays and non-linked relays if (!wi.antenna.is_relay || !wi.connection.linked) { continue; } // raytrace the other vessel visible = Sim.RaytraceVessel(v, w, position, Lib.VesselPosition(w), out dir, out dist); // get rate rate = antenna.indirect_rate(dist, wi.antenna); // if indirectly linked // - relays with no EC have zero relay_range // - avoid relay loops if (visible && rate > Settings.ControlRate && !wi.connection.path.Contains(v)) { // create indirect link data ConnectionInfo conn = new ConnectionInfo(wi.connection); // update the link data and return it conn.status = LinkStatus.indirect_link; conn.rate = Math.Min(conn.rate, rate); conn.cost = antenna.indirect_cost; conn.path.Add(w); connections.Add(conn); } } // if at least a connection has been found if (connections.Count > 0) { // select the best connection double best_rate = 0.0; int best_index = 0; for (int i = 0; i < connections.Count; ++i) { if (connections[i].rate > best_rate) { best_rate = connections[i].rate; best_index = i; } } // and return it return(connections[best_index]); } // no link return(new ConnectionInfo(LinkStatus.no_link)); }