EDCD / EDDI

Companion application for Elite Dangerous
Other
437 stars 81 forks source link

System materials report error #2628

Closed Darkcyde13 closed 4 weeks ago

Darkcyde13 commented 4 weeks ago

What's Wrong (please be as specific as possible)

Expected

System material report to function normally and produce a report on the current system.

Observed

If you set reportMats to 2, to report everything, then EDDI gives an error: Error with System materials report script: exceeded maximum allowed number of cycles '10000'

Steps to reproduce

  1. Set reportMaterials to 2.
  2. Run the script with the test button.
  3. EDDI gives the above error.

Configuration

My Investigation

Investigation Notes

I was going over the new updates to the scripts in v4.1.0-alpha1, and encountered the above error. The default worked OK at first, and I couldn't see anything very different code-wise with mine. I reset to default, but changed the reportMats to 2, and then the default was also giving the error.

EDDI Logs

eddi.log

Player journals

N/A

Tkael commented 4 weeks ago

exceeded maximum allowed number of cycles indicates a code loop which exceeded the limit of 10,000 cycles. Unfortunately I'm not seeing the same error when I test the same script (with reportMats set to 2). It may have to do with the way the code is executing in the context of the specific star system you're in? 🤔

Darkcyde13 commented 4 weeks ago

To be honest I'm not in game. I was just clicking the test button, and when it works I'm getting: is most abundant on body 7 e a in this system.

In my version I also tried by setting reportSystem to some other system names. While still not in game, I've added this to a copy of the default:

{_ Test systems _}
{set reportSystem to SystemDetails("Chnuphis")}
{_ set reportSystem to SystemDetails("Othime")}
{_ set reportSystem to SystemDetails("Sol")}

Then I've uncommented one at a time to test with. Using both Chnuphis and Sol I get the error. Using Othime, the script says nothing at all.

Tkael commented 4 weeks ago

I'm consistently getting "may be found in Encoded Emissions and High Grade Emissions signal sources and may be found in High Grade Emissions signal sources in this system.", e.g. the material names are not being spoken.

It seems to have to do with these lines of the default script.

    {_ Sort materials into alphabetical order _}
    {for signalType, mats in signalMaterials:
        {set sortMats to []}
        {for item in sort(mats, cmp(a, b)):
            {set sortMats to cat(sortMats, [item])}
        }
        {set signalDescriptions to cat(signalDescriptions, [cat(List(sortMats),
                                       " may be found in ", signalType, " signal sources")])}
    }

If I replace List(sortMats) with List(mats) then it works correctly (less any alphabetical sorting).

Tkael commented 4 weeks ago

If I replace {for item in sort(mats, cmp(a, b)): with {for item in sort(mats): then it seems to work just fine. Revised snippet:

    {_ Sort materials into alphabetical order _}
    {for signalType, mats in signalMaterials:
        {set sortMats to []}
        {for item in sort(mats):
            {set sortMats to cat(sortMats, [item])}
        }
        {set signalDescriptions to cat(signalDescriptions, [cat(List(sortMats), " may be found in ", signalType, " signal sources")])}
    }

It looks like the updated Cottle library had a problem with the way I was using the cmp(a,b) callback (which I'll admit appears to have been improper).

Darkcyde13 commented 4 weeks ago

I tried adding you code snippet, and now I can get both EDDI default and my version to work in the system I'm in, Ross 780. That '7 e a' is the body in this system that has some materials. Carbon, Phosphorus, Selenium, and Sulphur are found on body 7 e a.

However, this only works if I choose 1 for reportMats. If I choose 2, I simply can't get the script to work at all for me. My version, or EDDI default, I just keep getting that error.

Likewise, if I set it to 1, then Sol and Chnuphis produce the error too. I have no idea why the default fails for me, but not for you.

Would a verbose log help at all?

Tkael commented 4 weeks ago

I tried adding you code snippet, and now I can get both EDDI default and my version to work in the system I'm in, Ross 780. That '7 e a' is the body in this system that has some materials. Carbon, Phosphorus, Selenium, and Sulphur are found on body 7 e a.

However, this only works if I choose 1 for reportMats. If I choose 2, I simply can't get the script to work at all for me. My version, or EDDI default, I just keep getting that error.

Likewise, if I set it to 1, then Sol and Chnuphis produce the error too. I have no idea why the default fails for me, but not for you.

Would a verbose log help at all?

Here's the configuration option that we're running into: https://cottle.readthedocs.io/en/latest/page/04-configuration.html#render-cycle-limit. I've set the limit at the default of 10,000 cycles and we seems to be hitting a very large number of function calls with the default script.

I've revised my local copy of the script to be a little more efficient when it comes to looking up volcanic bodies and that seems to have made a difference.

{_ Preferences _}
{set reportMats to 1}    {_ Set to 0 to hear about materials below your minimum inventory level _}
                         {_ Set to 1 to hear about materials below your desired inventory level _}
                         {_ Set to 2 to hear about all materials irrespective of inventory levels _}

{set reportMatConc to 1} {_ Set to 0 if you only want to hear about great material concentrations _}
                         {_ Set to 1 if you want to hear about good and great material concentrations _}
                         {_ Set to 2 if you want to hear about all material concentrations _}

{_ Fetch from context _}
{if state.eddi_context_system_name:
    {set reportSystem to SystemDetails(state.eddi_context_system_name)}
|else:
    {set reportSystem to SystemDetails(system.systemname)}
}

{set bodyMaterials to []}
{set signalMaterials to []}
{set bodyDescriptions to []}
{set signalDescriptions to []}
{set volcanicBodies to []}

{_ Define various functions to help with this script _}
{set addBodyMaterial(details, bodyName) to:
    {set bodyName to:
        {if details.bodyname != details.bodyshortname: body} {details.bodyshortname}
    }
    {if has(bodyMaterials, bodyName):
        {if find(bodyMaterials[bodyName], details.localizedName) = -1:
            {set newMats to cat(bodyMaterials[bodyName], [details.localizedName])}      
            {set bodyMaterials to union(bodyMaterials, [bodyName: newMats])}
        }
    |else:
        {set bodyMaterials to union(bodyMaterials, [bodyName: [details.localizedName]])}
    }
}

{set addSignalMaterial(materialName, signalSourceType) to:
    {if has(signalMaterials, materialName):
        {if find(signalMaterials[materialName], signalSourceType) = -1:
            {set locations to cat(signalMaterials[materialName], [signalSourceType])}
            {set signalMaterials to union(signalMaterials, [materialName:locations])}
        }
    |else:
        {set signalMaterials to union(signalMaterials, [materialName: [signalSourceType]])}
    }
}

{set isConcentrated(materialPresence) to:
    {if (reportMatConc >= 0 && materialPresence.percentage >= materialPresence.definition.greatpctbody)
      || (reportMatConc >= 1 && materialPresence.percentage >= materialPresence.definition.goodpctbody)
      || (reportMatConc >= 2):
        {return 1}
    }
    {return 0}
}

{set isSeleniumSpecialCase(materialPresence, bodyName, reportSystem) to:
    {_ Special case Selenium, which is bugged to appear in crystalline fragments _}
    {_ (i.e. with common elements) rather than crystalline shards (i.e. with rare elements) _}
    {_ when surface prospecting geological sites _}
    {if materialPresence.name = "Selenium" && volcanicBodies[bodyName]:
        {set commonMaterialPercentages to 0}
        {for m in reportbody.materials:
            {_ Sum up the percentages of common elements so that we can _}
            {_ calculate the relative chance of Selenium being present _}
            {if m.definition.Rarity.invariantName = "common":
                {set commonMaterialPercentages to commonMaterialPercentages + m.percentage}
            }
        }
        {if reportMatConc >= 0 && materialPresence.percentage > commonMaterialPercentages:
            {_ More than a 50% chance to drop Selenium from crystalline fragments _}
            {return 1}    
        |elif reportMatConc >= 1
          && materialPresence.percentage > commonMaterialPercentages / 2:
            {_ More than a 33% chance to drop Selenium from crystalline fragments _}
            {return 1}    
        }
    }
    {return 0}
}

{set isNeeded(invMaterial) to:
    {if (reportMats >= 0 && invMaterial.minimum && invMaterial.amount < invMaterial.minimum)
      || (reportMats >= 1 && invMaterial.desired && invMaterial.amount < invMaterial.desired)
      || (reportMats >= 2):
        {return 1}
    }
    {return 0}
}

{set materialInventory(materialName) to:
    {for material in materials:
        {if material.material = materialName:
            {return material}
        }
    }
}

{set getMaterialPresence(materialName, bodyName, reportSystem) to:
    {for body in reportSystem.bodies:
        {for materialPresence in body.materials:
            {if body.name = bodyName && material.material = materialName: 
                {return materialPresence}
            }
        }
    }
}

{_ Get information about populated system signal sources _}
{if reportSystem.population > 0:
    {set sigMaterials to []}
    {set signalsType to ["Encoded Emissions", "High Grade Emissions"]}

    {for sigType in signalsType:
        {if sigType = "Encoded Emissions":
            {if reportSystem.Faction.Allegiance.invariantName = "Federation":
                {set sigMaterials to cat(sigMaterials, ["Proprietary Composites"])}
            }
        |elif sigType = "High Grade Emissions":
            {if reportSystem.Faction.Allegiance.invariantName = "Federation":
                {set sigMaterials to cat(sigMaterials, ["Core Dynamics Composites"])}
                {set sigMaterials to cat(sigMaterials, ["Proprietary Composites"])}
            |elif reportSystem.Faction.Allegiance.invariantName = "Empire":
                {set sigMaterials to cat(sigMaterials, ["Imperial Shielding"])}
            }
            {for faction in reportSystem.factions:
                {for factionPresence in faction.presences:
                    {if reportSystem.systemname = factionPresence.systemName && factionPresence.influence >= 25:
                        {for factionState in factionPresence.ActiveStates:
                            {if factionState.invariantName = "Civil Unrest":
                                {set sigMaterials to cat(sigMaterials, ["Improvised Components"])}
                            |elif factionState.invariantName = "Civil War" || factionState.invariantName = "War":
                                {set sigMaterials to cat(sigMaterials, ["Military Grade Alloys", 
                                                                        "Military Supercapacitors"])}
                            |elif factionState.invariantName = "Outbreak":
                                {set sigMaterials to cat(sigMaterials, ["Pharmaceutical Isolators"])}
                            |elif factionState.invariantName = "Boom":
                                {set sigMaterials to cat(sigMaterials, ["Proto Heat Radiators",
                                                                        "Proto Light Alloys",
                                                                        "Proto Radiolic Alloys"])}
                            }
                        }
                    }
                }
            }
        }

        {_ Add material if it is 'needed' and the signal type is not already added for it _}
        {for invariantMaterial in sigMaterials:
            {set details to MaterialDetails(invariantMaterial)}
            {if isNeeded(materialInventory(details.localizedName)):
                {if !has(signalMaterials[details.localizedName], sigType):
                     {addSignalMaterial(details.localizedName, sigType)}
                }
            }
        }
        {_ Reset sigMaterials array _}
        {set sigMaterials to []}
    }

    {_ Reorganise signals array -> 'material':'sources' _}
    {set signalsOrganised to []}
    {for mat, sig in signalMaterials:
        {set sig to List(sig)}
        {set matList to []}
        {if has(signalsOrganised, sig):
            {set matList to cat(signalsOrganised[sig], [mat])}
            {set signalsOrganised to union(signalsOrganised, [sig:matList])}
        |else:
            {set signalsOrganised to union(signalsOrganised, [sig:[mat]])}
        }
    }
    {set signalMaterials to signalsOrganised}

    {_ Sort materials into alphabetical order _}
    {for signalType, mats in signalMaterials:
        {set sortMats to []}
        {for item in sort(mats):
            {set sortMats to cat(sortMats, [item])}
        }
        {set signalDescriptions to cat(signalDescriptions, [cat(List(sortMats), " may be found in ", signalType, " signal sources")])}
    }
}

{for compartment in ship.compartments:
    {if compartment.module.invariantName = "Planetary Vehicle Hangar":
        {set hasSRV to true}
    }
}

{if hasSRV:

    {_ Get information about volcanic bodies in the system }
    {for reportbody in reportSystem.bodies:
        {if reportbody.volcanism:
            {set volcanicBodies to cat(volcanicBodies, [reportbody.name:[reportbody.volcanism]])}
        }
    }

    {_ Get information about body surface prospecting materials _}
    {for material in reportSystem.surfaceelements:
        {set detail to MaterialDetails(material, reportSystem.systemname)}
        {if detail.bodyname:
            {if isNeeded(materialInventory(detail.localizedName)):
                {set materialPresence to getMaterialPresence(material.name, detail.bodyname, reportSystem)}
                {if isConcentrated(materialPresence)
                  || isSeleniumSpecialCase(materialPresence):
                    {addBodyMaterial(detail)}
                }
            }
        }
    }

    {for bodyName, mats in bodyMaterials:
        {_ Sort materials into alphanumerical order _}
        {set sortMats to []}
        {for item in sort(mats):
            {set sortMats to cat(sortMats, [item])}
        }
        {set bodyDescriptions to cat(bodyDescriptions, [
            cat(List(sortMats),
            when(len(sortMats) > 1, " are ", " is "),
            when(len(mats) > 1, OneOf("most prevalent", "most abundant"), "found"),            
            " on " P(bodyName, 'body')
            )])}
    }

    {_ Sort bodies into alphanumerical order _}
    {declare check(a, b) as:
        {set body1 to token(a, " on ", 1)}
        {set body2 to token(b, " on ", 1)}
        {return cmp(body1, body2)}
    }
    {set sortDesc to []}
    {for item in sort(bodyDescriptions, check):
        {set sortDesc to cat(sortDesc, [item])}
    }
    {set bodyDescriptions to cat(sortDesc)}
}

{if len(signalMaterials) > 0 && len(bodyMaterials) > 0:
    {set break to OneOf(". ", ". Additionally, ")}
}

{set descriptions to cat(List(signalDescriptions), break, List(bodyDescriptions))}
{if len(descriptions) > 0:
    {descriptions} in this system.
}

Please review and let me know what you think?