Desperate for an answer: Error at WindManagerTick and CurrentSkyTarget

Started by SickBoyWi, June 26, 2020, 02:03:01 PM

Previous topic - Next topic

SickBoyWi

I'm working on a mod. When I build one of my buildings, this happens:

https://gist.github.com/HugsLibRecordKeeper/85a443e1a09cf7370ef105a985d42b9a

Here's the summary:
Root level exception in Update(): System.NullReferenceException: Object reference not set to an instance of an object
  at Verse.SkyManager.CurrentSkyTarget () [0x00164] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.SkyManager.SkyManagerUpdate () [0x00000] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Map.MapUpdate () [0x00005] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Game.UpdatePlay () [0x00031] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Root_Play.Update () [0x00026] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
Verse.Log:Error(String, Boolean)
Verse.Root_Play:Update()

Root level exception in Update(): System.NullReferenceException: Object reference not set to an instance of an object
  at Verse.WindManager.WindManagerTick () [0x000b0] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Map.MapPreTick () [0x0003b] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.TickManager.DoSingleTick () [0x00011] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.TickManager.TickManagerUpdate () [0x0006a] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Game.UpdatePlay () [0x00000] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
  at Verse.Root_Play.Update () [0x00026] in <4489f1367c1c4d76b2ae1272c8ed691d>:0
Verse.Log:Error(String, Boolean)
Verse.Root_Play:Update()


It fogs the map, and requires a reboot of the entire game. I have a second building just like the one that causes this issue, that works fine. I've compared the config line by line, and all looks to be the same.

Is this an issue with a typical cause? Any input is very much appreciated.

K

I've never seen that error before. Could you post whatever source material you have (C# and XML)? It would help a lot in determining the cause of your issue.

SickBoyWi

Sure thing.

Here's the config for it:
  <ThingDef ParentName="FurnitureWithQualityBase">
    <defName>RH_TET_Dwarfs_KingsThrone</defName>
    <label>king's throne</label>
    <drawGUIOverlay>true</drawGUIOverlay>
    <drawGUIOverlayQuality>false</drawGUIOverlayQuality>
    <description>A large, extremely ornate chair on a raised dais, designed for a dwarven king to rest upon.\n\nAssigning a dwarf to a dwarven throne will make the dwarf a high blood. High blood dwarfs have their own special needs, and requirements.\n\nOnce a dwarf is made a high blood, it cannot be undone unless they die. Their death will greatly upset the clans.\n\nA king's throne is the ultimate in dwarven furniture craftsmanship. They're extremely beautiful, and only enhance the respect given to the person that sits upon them; from both dawi and non-dawi alike.</description>
    <defaultPlacingRot>South</defaultPlacingRot>
    <altitudeLayer>BuildingOnTop</altitudeLayer>
    <leaveResourcesWhenKilled>true</leaveResourcesWhenKilled>
    <tickerType>Normal</tickerType>
    <category>Building</category>
    <!--<thingSetMakerTags><li>RewardStandardQualitySuper</li></thingSetMakerTags>-->
    <hasInteractionCell>true</hasInteractionCell>
    <interactionCellOffset>(0,0,1)</interactionCellOffset>
    <statBases>
      <MaxHitPoints>250</MaxHitPoints>
      <WorkToBuild>15000</WorkToBuild>
      <Mass>40</Mass>
      <Flammability>0</Flammability>
      <Beauty>25</Beauty>
      <Comfort>1.5</Comfort>
    </statBases>
    <thingClass>TheEndTimes_Dwarfs.Building_DwarfThrone</thingClass>
    <socialPropernessMatters>true</socialPropernessMatters>
    <constructionSkillPrerequisite>12</constructionSkillPrerequisite>
    <stuffCategories>
      <li>Stony</li>
    </stuffCategories>
    <costStuffCount>200</costStuffCount>
    <costList>
      <RH_TET_Dwarf_Gromril>25</RH_TET_Dwarf_Gromril>
      <Gold>50</Gold>
      <Silver>100</Silver>
    </costList>
    <pathCost>30</pathCost>
    <rotatable>true</rotatable>
    <fillPercent>0.6</fillPercent>
    <building>
      <isSittable>true</isSittable>
    </building>
<size>(3,2)</size>
    <uiIconOffset>(0, 0)</uiIconOffset>
    <uiIconScale>0.8</uiIconScale>
    <comps>
      <li Class="CompProperties_AssignableToPawn">
        <drawAssignmentOverlay>true</drawAssignmentOverlay>
        <compClass>TheEndTimes_Dwarfs.CompAssignableToPawn_Throne</compClass>
      </li>
    </comps>
    <researchPrerequisites>
      <li>RH_TET_Dwarfs_HighBlood_King</li>
    </researchPrerequisites>
    <graphicData>
      <drawSize>(3.0,3.0)</drawSize>
      <texPath>Things/Building/Furniture/RH_TET_Dwarfs_ThroneKing</texPath>
      <graphicClass>Graphic_Multi</graphicClass>
      <shadowData>
        <volume>(0.5,0.35,0.4)</volume>
      </shadowData>
      <damageData>
        <rect>(0.2,0,2, 0.6, 0.6)</rect>
      </damageData>
  <shaderType>CutoutComplex</shaderType>
    </graphicData>
    <designationCategory>RH_TET_Dwarfs_Buildings</designationCategory>
  </ThingDef>


The code is attached. The code is shared by a thane's throne and a king's throne. The thane's throne works fine, no issues. The king's throne causes the aforementioned errors.

K

From the two errors you posted, it seems that you violated some kind of assumption the game makes about data availability. There's a couple of things both those methods access (assuming they're both failing due to the same issue) that are suspect, most notably, they both access the comps of various Things on the map. Your building has one comp, perhaps that comp is causing issues? If you could post the code for that comp (TheEndTime_Dwarfs.CompAssignableToPawn_Throne), it would help greatly.

SickBoyWi

Sure thing.

Let me reiterate, the issue is for the King's Throne only, so that should narrow down the parts to look at dramatically. I'll look over it too, and see if I can see anything that might be causing it.

One note, is that if a Thane's Throne is already built, then building the King's Throne works just fine (the issues don't occur).

Code is attached.

SickBoyWi

The issue seems to happen on a delayed tick. Since when I build the throne, it takes an hour or so (in game time) for the error to occur. That comp doesn't have any code that should run, since it's all based on the player trying to assign a pawn to it. Unless maybe CompInspectStringExtra gets called. In the time before the error happens I can click on the throne, and there's no issues, so CompInspectStringExtra should be fine.

Unless those functions mentioned in the error run on a delayed/game-hourly tick. Then I guess it makes sense that they could be accessing something that is causing it. This issue shouldn't be such a gotcha, since it's related to the one throne only. The two throne defs are identical, other than the names, descriptions, stuff to make them, and beauty.

SickBoyWi

I'm still searching for a fix for this. Been over and over the code. Any ideas at all to set me in a direction would be helpful.

RawCode

logic is simple

NPE happens when you try to invoke method on null object

final method is CurrentSkyTarget  and you obviously should start your "hopeless" search with that method

private SkyTarget CurrentSkyTarget()
{
SkyTarget b = this.map.weatherManager.curWeather.Worker.CurSkyTarget(this.map);
SkyTarget skyTarget = SkyTarget.Lerp(this.map.weatherManager.lastWeather.Worker.CurSkyTarget(this.map), b, this.map.weatherManager.TransitionLerpFactor);
this.map.gameConditionManager.GetAllGameConditionsAffectingMap(this.map, this.tempAllGameConditionsAffectingMap);
for (int i = 0; i < this.tempAllGameConditionsAffectingMap.Count; i++)
{
SkyTarget? skyTarget2 = this.tempAllGameConditionsAffectingMap[i].SkyTarget(this.map);
if (skyTarget2 != null)
{
skyTarget = SkyTarget.LerpDarken(skyTarget, skyTarget2.Value, this.tempAllGameConditionsAffectingMap[i].SkyTargetLerpFactor(this.map));
}
}
this.tempAllGameConditionsAffectingMap.Clear();
List<WeatherEvent> liveEventsListForReading = this.map.weatherManager.eventHandler.LiveEventsListForReading;
for (int j = 0; j < liveEventsListForReading.Count; j++)
{
if (liveEventsListForReading[j].CurrentlyAffectsSky)
{
skyTarget = SkyTarget.Lerp(skyTarget, liveEventsListForReading[j].SkyTarget, liveEventsListForReading[j].SkyTargetLerpFactor);
}
}
List<Thing> list = this.map.listerThings.ThingsInGroup(ThingRequestGroup.AffectsSky);
for (int k = 0; k < list.Count; k++)
{
CompAffectsSky compAffectsSky = list[k].TryGetComp<CompAffectsSky>();
if (compAffectsSky.LerpFactor > 0f)
{
if (compAffectsSky.Props.lerpDarken)
{
skyTarget = SkyTarget.LerpDarken(skyTarget, compAffectsSky.SkyTarget, compAffectsSky.LerpFactor);
}
else
{
skyTarget = SkyTarget.Lerp(skyTarget, compAffectsSky.SkyTarget, compAffectsSky.LerpFactor);
}
}
}
return skyTarget;
}


plan A
debugger
plan B
code injection of trace output

this required to determinate what exact object is null at moment of execution

trace require literally 5 minutes to determinate issue and do not need any 3rd party software

SickBoyWi

I took your advice RawCode, and am still rather lost on this issue.

I added code to output what's happening in the methods that die.

In CurrentSkyTarget, the call to
         List<Thing> list = this.map.listerThings.ThingsInGroup(ThingRequestGroup.AffectsSky);

Returns the building in question.

Same goes for the line in in WindManagerTick():
      List<Thing> thingList = this.map.listerThings.ThingsInGroup(ThingRequestGroup.WindSource);

That call returns the building in question too.

Lastly, in Alert_ThroneroomInvalidConfiguration's GetReport method, the call to
      maps[index].listerThings.ThingsInGroup(ThingRequestGroup.Throne)

returns the building too.

The first two are pretty straight forward, those calls should return a thing only if it has the specific appropriate comp.

When I put code in to ThingDef's HasComp function, which is what those other calls use to determine whether a thing has a comp, none of those comps exist. The HasComp function is super simple, and I loop through and print out all the comps on the thing, there's only one (and it isn't one of the ones in question).

I tried to go a step further, and put some code into ListerThings's ThingsMatching function, to see which code path was being taken, and it's taking the path that is doing the checks I mentioned before.

I'm at a total loss here still.

RawCode

when you found source of issue but not sure, is this actually source of issue - remove problematic code section.

remove entire
List<Thing> list = this.map.listerThings.ThingsInGroup(ThingRequestGroup.AffectsSky);
{}
section from both methods and check, will game throw NPE or not.

if it continue to trow npes your guess is wrong and problem is not here.

SickBoyWi

I finally fixed this issue, and I'm posting the issue here for any one else that encounters it.

RawCode is on the right track here.

I was doing this:

List<Thing> thrones = maps[index].listerThings.ThingsOfDef(RH_TET_DwarfDefOf.RH_TET_Dwarfs_Throne);
thrones.AddRange(maps[index].listerThings.ThingsOfDef(RH_TET_DwarfDefOf.RH_TET_Dwarfs_KingsThrone));


The correct thing to do is this:
               
List<Thing> thrones = new List<Thing>();
thrones.AddRange(maps[index].listerThings.ThingsOfDef(RH_TET_DwarfDefOf.RH_TET_Dwarfs_Throne));
thrones.AddRange(maps[index].listerThings.ThingsOfDef(RH_TET_DwarfDefOf.RH_TET_Dwarfs_KingsThrone));


If that first call to the listerThings.ThingsOfdef finds nothing, it returns null. That causes all kinds of chaos, and the next call doesn't throw a NPE, it throws the weird error I mentioned above. This was very tough to track, and find. Almost impossible I'd say, as the actual error gets squashed.

I encountered this same error while working on a different mod, and was actually able to pin it down. With that info, I fixed my original error from way back when.

Hopefully this can be of help to someone at some point!