Exception in DebugTools with ingredientless Recipes

Started by DoctorVanGogh, July 28, 2017, 09:03:25 PM

Previous topic - Next topic

DoctorVanGogh

This is a bug in Rimworld core, but can only be exposed by a mod - any mod.

If you create a RecipeDef (doesn't matter if in xml or an assembly) it is perfectly valid to create that Recipe without any ingredients.

Code (xml) Select

<RecipeDef>
<defName>Bar</defName>
<products>
<WoodLog>1</WoodLog>
</products>
</RecipeDef>


If you then try to use DebugTools and check all the recipes, this will cause an InvalidOperationException:

Exception filling window for Verse.Dialog_DebugLogMenu: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Operation is not valid due to the current state of the object
  at System.Linq.Enumerable.First[IngredientCount] (IEnumerable`1 source) [0x00000] in <filename unknown>:0
  at Verse.DataAnalysisTableMaker.<DoTable_Recipes>m__9AC (Verse.RecipeDef d) [0x00000] in <filename unknown>:0
  at Verse.DataAnalysisTableMaker+<DoTable_Recipes>c__AnonStorey56F.<>m__9B2 (Verse.RecipeDef d) [0x00000] in <filename unknown>:0
  at Verse.DebugTables.MakeTablesDialog[RecipeDef] (IEnumerable`1 dataSources, Verse.TableDataGetter`1[] getters) [0x00000] in <filename unknown>:0
  at Verse.DataAnalysisTableMaker.DoTable_Recipes () [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0
  at Verse.Dialog_DebugLogMenu+<DoListingItems>c__AnonStorey5D1.<>m__BAB () [0x00000] in <filename unknown>:0
  at Verse.Dialog_DebugOptionLister.DebugAction (System.String label, System.Action action) [0x00000] in <filename unknown>:0
  at Verse.Dialog_DebugLogMenu.DoListingItems () [0x00000] in <filename unknown>:0
  at Verse.Dialog_OptionLister.DoWindowContents (Rect inRect) [0x00000] in <filename unknown>:0
  at Verse.Window+<WindowOnGUI>c__AnonStorey2C4.<>m__1B1 (Int32 x) [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.<WindowOnGUI>c__AnonStorey2C4:<>m__1B1(Int32)
UnityEngine.GUI:CallWindowDelegate(WindowFunction, Int32, GUISkin, Int32, Single, Single, GUIStyle)


Turns out DataAnalysisTableMaker.DoTable_Recipes() (or rather one of it's inline delegates) makes an assumption that there is always at least one ingredient defined on a recipe:
Quote from: decompiled code, so may not be 100% correct
   ...
   Func<RecipeDef, float> cheapestProductsVal = delegate(RecipeDef d)
   {
      ThingDef thingDef = d.ingredients.First<IngredientCount>().filter.AllowedThingDefs.MinBy((ThingDef td) => td.BaseMarketValue);
      float num = 0f;
      foreach (ThingCountClass current in d.products)
      {
         num += current.thingDef.GetStatValueAbstract(StatDefOf.MarketValue, (!current.thingDef.MadeFromStuff) ? null : thingDef) * (float)current.count;
      }
      return num;
   };
   ...
You'll probably want to use a FirstOrDefault there instead (If there's no ingredient there's nothing to use as stuff).

I've attached a 'demo' mod that triggers the issue.






[attachment deleted by admin: too old]
Appreciate my mods? Buy me a coffee

ison

Such recipes will now be simply ignored by this debug log. Thanks for reporting.