pardeike / Zombieland

Rimworld Mod featuring zombie hordes
https://www.patreon.com/pardeike
MIT License
55 stars 22 forks source link

nullpointer with vomiting zombie #11

Closed exuvo closed 6 years ago

exuvo commented 6 years ago

Another mod I use caused a zombie to vomit, which is obviously not supported. Is there a way to handle this case without affecting performance too much? A try catch around line 277 "jobs?.JobTrackerTick();" in Zombie.cs and clearing the job list if an exception occurs maybe?

The offending mod is A RimWorld of Magic because of the hediff source Projectile_FogOfTorment.

Object reference not set to an instance of an object at RimWorld.JobDriver_Vomit/cIterator0.<>m1 () <0x000a9> at (wrapper dynamic-method) Verse.AI.JobDriver.DriverTick_Patch1 (object) <0x002ba> at Verse.AI.Pawn_JobTracker.JobTrackerTick () <0x00242> at ZombieLand.Zombie.CustomTick () <0x00089> at ZombieLand.TickManager.ZombieTicking () <0x000fd> at ZombieLand.Patches/Verse_TickManager_TickManagerUpdate_Patch.ZombieTick () <0x00039> at (wrapper dynamic-method) Verse.TickManager.TickManagerUpdate_Patch0 (object) <0x000d9> at Verse.Game.UpdatePlay () <0x00015> at Verse.Root_Play.Update () <0x0004c>

The save data for the offending zombie:

<thing Class="ZombieLand.Zombie">
    <def>Zombie</def>
    <id>Zombie120686</id>
    <map>0</map>
    <pos>(285, 0, 158)</pos>
    <rot>1</rot>
    <faction>Faction_17</faction>
    <boughtItems />
    <guestArea>null</guestArea>
    <shoppingArea>null</shoppingArea>
    <drugPolicy IsNull="True" />
    <mightData IsNull="True" />
    <magicData IsNull="True" />
    <IsInitialized>True</IsInitialized>
    <abilityData>
        <pawn>Thing_Zombie120686</pawn>
        <abilityClass>AbilityUser.GenericCompAbilityUser</abilityClass>
        <Powers />
    </abilityData>
    <kindDef>Zombie</kindDef>
    <name Class="NameTriple">
        <first>Lawrence</first>
        <nick>Larry</nick>
        <last>Zicklez</last>
    </name>
    <mindState>
        <meleeThreat>Thing_Human187374</meleeThreat>
        <enemyTarget>null</enemyTarget>
        <knownExploder>null</knownExploder>
        <lastMannedThing>null</lastMannedThing>
        <lastAttackedTarget>Thing_Human187374</lastAttackedTarget>
        <thinkData>
            <keys />
            <values />
        </thinkData>
        <lastEngageTargetTick>2610945</lastEngageTargetTick>
        <lastAttackTargetTick>2610945</lastAttackTargetTick>
        <canFleeIndividual>True</canFleeIndividual>
        <lastMeleeThreatHarmTick>2610639</lastMeleeThreatHarmTick>
        <nextMoveOrderIsWait>False</nextMoveOrderIsWait>
        <lastHarmTick>2609077</lastHarmTick>
        <duty IsNull="True" />
        <mentalStateHandler>
            <curState IsNull="True" />
        </mentalStateHandler>
        <mentalBreaker />
        <inspirationHandler>
            <curState IsNull="True" />
        </inspirationHandler>
        <priorityWork>
            <prioritizedCell>(-1000, -1000, -1000)</prioritizedCell>
            <prioritizeTick>938975</prioritizeTick>
        </priorityWork>
    </mindState>
    <jobs>
        <curJob>
            <commTarget>null</commTarget>
            <verbToUse>null</verbToUse>
            <bill>null</bill>
            <lord>null</lord>
            <def>Vomit</def>
            <loadID>307920</loadID>
            <targetA>(286, 0, 159)</targetA>
            <targetQueueA IsNull="True" />
            <targetQueueB IsNull="True" />
            <countQueue IsNull="True" />
            <startTick>2611169</startTick>
            <placedThings IsNull="True" />
        </curJob>
        <curDriver Class="JobDriver_Vomit">
            <curToilIndex>0</curToilIndex>
            <ticksLeftThisToil>-4646</ticksLeftThisToil>
            <curToilCompleteMode>Never</curToilCompleteMode>
            <startTick>2611169</startTick>
            <ticksLeft>599</ticksLeft>
        </curDriver>
        <jobQueue>
            <jobs>
                <li>
                    <job>
                        <commTarget>null</commTarget>
                        <verbToUse>null</verbToUse>
                        <bill>null</bill>
                        <lord>null</lord>
                        <def>Stumble</def>
                        <loadID>307887</loadID>
                        <targetQueueA IsNull="True" />
                        <targetQueueB IsNull="True" />
                        <countQueue IsNull="True" />
                        <startTick>2610946</startTick>
                        <placedThings IsNull="True" />
                    </job>
                </li>
                <li>
                    <job>
                        <commTarget>null</commTarget>
                        <verbToUse>null</verbToUse>
                        <bill>null</bill>
                        <lord>null</lord>
                        <def>Stumble</def>
                        <loadID>307839</loadID>
                        <targetQueueA IsNull="True" />
                        <targetQueueB IsNull="True" />
                        <countQueue IsNull="True" />
                        <startTick>2610704</startTick>
                        <placedThings IsNull="True" />
                    </job>
                </li>
                <li>
                    <job>
                        <commTarget>null</commTarget>
                        <verbToUse>null</verbToUse>
                        <bill>null</bill>
                        <lord>null</lord>
                        <def>Stumble</def>
                        <loadID>307794</loadID>
                        <targetQueueA IsNull="True" />
                        <targetQueueB IsNull="True" />
                        <countQueue IsNull="True" />
                        <startTick>2610462</startTick>
                        <placedThings IsNull="True" />
                    </job>
                </li>
                <li>
                    <job>
                        <commTarget>null</commTarget>
                        <verbToUse>null</verbToUse>
                        <bill>null</bill>
                        <lord>null</lord>
                        <def>Stumble</def>
                        <loadID>171542</loadID>
                        <targetQueueA IsNull="True" />
                        <targetQueueB IsNull="True" />
                        <countQueue IsNull="True" />
                        <startTick>939257</startTick>
                        <placedThings IsNull="True" />
                    </job>
                </li>
            </jobs>
        </jobQueue>
    </jobs>
    <stances>
        <staggerUntilTick>2610734</staggerUntilTick>
        <stunner />
        <curStance Class="Stance_Mobile" />
    </stances>
    <verbTracker>
        <verbs>
            <li Class="Verb_MeleeAttack">
                <loadID>Zombie120686_0</loadID>
                <currentTarget>Thing_Human187374</currentTarget>
            </li>
            <li Class="Verb_MeleeAttack">
                <loadID>Zombie120686_1</loadID>
            </li>
            <li Class="Verb_MeleeAttack">
                <loadID>Zombie120686_2</loadID>
            </li>
        </verbs>
    </verbTracker>
    <natives>
        <beatFireVerb>
            <loadID>Zombie120686_BeatFire</loadID>
        </beatFireVerb>
        <igniteVerb>
            <loadID>Zombie120686_Ignite</loadID>
        </igniteVerb>
    </natives>
    <meleeVerbs>
        <curMeleeVerb>Verb_Zombie120686_0</curMeleeVerb>
        <curMeleeVerbUpdateTick>2610945</curMeleeVerbUpdateTick>
    </meleeVerbs>
    <rotationTracker />
    <pather>
        <moving>False</moving>
        <nextCell>(285, 0, 158)</nextCell>
        <nextCellCostInitial>79</nextCellCostInitial>
        <peMode>Touch</peMode>
        <cellsUntilClamor>2</cellsUntilClamor>
        <lastMovedTick>2615815</lastMovedTick>
    </pather>
    <carryTracker>
        <innerContainer>
            <maxStacks>1</maxStacks>
            <innerList />
        </innerContainer>
    </carryTracker>
    <apparel>
        <wornApparel>
            <innerList>
                <li>
                    <def>Apparel_Pants</def>
                    <id>Apparel_Pants120687</id>
                    <health>100</health>
                    <stackCount>1</stackCount>
                    <stuff>Visceroid_TBI_Leather</stuff>
                    <color>RGBA(0.173, 0.259, 0.165, 1.000)</color>
                    <colorActive>True</colorActive>
                    <quality>Shoddy</quality>
                </li>
            </innerList>
        </wornApparel>
        <lastApparelWearoutTick>-1</lastApparelWearoutTick>
    </apparel>
    <story>
        <childhood>AspergersRebel96</childhood>
        <adulthood>Jailbird93</adulthood>
        <bodyType>Male</bodyType>
        <crownType>Average</crownType>
        <headGraphicPath>Things/Pawn/Humanlike/Heads/Male/Male_Average_Normal</headGraphicPath>
        <hairDef>VARBundle</hairDef>
        <hairColor>RGBA(0.250, 0.200, 0.150, 1.000)</hairColor>
        <melanin>0.58</melanin>
        <traits>
            <allTraits />
        </traits>
    </story>
    <equipment>
        <equipment>
            <innerList />
        </equipment>
    </equipment>
    <drafter IsNull="True" />
    <ageTracker>
        <ageBiologicalTicks>3498906685</ageBiologicalTicks>
        <birthAbsTicks>-3497050210</birthAbsTicks>
    </ageTracker>
    <healthTracker>
        <hediffSet>
            <hediffs>
                <li Class="Hediff_Injury">
                    <loadID>10752</loadID>
                    <def>TM_TormentHD</def>
                    <ageTicks>2601</ageTicks>
                    <source>Projectile_FogOfTorment</source>
                    <partIndex>0</partIndex>
                    <severity>0.04000006</severity>
                    <visible>True</visible>
                </li>
            </hediffs>
        </hediffSet>
        <surgeryBills>
            <bills />
        </surgeryBills>
        <immunity>
            <imList />
        </immunity>
    </healthTracker>
    <records>
        <records>
            <vals>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
                <li>0</li>
            </vals>
        </records>
        <storyRelevance>40</storyRelevance>
        <battleActive>null</battleActive>
    </records>
    <inventory>
        <itemsNotForSale />
        <innerContainer>
            <innerList />
        </innerContainer>
    </inventory>
    <filth>
        <carriedFilth>
            <li>
                <def>FilthDirt</def>
                <id>FilthDirt120711</id>
            </li>
        </carriedFilth>
    </filth>
    <needs>
        <needs />
    </needs>
    <guest>
        <hostFaction>null</hostFaction>
        <getsFood>True</getsFood>
        <interactionMode>NoInteraction</interactionMode>
        <spotToWaitInsteadOfEscaping>(-1000, -1000, -1000)</spotToWaitInsteadOfEscaping>
        <lastPrisonBreakTicks>-1</lastPrisonBreakTicks>
    </guest>
    <guilt />
    <social>
        <directRelations />
        <relativeInvolvedInRescueQuest>null</relativeInvolvedInRescueQuest>
    </social>
    <ownership>
        <ownedBed>null</ownedBed>
        <assignedGrave>null</assignedGrave>
    </ownership>
    <interactions />
    <skills>
        <skills>
            <li>
                <def>Shooting</def>
            </li>
            <li>
                <def>Melee</def>
                <xpSinceLastLevel>262.5</xpSinceLastLevel>
                <xpSinceMidnight>262.5</xpSinceMidnight>
            </li>
            <li>
                <def>Social</def>
            </li>
            <li>
                <def>Animals</def>
            </li>
            <li>
                <def>Medicine</def>
            </li>
            <li>
                <def>Cooking</def>
            </li>
            <li>
                <def>Construction</def>
            </li>
            <li>
                <def>Growing</def>
            </li>
            <li>
                <def>Mining</def>
            </li>
            <li>
                <def>Artistic</def>
            </li>
            <li>
                <def>Crafting</def>
            </li>
            <li>
                <def>Intellectual</def>
            </li>
        </skills>
        <lastXpSinceMidnightResetTimestamp>-1</lastXpSinceMidnightResetTimestamp>
    </skills>
    <workSettings>
        <priorities IsNull="True" />
    </workSettings>
    <trader IsNull="True" />
    <outfits IsNull="True" />
    <drugs IsNull="True" />
    <timetable IsNull="True" />
    <playerSettings IsNull="True" />
    <training IsNull="True" />
    <zstate>Tracking</zstate>
    <wanderDestination>(200, 0, 34)</wanderDestination>
    <rubbleTicks>4</rubbleTicks>
    <rubbleCounter>50</rubbleCounter>
    <rubbles />
    <bombTickingInterval>-1</bombTickingInterval>
</thing>
pardeike commented 6 years ago

Putting a try catch in that expensive function will slow things down too much. The other mod should simply be coded more defensive to prevent the NRE. Sorry.

exuvo commented 6 years ago

Okay then ill try with the other mod creator and see if they can avoid creating the effect it the target is a zombie. What is the best way to detect if the target is a zombie?

pardeike commented 6 years ago

If the class of the zombie is called Zombieland.Zombie. But personally, I would rather add ordinary null checks to cover any other slimmed down creatures of other mods too. So if you deal with property X.Y you always check if X is non-Null or else you bail out. No need to check if X is from a zombie.

exuvo commented 6 years ago

The failing job is a default rimworld vomit job and after looking at the decompiled source at https://github.com/josh-m/RW-Decompile/blob/master/RimWorld/JobDriver_Vomit.cs it think the error is because the vomit job tries to make pawns more hungry each vomit and zombies don't have a hunger need?

So if the target pawn does not have a hunger need, don't add a vomit job. Does my understanding seem correct?

pardeike commented 6 years ago

Sounds reasonable. Indeed, zombies have no hunger need and if the vomit job references that you better not assign it. Zombies don’t vomit anyway! :-)