Exception when feeding prisoners from a Nutrient dispenser

Started by DoctorVanGogh, July 06, 2017, 11:31:22 PM

Previous topic - Next topic

DoctorVanGogh

This is a bug in vanilla Rimworld, but it's only exposed when you install certain mods - so no idea if this is the right place or the Mods subsection.
---
If you have pawns that are non-people who try to feed an incapacitated prisoner from a nutrient dispenser you get an error in the log on every job:

Exception in BreadthFirstTraverse: System.NullReferenceException: Object reference not set to an instance of an object
  at RimWorld.FoodUtility+<BestFoodSourceOnMap>c__AnonStorey259.<>m__1 (Verse.Thing t) [0x00000] in <filename unknown>:0
  at Verse.GenClosest+<RegionwiseBFSWorker>c__AnonStorey5FC.<>m__BFF (Verse.Region r) [0x00000] in <filename unknown>:0
  at Verse.RegionTraverser+BFSWorker.BreadthFirstTraverseWork (Verse.Region root, Verse.RegionEntryPredicate entryCondition, Verse.RegionProcessor regionProcessor, Int32 maxRegions, RegionType traversableRegionTypes) [0x00000] in <filename unknown>:0
  at Verse.RegionTraverser.BreadthFirstTraverse (Verse.Region root, Verse.RegionEntryPredicate entryCondition, Verse.RegionProcessor regionProcessor, Int32 maxRegions, RegionType traversableRegionTypes) [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.RegionTraverser:BreadthFirstTraverse(Region, RegionEntryPredicate, RegionProcessor, Int32, RegionType)
Verse.GenClosest:RegionwiseBFSWorker(IntVec3, Map, ThingRequest, PathEndMode, TraverseParms, Predicate`1, Func`2, Int32, Int32, Single, RegionType, Boolean)
Verse.GenClosest:ClosestThingReachable(IntVec3, Map, ThingRequest, PathEndMode, TraverseParms, Single, Predicate`1, IEnumerable`1, Int32, Int32, Boolean, RegionType, Boolean)
RimWorld.FoodUtility:BestFoodSourceOnMap(Pawn, Pawn, Boolean, FoodPreferability, Boolean, Boolean, Boolean, Boolean, Boolean, Boolean, Boolean)
RimWorld.FoodUtility:TryFindBestFoodSourceFor(Pawn, Pawn, Boolean, Thing&, ThingDef&, Boolean, Boolean, Boolean, Boolean, Boolean)
RimWorld.WorkGiver_FeedPatient:HasJobOnThing(Pawn, Thing, Boolean)
AIRobot.<>c__DisplayClass1_1:<TryGiveJob>b__0(Thing)
Verse.<RegionwiseBFSWorker>c__AnonStorey5FC:<>m__BFF(Region)
Verse.BFSWorker:BreadthFirstTraverseWork(Region, RegionEntryPredicate, RegionProcessor, Int32, RegionType)
Verse.RegionTraverser:BreadthFirstTraverse(Region, RegionEntryPredicate, RegionProcessor, Int32, RegionType)
Verse.GenClosest:RegionwiseBFSWorker(IntVec3, Map, ThingRequest, PathEndMode, TraverseParms, Predicate`1, Func`2, Int32, Int32, Single, RegionType, Boolean)
Verse.GenClosest:ClosestThingReachable(IntVec3, Map, ThingRequest, PathEndMode, TraverseParms, Single, Predicate`1, IEnumerable`1, Int32, Int32, Boolean, RegionType, Boolean)
AIRobot.X2_JobGiver_Work:TryGiveJob(Pawn)
Verse.AI.ThinkNode_JobGiver:TryIssueJobPackage(Pawn, JobIssueParams)
Verse.AI.ThinkNode_PrioritySorter:TryIssueJobPackage(Pawn, JobIssueParams)
Verse.AI.ThinkNode_Priority:TryIssueJobPackage(Pawn, JobIssueParams)
RimWorld.ThinkNode_Conditional:TryIssueJobPackage(Pawn, JobIssueParams)
Verse.AI.ThinkNode_Priority:TryIssueJobPackage(Pawn, JobIssueParams)
Verse.AI.Pawn_JobTracker:DetermineNextJob(ThinkTreeDef&)
Verse.AI.Pawn_JobTracker:TryFindAndStartJob()
Verse.AI.Pawn_JobTracker:JobTrackerTick()
Verse.Pawn:Tick()
AIRobot.X2_AIRobot:Tick()
Verse.TickList:Tick()
Verse.TickManager:DoSingleTick()
Verse.TickManager:TickManagerUpdate()
Verse.Game:UpdatePlay()
Verse.Root_Play:Update()

While there's non vanilla code in the stack trace, the bug itself is completely from the base game.

RimWorld.FoodUtility+<BestFoodSourceOnMap>c__AnonStorey259.<>m__1 (or in non-decompiled source, just the RimWorld.FoodUtility.BestFoodSourceOnMap method) contains code near the bottom of:
Quote
...
Predicate<Thing> predicate = (Thing t) => foodValidator(t) && !FoodUtility.filtered.Contains(t) && t.def.ingestible.preferability > FoodPreferability.DesperateOnly && !t.IsNotFresh();
Predicate<Thing> validator = predicate;
bool ignoreEntirelyForbiddenRegions = flag;
thing = GenClosest.ClosestThingReachable(getter.Position, getter.Map, thingRequest, PathEndMode.ClosestTouch, TraverseParms.For(getter, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, null, 0, searchRegionsMax, false, RegionType.Set_Passable, ignoreEntirelyForbiddenRegions);
...
If the feeding pawn is a person this line never seems to be reached, the code exits early. With non-person pawns this check is run, and subsequently blows, since it's invoked with a Nutrient dispenser for the Thing - which has no ingestible, so access to it's preferability blows.

A simple fix would be a change of the highlighted piece to t.def.ingestible?.preferability > FoodPreferability.DesperateOnly to prevent any errors. But probably there's a substitution to the dispensed nutrient meal's properties intended there, like in earlier parts of the method.

Demo savegame (needs Miscellaneous 'CORE', Misc. Robots & Misc. Robots++): https://drive.google.com/open?id=0B2GJ6pU1K8cKYldGTTY0bXctYlE

Just hit the Activate gizmo on the ER bot to awake it and have it start the feeding jobs and trigger the error.



Offtopic: This contains a bare minimum savegame with a 5% world as an ultra compression 620kb 7zip.... How is this supposed to fit in 600kb as an attachment? ;)

Moderator's edit (Calahan)
Quote from: DoctorVanGogh on July 06, 2017, 11:31:22 PM
How is this supposed to fit in 600kb as an attachment? ;)
By using some 7zip wizardry to get it down to 540kb :D 

[attachment deleted by admin due to age]
Appreciate my mods? Buy me a coffee

ison

I think it happens for pawns who are RaceProps.ToolUser but aren't RaceProps.Humanlike. Fixed, thanks.