/// <summary> /// Register all native functions with managed code /// </summary> /// <param name="registerFunctionsAddr">The address of the RegisterFunctions method defined in native code</param> public static void RegisterFunctions(IntPtr registerFunctionsAddr) { if (!EntryPoint.Preloaded) { string namespaceName = typeof(NativeFunctions).Namespace; foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) { if (type.IsClass && type.IsAbstract && type.IsSealed && type.Name.StartsWith("Native_") && type.Namespace == namespaceName) { // Native_FName -> Export_FName_XXXXX string nativeFunctionPrefix = "Export" + type.Name.Replace("Native", string.Empty) + "_"; foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) { if (field.IsStatic && field.FieldType.IsSubclassOf(typeof(Delegate))) { functions.Add(nativeFunctionPrefix + field.Name, field); } } } } // Call the native RegisterFunctions method with our managed RegisterFunction as the callback Del_RegisterFunctions registerFunctions = (Del_RegisterFunctions)Marshal.GetDelegateForFunctionPointer( registerFunctionsAddr, typeof(Del_RegisterFunctions)); registerFunctions(Marshal.GetFunctionPointerForDelegate(registerFunction)); // Highest (these may be called from a thread other than the game thread, don't access UObject methods) FBuild.OnNativeFunctionsRegistered(); FGlobals.OnNativeFunctionsRegistered(); Classes.OnNativeFunctionsRegistered(); BoolMarshaler.OnNativeFunctionsRegistered(); FStringMarshaler.OnNativeFunctionsRegistered(); InputCore.FKey.OnNativeFunctionsRegistered(); FFrame.OnNativeFunctionsRegistered(); // Validate native struct sizes match the managed struct sizes before running any handlers // NOTE: This MUST come after FStringMarshaler.OnNativeFunctionsRegistered as this requires TCHAR size StructValidator.ValidateStructs(); } if (!EntryPoint.Preloading) { Debug.Assert(FThreading.IsInGameThread(), "Load/hotreload should be on the game thread"); // Highest = Used for initializing very important data required by other functions. // VeryHigh = Used for initializing data before UObject classes are loaded. // High = This is when UObject classes are loaded. Don't access UObjects at this or any higher priority. // Medium = UObject classes are loaded and available to use at this priority. // If GEngine is null we need to bind OnPostEngineInit to load anything which requires GEngine if (FGlobals.GEngine == IntPtr.Zero) { FCoreDelegates.OnPostEngineInit.Bind(OnPostEngineInit); } // Highest GCHelper.OnNativeFunctionsRegistered(); FMessage.OnNativeFunctionsRegistered(); Engine.FTimerManagerCache.OnNativeFunctionsRegistered(); WorldTimeHelper.OnNativeFunctionsRegistered(); if (FGlobals.GEngine != IntPtr.Zero) { // Needs GEngine for binding delegates StaticVarManager.OnNativeFunctionsRegistered(); Coroutine.OnNativeFunctionsRegistered(); } // VeryHigh NativeReflection.OnNativeFunctionsRegistered();// Requires Classes to be initialized // High OnNativeFunctionsRegistered(); // Low EngineLoop.OnNativeFunctionsRegistered(); // Lowest CodeGenerator.OnNativeFunctionsRegistered(); #if WITH_USHARP_TESTS Tests.Tests.OnNativeFunctionsRegistered(); #endif } }
/// <summary> /// Callback where GEngine is initialized and is safe to use /// </summary> private static void OnPostEngineInit() { StaticVarManager.OnNativeFunctionsRegistered(); Coroutine.OnNativeFunctionsRegistered(); }