Unexplainable Gamecrash by Mod on Linux only

Started by Haplo, April 20, 2016, 05:59:32 PM

Previous topic - Next topic

Haplo

Sorry to post it here, but I have a problem with a graphic where I don't know how to resolve it or even what exactly the problem is.

But first things first:
I have a mod where I have modified the pawn class for my usage with robots. I will load the graphics, resolve the renderer and replace the head graphic with an alpha graphic and the body with the defined graphic.

This works on my computer without problems (Win10 x64) but on the machine of an user (I think he is an Linux user) it crashes when I call the resolve on the renderer BEFORE I define the new head graphic. I just load it to the variable when it crashes..


From AIRobots.cs:

        public override void SpawnSetup()
        {
            BackstoryHelperRobot.AddNewBackstoriesToDatabase();

            base.SpawnSetup();

            // Get the data from the extended def
            ReadXmlData();

            // Load base graphics
            Color colorNormal = Color.white;
            nakedHeadGraphic = GraphicDatabase.Get<Graphic_Multi>(normalHeadGraphicPathMulti, ShaderDatabase.Cutout, Vector2.one, colorNormal);
            nakedBodyGraphic = GraphicDatabase.Get<Graphic_Multi>(kindDef.lifeStages[0].bodyGraphicData.texPath, ShaderDatabase.Cutout, Vector2.one, colorNormal);

            // Load hue graphics
            Color colorHue = Color.Lerp(Color.white, Color.red, 0.30f);
            nakedHeadGraphicHue = GraphicDatabase.Get<Graphic_Multi>(normalHeadGraphicPathMulti, ShaderDatabase.Cutout, Vector2.one, colorHue);
            nakedBodyGraphicHue = GraphicDatabase.Get<Graphic_Multi>(kindDef.lifeStages[0].bodyGraphicData.texPath, ShaderDatabase.Cutout, Vector2.one, colorHue);

// HERE IT CRASHES ACCORDING TO THE LOG:
            Drawer.renderer.graphics.ResolveAllGraphics(); //Causes errors?

// HERE THE NEW GRAPHIC WILL BE SET...
            UpdateGraphics();

...


Do you have by chance an idea what the problem could be?

I really have my problems finding anything because here on Windows 10 it all works without any errors..

Thank you in advance for your help.
Greetings
Haplo


PS:
Here you'll find additional infos like the Player.log, the full source code and the mod files.
http://www.mediafire.com/download/c09vpg56tmxexp0/RimWorld_Robots_CrashInfo.zip

Here is the forum-thread concerning that problem:
https://ludeon.com/forums/index.php?topic=3612.msg209697#msg209697

This is a part of the Player.log of the user

Stacktrace:

  at (wrapper managed-to-native) UnityEngine.Resources.LoadAll (string,System.Type) <0x00068>
  at (wrapper managed-to-native) UnityEngine.Resources.LoadAll (string,System.Type) <0x00068>
  at UnityEngine.Resources.LoadAll<object> (string) <0x00032>
  at Verse.GraphicDatabaseUtility/<GraphicNamesInFolder>c__Iterator18B.MoveNext () <0x000a1>
  at Verse.GraphicDatabaseHeadRecords.BuildDatabaseIfNecessary () <0x00145>
  at Verse.GraphicDatabaseHeadRecords.GetHeadNamed (string,UnityEngine.Color) <0x0002b>
  at Verse.PawnGraphicSet.ResolveAllGraphics () <0x001c6>
  at AIRobot.AIRobot.SpawnSetup () <0x003f6>
  at Verse.GenSpawn.Spawn (Verse.Thing,Verse.IntVec3,Verse.Rot4) <0x00232>
  at Verse.MapIniter_LoadFromFile.InitMapFromFile (string) <0x00819>
  at Verse.RootMap.<Init>m__513 () <0x0001d>
  at Verse.LongEventHandler.RunEventFromAnotherThread (System.Action) <0x0002b>
  at Verse.LongEventHandler.<UpdateCurrentAsynchronousEvent>m__50B () <0x00021>
  at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) <0x0005e>

Native stacktrace:

/home/redacted/Downloads/Rimworld/Alpha13/RimWorld1135Linux/RimWorld1135Linux_Data/Mono/x86_64/libmono.so(+0x926cb) [0x7f04bf3b76cb]
/home/redacted/Downloads/Rimworld/Alpha13/RimWorld1135Linux/RimWorld1135Linux_Data/Mono/x86_64/libmono.so(+0x36226) [0x7f04bf35b226]
/usr/lib/libpthread.so.0(+0x10e80) [0x7f04c2421e80]
/usr/lib/libGL.so.1(+0x3b1a9) [0x7f04c1d4d1a9]

Debug info from gdb:


=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
...

Fluffy (l2032)


  at Verse.LongEventHandler.RunEventFromAnotherThread (System.Action) <0x0002b>

+


  at UnityEngine.Resources.LoadAll<object> (string) <0x00032>

Mods are loaded in a separate thread. Unity doesn't like multithreading. On win, it sometimes works, sometimes errors with a vague "can't find asset". On linux, it flat out crashes the game.

Either resolve assets when you need them (e.g. through a property), use StaticConstructorOnStartup attribute on the class, or use LongEventHandler.ExecuteWhenFinished. I now tend to put an InitIfNeeded() function on my static resource classes, that loads all assets the first time any asset is used.

Haplo

The StaticConstructorOnStartup is set for the class.
with the second one: Can I just split it up and call the assets with the second function call?

something like this?

public override void SpawnSetup()
        {
            BackstoryHelperRobot.AddNewBackstoriesToDatabase();

            base.SpawnSetup();

            LongEventHandler.ExecuteWhenFinished(SpawnSetupPart2);

        }

        private void SpawnSetupPart2()
        {

            // Get the data from the extended def
            ReadXmlData();

Fluffy (l2032)

Basically you need to take loading of assets (or better yet, interacting with unity in any way) out of the asynchronous mod loading phase.

In theory, there's three ways of doing that;

  • Use a public static constructor, and add the StaticConstructorOnStartupTag. This is the preferred method, which has the game call the static constructor from the main thread. This works for some classes/deftypes, but annoyingly not for all. I personally have given up on using it for that reason.
  • Use LongEventHandler.ExecuteWhenFinished from the instance cosntructor. This is only useful if called from the asynchronous mod loading phase (so in non-static default constructors, and whatever gets called from them). It queues up an action to be performed on the main thread when mod loading is complete.
  • Since the mod loading thread is only used during startup, you can also load assets on demand in the gameplay phase. A simple construct to make sure assets are loaded (e.g. if (asset=null) load(asset);) will work just fine.

ison

As Fluffy said, it's not possible to load assets like textures in a separate thread. In the next release we'll give a proper error instead of crashing the game when you try to load an asset from a separate thread.

You can use StaticConstructorOnStartup but then you have to use only static resources:


[StaticConstructorOnStartup]
public class MyThing : Thing
{
public static readonly Material MyThingMat = MaterialPool.MatFrom("Something");
}


or load all resources in the static constructor:


[StaticConstructorOnStartup]
public class MyThing : Thing
{
public static Material myThingMat;

static MyThing()
{
myThingMat = MaterialPool.MatFrom("Something");
}
}


The way it works is that the class' static constructor is called in the main thread before everything else.

Quote from: Haplo on April 20, 2016, 06:21:29 PM
something like this?

public override void SpawnSetup()
        {
            BackstoryHelperRobot.AddNewBackstoriesToDatabase();

            base.SpawnSetup();

            LongEventHandler.ExecuteWhenFinished(SpawnSetupPart2);

        }

        private void SpawnSetupPart2()
        {

            // Get the data from the extended def
            ReadXmlData();


yeah, this should work

Haplo

Thank you for the info.
I think I'll need to do it like Fluffy said with the update-when-needed functionality or with the LongEventHandler as I have most of my texture assigned in SpawnSetup and not in a static constructor (even when the variable is static).
As the LongEventHandler variant seems to work perfectly and seems to be the easiest implementation for my existing parts, I think I'll use that mostly :)

Thank you Fluffy and ison for helping and explaining it to me :)