[StaticConstructorOnStartup] does not appear to work correctly

Started by Fluffy (l2032), April 07, 2016, 04:08:25 PM

Previous topic - Next topic

Fluffy (l2032)

Steps to reproduce:

       
  • Create a mod with an assembly, add classes that contain static assets (textures, etc)
  • Add the [StaticConstructorOnStartup] tag
  • Try to use ContentFinder<>.Get() in the constructor
I've used the following snippet of code to obtain some test results; the rest of the class/mod can be found here: https://github.com/Karel-Kroeze/RW_Blueprints/

using RimWorld;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;


namespace Blueprints
{
    [StaticConstructorOnStartup]
    public class Designator_CreateBlueprint : Designator
    {
        #region Public Constructors


        public Designator_CreateBlueprint()
        {
            Log.Error( "INSTANTIATING NOW:\n" + System.Environment.StackTrace );
            Log.Message( "Types with static constructors:" );
            foreach ( var type in GenTypes.AllTypesWithAttribute<StaticConstructorOnStartup>() )
            {
                Log.Message( "\t" + type.FullName );
            }


            LongEventHandler.ExecuteWhenFinished( delegate
            {
                Resources.Icon_AddBlueprint = ContentFinder<Texture2D>.Get( "Icons/AddBlueprint", true );
                Resources.Icon_Blueprint = ContentFinder<Texture2D>.Get( "Icons/Blueprint", true );
                Resources.Icon_Edit = ContentFinder<Texture2D>.Get( "Icons/Edit", true );


                icon = Resources.Icon_AddBlueprint;
            } );


            defaultLabel = "Fluffy.Blueprints.Create".Translate();
            defaultDesc = "Fluffy.Blueprints.CreateHelp".Translate();
            useMouseIcon = true;
        }


        #endregion Public Constructors


// more code.
}
}

Note that the LongEventHandler.ExecuteWhenFinished call does work, and circumvents the issue.


The attached output log highlights the issue. The game is clearly aware that this type (and a few others) should have a static constructor, yet the stack trace shows that it is still instantiated from a secondary thread.

[attachment deleted by admin - too old]

ison

Note that it must be a static constructor and in your case its a normal constructor. Could you try to load your resources in a static constructor and tell if it helps?

Fluffy (l2032)

doh - if that's it that would be a tad silly of me, will try right now.

Fluffy (l2032)

Nope, no luck.

new code;
using RimWorld;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;

namespace Blueprints
{
    [StaticConstructorOnStartup]
    public class Designator_CreateBlueprint : Designator
    {
        static Designator_CreateBlueprint()
        {
            Log.Error( "INSTANTIATING NOW:\n" + System.Environment.StackTrace );
            string text = "Types with static constructors:";
            foreach ( var type in GenTypes.AllTypesWithAttribute<StaticConstructorOnStartup>() )
            {
                text += ( "\n\t" + type.FullName );
            }
            Log.Message( text );

            Resources.Icon_AddBlueprint = ContentFinder<Texture2D>.Get( "Icons/AddBlueprint", true );
            Resources.Icon_Blueprint = ContentFinder<Texture2D>.Get( "Icons/Blueprint", true );
            Resources.Icon_Edit = ContentFinder<Texture2D>.Get( "Icons/Edit", true );
        }

        #region Public Constructors

        public Designator_CreateBlueprint()
        {
            icon = Resources.Icon_AddBlueprint;
            defaultLabel = "Fluffy.Blueprints.Create".Translate();
            defaultDesc = "Fluffy.Blueprints.CreateHelp".Translate();
            useMouseIcon = true;
        }


new output attached, still called from asynch thread, now with the resulting errors.

On a possibly related note, whenever I enable any of my mods, even those where textures are (magically?) working correctly - the language flag is rendered as the bad texture box.

[attachment deleted by admin - too old]

ison

Ah, it's because Designators are created during async loading, so using a StaticConstructorOnStartup doesn't do anything because the static constructor is called during async loading anyway. I'll make it so designators are created after the loading is done. In the meantime, you'll have to use LongEventHandler.ExecuteWhenFinished.

Fluffy (l2032)

Good to hear you're on it ;)

On a related note, is it possible to check what thread ContentFinder is called from? It would be great to have a more descriptive warning pop up.  E.g. in this case, and on windows, the error suggest there is something wrong with the path. I my manager mod, loading textures from the second thread actually seems to (weirdly) work on windows - but it breaks for users running Linux, see the reply by Vee here;
https://ludeon.com/forums/index.php?topic=16888.msg201942#msg201942




ison

Quote from: Fluffy (l2032) on April 08, 2016, 08:41:24 AM
On a related note, is it possible to check what thread ContentFinder is called from? It would be great to have a more descriptive warning pop up.  E.g. in this case, and on windows, the error suggest there is something wrong with the path. I my manager mod, loading textures from the second thread actually seems to (weirdly) work on windows - but it breaks for users running Linux, see the reply by Vee here;
https://ludeon.com/forums/index.php?topic=16888.msg201942#msg201942

Good idea, I'll add a check with more descriptive error when someone tries to load an asset from a different thread so the behavior on different platforms will be consistent.