示例#1
0
        static void Render_supplies(Panel p, Vessel v, VesselData vd, VesselResources resources)
        {
            int supplies = 0;

            // for each supply
            foreach (Supply supply in Profile.supplies)
            {
                // get resource info
                ResourceInfo res = resources.GetResource(v, supply.resource);

                // only show estimate if the resource is present
                if (res.Capacity <= 1e-10)
                {
                    continue;
                }

                // render panel title, if not done already
                if (supplies == 0)
                {
                    p.AddSection(Local.TELEMETRY_SUPPLIES);                               //"SUPPLIES"
                }
                // determine label
                var    resource = PartResourceLibrary.Instance.resourceDefinitions[supply.resource];
                string label    = Lib.SpacesOnCaps(resource.displayName).ToLower();

                StringBuilder sb = new StringBuilder();

                sb.Append("<align=left />");
                if (res.AverageRate != 0.0)
                {
                    sb.Append(Lib.Color(res.AverageRate > 0.0,
                                        Lib.BuildString("+", Lib.HumanReadableRate(Math.Abs(res.AverageRate))), Lib.Kolor.PosRate,
                                        Lib.BuildString("-", Lib.HumanReadableRate(Math.Abs(res.AverageRate))), Lib.Kolor.NegRate,
                                        true));
                }
                else
                {
                    sb.Append("<b>");
                    sb.Append(Local.TELEMETRY_nochange);                    //no change
                    sb.Append("</b>");
                }

                if (res.AverageRate < 0.0 && res.Level < 0.0001)
                {
                    sb.Append(" <i>");
                    sb.Append(Local.TELEMETRY_empty);                    //(empty)
                    sb.Append("</i>");
                }
                else if (res.AverageRate > 0.0 && res.Level > 0.9999)
                {
                    sb.Append(" <i>");
                    sb.Append(Local.TELEMETRY_full);                    //(full)
                    sb.Append("</i>");
                }
                else
                {
                    sb.Append("   ");                  // spaces to prevent alignement issues
                }
                sb.Append("\t");
                sb.Append(res.Amount.ToString("F1"));
                sb.Append("/");
                sb.Append(res.Capacity.ToString("F1"));
                sb.Append(" (");
                sb.Append(res.Level.ToString("P0"));
                sb.Append(")");

                List <SupplyData.ResourceBrokerRate> brokers = vd.Supply(supply.resource).ResourceBrokers;
                if (brokers.Count > 0)
                {
                    sb.Append("\n<b>------------    \t------------</b>");
                    foreach (SupplyData.ResourceBrokerRate rb in brokers)
                    {
                        sb.Append("\n");
                        sb.Append(Lib.Color(rb.rate > 0.0,
                                            Lib.BuildString("+", Lib.HumanReadableRate(Math.Abs(rb.rate)), "   "), Lib.Kolor.PosRate,             // spaces to mitigate alignement issues
                                            Lib.BuildString("-", Lib.HumanReadableRate(Math.Abs(rb.rate)), "   "), Lib.Kolor.NegRate,             // spaces to mitigate alignement issues
                                            true));
                        sb.Append("\t");
                        sb.Append(rb.broker.Title);
                    }
                }

                string rate_tooltip = sb.ToString();

                // finally, render resource supply
                p.AddContent(label, Lib.HumanReadableDuration(res.DepletionTime()), rate_tooltip);
                ++supplies;
            }
        }
示例#2
0
        /// <summary>synchronize resources from cache to vessel</summary>
        /// <remarks>
        /// this function will also sync from vessel to cache so you can always use the
        /// ResourceInfo interface to get information about resources
        /// </remarks>
        public void Sync(Vessel v, VesselData vd, double elapsed_s, List <PartResource> loadedResList, List <ProtoPartResourceSnapshot> unloadedResList)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Resource.Sync");
            // # OVERVIEW
            // - consumption/production is accumulated in "Deferred", then this function called
            // - save previous step amount/capacity
            // - part loop 1 : detect new amount/capacity
            // - if amount has changed, this mean there is non-Kerbalism producers/consumers on the vessel
            // - if non-Kerbalism producers are detected on a loaded vessel, prevent high timewarp rates
            // - clamp "Deferred" to amount/capacity
            // - part loop 2 : apply "Deferred" to all parts
            // - apply "Deferred" to amount
            // - calculate change rate per-second
            // - calculate resource level
            // - reset deferred

            // # NOTE
            // It is impossible to guarantee coherency in resource simulation of loaded vessels,
            // if consumers/producers external to the resource cache exist in the vessel (#96).
            // Such is the case for example on loaded vessels with stock solar panels.
            // The effect is that the whole resource simulation become dependent on timestep again.
            // From the user point-of-view, there are two cases:
            // - (A) the timestep-dependent error is smaller than capacity
            // - (B) the timestep-dependent error is bigger than capacity
            // In case [A], there are no consequences except a slightly wrong computed level and rate.
            // In case [B], the simulation became incoherent and from that point anything can happen,
            // like for example insta-death by co2 poisoning or climatization.
            // To avoid the consequences of [B]:
            // - we hacked the solar panels to use the resource cache (SolarPanelFixer)
            // - we detect incoherency on loaded vessels, and forbid the two highest warp speeds

            // remember vessel-wide amount currently known, to calculate rate and detect non-Kerbalism brokers
            double oldAmount = Amount;

            // remember vessel-wide capacity currently known, to detect flow state changes
            double oldCapacity = Capacity;

            // iterate over all enabled resource containers and detect amount/capacity again
            // - this detect production/consumption from stock and third-party mods
            //   that by-pass the resource cache, and flow state changes in general
            Amount   = 0.0;
            Capacity = 0.0;

            if (v.loaded)
            {
                foreach (PartResource r in loadedResList)
                {
                    Amount   += r.amount;
                    Capacity += r.maxAmount;
                }
            }
            else
            {
                foreach (ProtoPartResourceSnapshot r in unloadedResList)
                {
                    Amount   += r.amount;
                    Capacity += r.maxAmount;
                }
            }

            // As we haven't yet synchronized anything, changes to amount can only come from non-Kerbalism producers or consumers
            double unsupportedBrokersRate = Amount - oldAmount;

            // Avoid false detection due to precision errors
            if (Math.Abs(unsupportedBrokersRate) < 1e-05)
            {
                unsupportedBrokersRate = 0.0;
            }
            // Calculate the resulting rate
            unsupportedBrokersRate /= elapsed_s;

            // Detect flow state changes
            bool flowStateChanged = Capacity - oldCapacity > 1e-05;

            // clamp consumption/production to vessel amount/capacity
            // - if deferred is negative, then amount is guaranteed to be greater than zero
            // - if deferred is positive, then capacity - amount is guaranteed to be greater than zero
            Deferred = Lib.Clamp(Deferred, -Amount, Capacity - Amount);

            // apply deferred consumption/production to all parts, simulating ALL_VESSEL_BALANCED
            // - iterating again is faster than using a temporary list of valid PartResources
            // - avoid very small values in deferred consumption/production
            if (Math.Abs(Deferred) > 1e-10)
            {
                if (v.loaded)
                {
                    foreach (PartResource r in loadedResList)
                    {
                        // calculate consumption/production coefficient for the part
                        double k = Deferred < 0.0
                                                  ? r.amount / Amount
                                                  : (r.maxAmount - r.amount) / (Capacity - Amount);

                        // apply deferred consumption/production
                        r.amount += Deferred * k;
                    }
                }
                else
                {
                    foreach (ProtoPartResourceSnapshot r in unloadedResList)
                    {
                        // calculate consumption/production coefficient for the part
                        double k = Deferred < 0.0
                                                  ? r.amount / Amount
                                                  : (r.maxAmount - r.amount) / (Capacity - Amount);

                        // apply deferred consumption/production
                        r.amount += Deferred * k;
                    }
                }
            }

            // update amount, to get correct rate and levels at all times
            Amount += Deferred;

            // reset deferred production/consumption
            Deferred = 0.0;

            // recalculate level
            Level = Capacity > 0.0 ? Amount / Capacity : 0.0;

            // calculate rate of change per-second
            // - don't update rate during warp blending (stock modules have instabilities during warp blending)
            // - ignore interval-based rules consumption/production
            if (!v.loaded || !Kerbalism.WarpBlending)
            {
                Rate = (Amount - oldAmount - intervalRuleAmount) / elapsed_s;
            }

            // calculate average rate of change per-second from interval-based rules
            intervalRulesRate = 0.0;
            foreach (var rb in intervalRuleBrokersRates)
            {
                intervalRulesRate += rb.Value;
            }

            // AverageRate is the exposed property that include simulated rate from interval-based rules.
            // For consistency with how "Rate" is calculated, we only add the simulated rate if there is some capacity or amount for it to have an effect
            AverageRate = Rate;
            if ((intervalRulesRate > 0.0 && Level < 1.0) || (intervalRulesRate < 0.0 && Level > 0.0))
            {
                AverageRate += intervalRulesRate;
            }

            // For visualization purpose, update the VesselData.supplies brokers list, merging all detected sources :
            // - normal brokers that use Consume() or Produce()
            // - "virtual" brokers from interval-based rules
            // - non-Kerbalism brokers (aggregated rate)
            vd.Supply(ResourceName).UpdateResourceBrokers(brokersResourceAmounts, intervalRuleBrokersRates, unsupportedBrokersRate, elapsed_s);

            //Lib.Log("RESOURCE UPDATE : " + v);
            //foreach (var rb in vd.Supply(ResourceName).ResourceBrokers)
            //	Lib.Log(Lib.BuildString(ResourceName, " : ", rb.rate.ToString("+0.000000;-0.000000;+0.000000"), "/s (", rb.name, ")"));
            //Lib.Log("RESOURCE UPDATE END");

            // reset amount added/removed from interval-based rules
            IntervalRuleHappened = intervalRuleAmount > 0.0;
            intervalRuleAmount   = 0.0;

            // if incoherent producers are detected, do not allow high timewarp speed
            // - can be disabled in settings
            // - unloaded vessels can't be incoherent, we are in full control there
            // - ignore incoherent consumers (no negative consequences for player)
            // - ignore flow state changes (avoid issue with process controllers and other things)
            if (Settings.EnforceCoherency && v.loaded && TimeWarp.CurrentRate > 1000.0 && unsupportedBrokersRate > 0.0 && !flowStateChanged)
            {
                Message.Post
                (
                    Severity.warning,
                    Lib.BuildString
                    (
                        !v.isActiveVessel ? Lib.BuildString("On <b>", v.vesselName, "</b>\na ") : "A ",
                        "producer of <b>", ResourceName, "</b> has\n",
                        "incoherent behavior at high warp speed.\n",
                        "<i>Unload the vessel before warping</i>"
                    )
                );
                Lib.StopWarp(1000.0);
            }

            // reset brokers
            brokersResourceAmounts.Clear();
            intervalRuleBrokersRates.Clear();

            // reset amount added/removed from interval-based rules
            intervalRuleAmount = 0.0;
            UnityEngine.Profiling.Profiler.EndSample();
        }