/// <summary> /// Finds the first native (non-managed) class for this managed class. This also caches the native parent /// class constructor for calling the parent constructor and setting it as the fallback constructor on hotreload. /// </summary> public void ResolveNativeParentClass() { NativeParentClassConstructor = IntPtr.Zero; if (Address == IntPtr.Zero) { return; } // We could possibly update this code to do a deep search for the first non-C# class (e.g. C# : X : C# : X : UObject). // We aren't currently calling the parent constructor in a way which would allow this. If we supported it as-is the C# // constructors would get called multiple times when calling the parent constructor. IntPtr parentClass = Native_UClass.GetSuperClass(Address); while (parentClass != IntPtr.Zero) { if (!Native_UObjectBaseUtility.IsA(parentClass, Runtime.Classes.USharpClass)) { NativeParentClass = parentClass; NativeParentClassConstructor = Native_UClass.Get_ClassConstructor(parentClass); break; } parentClass = Native_UClass.GetSuperClass(parentClass); } Debug.Assert(NativeParentClass != IntPtr.Zero); }
/// <summary> /// Get the name of the CDO for the this class /// </summary> /// <returns>The name of the CDO</returns> public FName GetDefaultObjectName() { FName result; Native_UClass.GetDefaultObjectName(Address, out result); return(result); }
private void MoveToTransientPackage(IntPtr obj) { // Copy of UObjectBase.cpp UClassCompiledInDefer // Check if rooted? Native_UObjectBaseUtility.RemoveFromRoot(obj); Native_UObjectBaseUtility.ClearFlags(obj, EObjectFlags.Standalone | EObjectFlags.Public); IntPtr defaultObject = Native_UClass.GetDefaultObject(obj, false); if (defaultObject != IntPtr.Zero) { // Check if rooted? Native_UObjectBaseUtility.RemoveFromRoot(defaultObject); Native_UObjectBaseUtility.ClearFlags(defaultObject, EObjectFlags.Standalone | EObjectFlags.Public); } FName oldClassRename = NativeReflection.MakeUniqueObjectName(NativeReflection.GetTransientPackage(), Native_UObjectBase.GetClass(obj), new FName("USharpHotReload_" + Native_UObjectBase.GetFName(obj))); using (FStringUnsafe oldClassRenameUnsafe = new FStringUnsafe(oldClassRename.ToString())) { Native_UObject.Rename(obj, ref oldClassRenameUnsafe.Array, NativeReflection.GetTransientPackage(), ERenameFlags.None); } Native_UObjectBaseUtility.SetFlags(obj, EObjectFlags.Transient); Native_UObjectBaseUtility.AddToRoot(obj); }
/// <summary> /// Gets the name of the function when exposed to a scripting system (e.g. Python) /// </summary> public bool GetScriptName(string originalName, out string name) { string scriptFunctionName = originalName; bool hasScriptFunctionName = false; string scriptName = this.GetMetaData(MDFunc.ScriptName); if (!string.IsNullOrEmpty(scriptName)) { scriptFunctionName = scriptName; hasScriptFunctionName = true; } else { // Remove the K2_ prefix (do it in a loop just incase there are multiple K2_ prefixes) IntPtr ownerClass = Native_UField.GetOwnerClass(Address); if (ownerClass != IntPtr.Zero && Native_UClass.HasAnyClassFlags(ownerClass, EClassFlags.Native)) { while (scriptFunctionName.StartsWith("K2_")) { scriptFunctionName = scriptFunctionName.Substring(3); hasScriptFunctionName = true; } } } name = scriptFunctionName; return(hasScriptFunctionName); }
/// <summary> /// Add a native function to the internal native function table /// </summary> /// <param name="name">name of the function</param> /// <param name="pointer">pointer to the function</param> public void AddNativeFunction(string name, IntPtr pointer) { using (FStringUnsafe nameUnsafe = new FStringUnsafe(name)) { Native_UClass.AddNativeFunction(Address, ref nameUnsafe.Array, pointer); } }
private static unsafe IntPtr FindOriginalVTableOwner(IntPtr baseMostClass, IntPtr ownerClass, IntPtr functionAddress, int vtableIndex) { // Don't search lower than the target base if (ownerClass == baseMostClass) { return(ownerClass); } IntPtr originalOwner = ownerClass; while ((ownerClass = Native_UClass.GetSuperClass(ownerClass)) != IntPtr.Zero) { IntPtr obj = Native_UClass.GetDefaultObject(ownerClass, true); IntPtr *vtable = *(IntPtr **)obj; if (vtable[vtableIndex] == functionAddress) { originalOwner = ownerClass; } // Don't search lower than the target base if (ownerClass == baseMostClass) { return(ownerClass); } } return(originalOwner); }
public unsafe T GetInterface <T>() where T : class, IInterface { T result = this as T; if (result != null) { return(result); } if (injectedInterfaces == null) { // If the injected interfaces haven't been set up set them up now if (objRef == null) { return(null); } UClass unrealClass = GetClass(); if (unrealClass as USharpClass != null) { // This is a C# defined type. We know if it implements the target interface or not due to the // above "this as T". There isn't any need to inject interfaces into the UObject. return(null); } FScriptArray *interfacesPtr = (FScriptArray *)Native_UClass.Get_InterfacesRef(unrealClass.Address); if (interfacesPtr->ArrayNum != 0) { injectedInterfaces = new Dictionary <Type, IInterface>(); foreach (FImplementedInterface implementedInterface in unrealClass.Interfaces) { if (implementedInterface.InterfaceClassAddress != IntPtr.Zero) { Type type = UClass.GetTypeFromClassAddress(implementedInterface.InterfaceClassAddress); if (type != null) { IInterface instance = UnrealInterfacePool.New(type, objRef); if (instance != null) { injectedInterfaces[type] = instance; if (type == typeof(T)) { result = instance as T; } } } } } } } else { // Try and get the interface from the injected interfaces. IInterface instance; injectedInterfaces.TryGetValue(typeof(T), out instance); result = instance as T; } return(result); }
/// <summary> /// Finds the common base class that parents the array of classes passed in. /// </summary> /// <param name="inClasses">the array of classes to find the common base for</param> /// <returns>the common base class or NULL</returns> public static UClass FindCommonBase(UClass[] inClasses) { using (TArrayUnsafe <UClass> inClassesUnsafe = new TArrayUnsafe <UClass>()) { inClassesUnsafe.AddRange(inClasses); return(GCHelper.Find <UClass>(Native_UClass.FindCommonBaseMany(inClassesUnsafe.Address))); } }
/// <summary> /// This will return whether or not this class implements the passed in class / interface /// </summary> /// <param name="someInterface">the interface to check and see if this class implements it</param> /// <returns></returns> public bool ImplementsInterface(UClass someInterface) { if (someInterface != null && someInterface.HasAnyClassFlags(EClassFlags.Interface)) { return(Native_UClass.ImplementsInterface(Address, someInterface.Address)); } return(false); }
public string GetDescription() { using (FStringUnsafe resultUnsafe = new FStringUnsafe()) { Native_UClass.GetDescription(Address, ref resultUnsafe.Array); return(resultUnsafe.Value); } }
/// <summary> /// Gets all default instanced objects (often components). /// </summary> /// <param name="outDefaultSubobjects">An array to be filled with default subobjects.</param> public void GetDefaultObjectSubobjects(out UObject[] outDefaultSubobjects) { using (TArrayUnsafe <UObject> defaultSubobjectsUnsafe = new TArrayUnsafe <UObject>()) { Native_UClass.GetDefaultObjectSubobjects(Address, defaultSubobjectsUnsafe.Address); outDefaultSubobjects = defaultSubobjectsUnsafe.ToArray(); } }
/// <summary> /// Translates the hardcoded script config names (engine, editor, input and /// game) to their global pendants and otherwise uses config(myini) name to /// look for a game specific implementation and creates one based on the /// default if it doesn't exist yet. /// </summary> /// <returns>name of the class specific ini file</returns> public string GetConfigName() { using (FStringUnsafe resultUnsafe = new FStringUnsafe()) { Native_UClass.GetConfigName(Address, ref resultUnsafe.Array); return(resultUnsafe.Value); } }
/// <summary> /// Replace a native function in the internal native function table /// </summary> /// <param name="inName">name of the function</param> /// <param name="inPointer">pointer to the function</param> /// <param name="addToFunctionRemapTable">For C++ hot-reloading, UFunctions are patched in a deferred manner and this should be true /// For script hot-reloading, script integrations may have a many to 1 mapping of UFunction to native pointer /// because dispatch is shared, so the C++ remap table does not work in this case, and this should be false</param> /// <returns>true if the function was found and replaced, false if it was not</returns> public bool ReplaceNativeFunction(FName inName, IntPtr inPointer, bool addToFunctionRemapTable) { // WITH_HOT_RELOAD if (Native_UClass.ReplaceNativeFunction == null) { return(false); } return(Native_UClass.ReplaceNativeFunction(Address, ref inName, inPointer, addToFunctionRemapTable)); }
/// <summary> /// Returns true if this objects class implements the given IInterface derived type /// (call this on UObject instances; if you are working with a UClass call ImplementsInterface() instead).<para/> /// This is the equivalent of UKismetSystemLibrary::DoesImplementInterface(). /// This is also the same as obj.GetClass().ImplementsInterface(). /// </summary> /// <param name="type">The IInterface derived type</param> /// <returns>True if this objects class implements the given IInterface derived type</returns> public bool DoesImplementInterface(Type type) { UClass interfaceClass = UClass.GetClass(type); if (interfaceClass != null && interfaceClass.ClassFlags.HasFlag(EClassFlags.Interface)) { return(Native_UClass.ImplementsInterface(Native_UObjectBase.GetClass(Address), interfaceClass.Address)); } return(false); }
private IntPtr FindFirstNonUSharpClassParentClass(IntPtr unrealClass) { IntPtr sharpStaticClass = Native_USharpClass.StaticClass(); while (unrealClass != IntPtr.Zero && !Native_UObjectBaseUtility.IsA(unrealClass, sharpStaticClass)) { unrealClass = Native_UClass.GetSuperClass(unrealClass); } return(unrealClass); }
/// <summary> /// Returns true if this objects class implements the given IInterface derived type /// (call this on UObject instances; if you are working with a UClass call ImplementsInterface() instead).<para/> /// This is the equivalent of UKismetSystemLibrary::DoesImplementInterface(). /// This is also the same as obj.GetClass().ImplementsInterface(). /// </summary> /// <typeparam name="T">The IInterface derived type</typeparam> /// <returns>True if this objects class implements the given IInterface derived type</returns> public bool DoesImplementInterface <T>() where T : IInterface { IntPtr interfaceClass = UClass.GetInterfaceClassAddress <T>(); if (interfaceClass != IntPtr.Zero && Native_UClass.GetClassFlags(interfaceClass).HasFlag(EClassFlags.Interface)) { return(Native_UClass.ImplementsInterface(Native_UObjectBase.GetClass(Address), interfaceClass)); } return(false); }
private IntPtr FindFirstNativeParentClass(IntPtr unrealClass) { IntPtr sharpStaticClass = Native_USharpClass.StaticClass(); while (unrealClass != IntPtr.Zero && (!Native_UClass.HasAnyClassFlags(unrealClass, EClassFlags.Native) || Native_UObjectBaseUtility.IsA(unrealClass, sharpStaticClass))) { unrealClass = Native_UClass.GetSuperClass(unrealClass); } return(unrealClass); }
/// <summary> /// Gets the UClass address holding the interface information for the given path (e.g. "/Script/MovieScene.MovieSceneEasingFunction") /// </summary> /// <param name="path">The path of the interface</param> /// <returns>The address of the UClass interface information for the given path</returns> public static IntPtr GetInterfaceClassAddress(string path) { IntPtr address = GetClassAddress(path); // Restrict this to just interfaces if (address != IntPtr.Zero && Native_UClass.HasAnyClassFlags(address, EClassFlags.Interface)) { return(address); } return(IntPtr.Zero); }
public bool IsFunctionHidden(string inFunction) { // WITH_EDITOR || HACK_HEADER_GENERATOR if (Native_UClass.IsFunctionHidden == null) { return(false); } using (FStringUnsafe inFunctionUnsafe = new FStringUnsafe(inFunction)) { return(Native_UClass.IsFunctionHidden(Address, ref inFunctionUnsafe.Array)); } }
public bool IsAutoCollapseCategory(string inCategory) { // WITH_EDITOR || HACK_HEADER_GENERATOR if (Native_UClass.IsAutoCollapseCategory == null) { return(false); } using (FStringUnsafe inCategoryUnsafe = new FStringUnsafe(inCategory)) { return(Native_UClass.IsAutoCollapseCategory(Address, ref inCategoryUnsafe.Array)); } }
public bool IsClassGroupName(string inGroupName) { // WITH_EDITOR || HACK_HEADER_GENERATOR if (Native_UClass.IsClassGroupName == null) { return(false); } using (FStringUnsafe inGroupNameUnsafe = new FStringUnsafe(inGroupName)) { return(Native_UClass.IsClassGroupName(Address, ref inGroupNameUnsafe.Array)); } }
private void Constructor(IntPtr objectInitializerPtr) { Native_UClass.Call_ClassConstructor(ParentClass, objectInitializerPtr); FObjectInitializer objectInitializer = new FObjectInitializer(objectInitializerPtr); IntPtr sharpStaticClass = Native_USharpClass.StaticClass(); IntPtr unrealClass = Native_FObjectInitializer.GetClass(objectInitializerPtr); IntPtr sharpClass = unrealClass; while (sharpClass != IntPtr.Zero && !Native_UObjectBaseUtility.IsA(sharpClass, sharpStaticClass)) { sharpClass = Native_UClass.GetSuperClass(sharpClass); } System.Diagnostics.Debug.Assert(sharpClass != IntPtr.Zero); }
public static unsafe void Load() { vtableRedirects = new List <FunctionRedirect>(); AddVTableRedirects(); // We have three classes UDummyObject3 : UDummyObject2 : UDummyObject1 : UObject // // UDummyObject1 overrides function "X" // UDummyObject2 doesn't function "X" // UDummyObject3 overrides function "X" // // Scan the vtable for each dummy object, search for an entry where vtable entry 1==2 && 1!=3 // - We can assume this entry is our desired vtable index // - This may break down in situations where there is multiple inheritance // - If this fails to complete properly this will result in a crash (or worse) foreach (FunctionRedirect redirect in vtableRedirects) { IntPtr dummyClass1 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "1"); IntPtr dummyClass2 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "2"); IntPtr dummyClass3 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "3"); IntPtr dummyObject1 = Native_UClass.GetDefaultObject(dummyClass1, true); IntPtr dummyObject2 = Native_UClass.GetDefaultObject(dummyClass2, true); IntPtr dummyObject3 = Native_UClass.GetDefaultObject(dummyClass3, true); IntPtr *dummyVTable1 = *(IntPtr **)dummyObject1; IntPtr *dummyVTable2 = *(IntPtr **)dummyObject2; IntPtr *dummyVTable3 = *(IntPtr **)dummyObject3; for (int i = 0; i < int.MaxValue; i++) { IntPtr dummyFunc1 = dummyVTable1[i]; IntPtr dummyFunc2 = dummyVTable2[i]; IntPtr dummyFunc3 = dummyVTable3[i]; if (dummyFunc1 == dummyFunc2 && dummyFunc1 != dummyFunc3) { redirect.NativeCallback = dummyFunc1; redirect.VTableIndex = i; break; } } } }
public ManagedUnrealClass(Type type, string packageName, string className, IntPtr parentClass) { Type = type; PackageName = packageName; ClassName = className; ParentClass = parentClass; ClassConstructor = Constructor; ClassVTableHelperCtorCaller = VTableHelperCtorCaller; ClassAddReferencedObjects = AddReferencedObjects; // This is what FKismetCompilerContext::CleanAndSanitizeClass uses IntPtr parentClassWithin = Native_UClass.Get_ClassWithin(ParentClass); WithinClass = parentClassWithin != IntPtr.Zero ? parentClassWithin : Native_UObject.StaticClass(); NonUSharpClassParentClass = FindFirstNonUSharpClassParentClass(ParentClass); NativeParentClass = FindFirstNativeParentClass(ParentClass); }
/// <summary> /// Adds a new default instance map item /// </summary> /// <param name="newSubobject"></param> /// <param name="baseClass"></param> public void AddDefaultSubobject(UObject newSubobject, UClass baseClass) { Native_UClass.AddDefaultSubobject(Address, newSubobject == null ? IntPtr.Zero : newSubobject.Address, baseClass == null ? IntPtr.Zero : baseClass.Address); }
public UClass GetSuperClass() { return(GCHelper.Find <UClass>(Native_UClass.GetSuperClass(Address))); }
public int GetDefaultsCount() { return(Native_UClass.GetDefaultsCount(Address)); }
/// <summary> /// Get the default object from the class /// </summary> /// <param name="createIfNeeded">if true (default) then the CDO is created if it is NULL.</param> /// <returns>the CDO for this class</returns> public UObject GetDefaultObject(bool createIfNeeded = true) { return(GCHelper.Find(Native_UClass.GetDefaultObject(Address, createIfNeeded))); }
/// <summary> /// Searches for the default instanced object (often a component) by name /// </summary> /// <param name="toFind"></param> /// <returns></returns> public UObject GetDefaultSubobjectByName(FName toFind) { return(GCHelper.Find(Native_UClass.GetDefaultSubobjectByName(Address, ref toFind))); }
private static void MetaDataMergeClassCategories(IntPtr metadata, IntPtr obj, Dictionary <FName, string> values) { // Copying the logic in FClassDeclarationMetaData::MergeClassCategories // Engine\Source\Programs\UnrealHeaderTool\Private\ClassDeclarationMetaData.cpp // ShowFunctions HideFunctions // HideCategories ShowCategories ShowSubCatgories // AutoExpandCategories AutoCollapseCategories // - How is ShowFunctions / HideFunctions used? Hiding a function doesn't seem to hide it from being // visible in the actions list in Blueprint. If it isn't super important we could skip it. // - ShowCategories / HideCategories is important // Maybe cache these lists and clear them for each type HashSet <string> showCategories = new HashSet <string>(); HashSet <string> hideCategories = new HashSet <string>(); HashSet <string> showSubCategories = new HashSet <string>(); HashSet <string> showFunctions = new HashSet <string>(); HashSet <string> hideFunctions = new HashSet <string>(); HashSet <string> autoExpandCategories = new HashSet <string>(); HashSet <string> autoCollapseCategories = new HashSet <string>(); HashSet <string> dontAutoCollapseCategories = new HashSet <string>(); HashSet <string> classGroupNames = new HashSet <string>(); GetMetaDataItems(UMeta.GetKeyName(MDClass.ShowCategories), values, showCategories); GetMetaDataItems(UMeta.GetKeyName(MDClass.HideCategories), values, hideCategories); GetMetaDataItems(UMeta.GetKeyName(MDClass.ShowFunctions), values, showFunctions); GetMetaDataItems(UMeta.GetKeyName(MDClass.HideFunctions), values, hideFunctions); GetMetaDataItems(UMeta.GetKeyName(MDClass.AutoExpandCategories), values, autoExpandCategories); GetMetaDataItems(UMeta.GetKeyName(MDClass.AutoCollapseCategories), values, autoCollapseCategories); GetMetaDataItems(UMeta.GetKeyName(MDClass.DontAutoCollapseCategories), values, dontAutoCollapseCategories); GetMetaDataItems(UMeta.GetKeyName(MDClass.ClassGroupNames), values, classGroupNames); IntPtr parentClass = Native_UClass.GetSuperClass(obj); HashSet <string> parentHideCategories = new HashSet <string>(); HashSet <string> parentShowSubCatgories = new HashSet <string>(); HashSet <string> parentHideFunctions = new HashSet <string>(); HashSet <string> parentAutoExpandCategories = new HashSet <string>(); HashSet <string> parentAutoCollapseCategories = new HashSet <string>(); GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.HideCategories), parentHideCategories); GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.ShowCategories), parentShowSubCatgories); GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.HideFunctions), parentHideFunctions); GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.AutoExpandCategories), parentAutoExpandCategories); GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.AutoCollapseCategories), parentAutoCollapseCategories); // Add parent categories. We store the opposite of HideCategories and HideFunctions in a separate array anyway. MetaDataMergeCollection(hideCategories, parentHideCategories); MetaDataMergeCollection(showSubCategories, parentShowSubCatgories); MetaDataMergeCollection(hideFunctions, parentHideFunctions); MetaDataMergeShowCategories(showCategories, hideCategories, showSubCategories); // Merge ShowFunctions and HideFunctions foreach (string value in showFunctions) { hideFunctions.Remove(value); } //showFunctions.Clear(); // Merge DontAutoCollapseCategories and AutoCollapseCategories foreach (string value in dontAutoCollapseCategories) { autoCollapseCategories.Remove(value); } //dontAutoCollapseCategories.Clear(); // The original function then merges ShowFunctions / HideFunctions again? (ShowFunctions will now be empty) // Merge AutoExpandCategories and AutoCollapseCategories (we still want to keep AutoExpandCategories though!) foreach (string value in autoExpandCategories) { autoCollapseCategories.Remove(value); parentAutoCollapseCategories.Remove(value); } // Do the same as above but the other way around foreach (string value in autoCollapseCategories) { autoExpandCategories.Remove(value); parentAutoExpandCategories.Remove(value); } // Once AutoExpandCategories and AutoCollapseCategories for THIS class have been parsed, add the parent inherited categories MetaDataMergeCollection(autoCollapseCategories, parentAutoCollapseCategories); MetaDataMergeCollection(autoExpandCategories, parentAutoExpandCategories); SetOrClearMetaDataClassCollection(MDClass.ClassGroupNames, values, classGroupNames); SetOrClearMetaDataClassCollection(MDClass.AutoCollapseCategories, values, autoCollapseCategories); SetOrClearMetaDataClassCollection(MDClass.HideCategories, values, hideCategories); SetOrClearMetaDataClassCollection(MDClass.ShowCategories, values, showSubCategories); SetOrClearMetaDataClassCollection(MDClass.HideFunctions, values, hideFunctions); SetOrClearMetaDataClassCollection(MDClass.AutoExpandCategories, values, autoExpandCategories); }