private IntPtr CreateProperty(IntPtr outer, PropertyInfo propertyInfo) { // Note that HeaderParser.cpp and UObjectGlobals.cpp use "new" instead of NewObject for creating properties // KismetCompilerMisc.cpp uses NewObject // The "new" initialization sets the offset and adds the property to the owner which in the case of UStruct // does the following: // void UStruct::AddCppProperty(UProperty* Property) { Property->Next = Children; Children = Property; } USharpPathAttribute pathAttribute = propertyInfo.GetCustomAttribute <USharpPathAttribute>(); if (pathAttribute == null) { return(IntPtr.Zero); } string root, directory, moduleName, typeName, propertyName; FPackageName.GetPathInfo(pathAttribute.Path, out root, out directory, out moduleName, out typeName, out propertyName); if (string.IsNullOrEmpty(propertyName)) { return(IntPtr.Zero); } IntPtr property = CreateProperty(outer, propertyInfo.PropertyType, propertyName, pathAttribute.PropertyType, pathAttribute.InnerPropertyType1, pathAttribute.InnerPropertyType2); if (property == IntPtr.Zero) { return(IntPtr.Zero); } if (FBuild.WithMetaData) { IntPtr outermost = Native_UObjectBaseUtility.GetOutermost(property); IntPtr metadata = outermost == IntPtr.Zero ? IntPtr.Zero : Native_UPackage.GetMetaData(outermost); if (metadata != IntPtr.Zero) { string categoryName = null; //propertyInfo.GetCustomAttribute if (string.IsNullOrEmpty(categoryName)) { categoryName = "Default"; } SetMetaData(metadata, property, "Category", categoryName); } } return(property); }
public void OnCodeGenerated(CodeGenerator.UnrealModuleInfo module, UnrealModuleType moduleAssetType, string typeName, string path, string code) { // Note: path will be empty if using combined enums file or global delegates files string root, directory, moduleName, assetName, memberName; FPackageName.GetPathInfo(path, out root, out directory, out moduleName, out assetName, out memberName); //Log("path:'{0}' root:'{1}' directory:'{2}' asset:'{3}' member:'{4}'", path, root, directory, assetName, memberName); string rootFolderName = GetRootFolderName(path, root, module.Type, moduleAssetType); if (string.IsNullOrEmpty(rootFolderName) && !string.IsNullOrEmpty(path)) { Log(ELogVerbosity.Error, "Unknown asset root '{0}' ModuleType:'{1}' ModuleAssetType:'{2}' Path:'{3}'", root, module.Type, moduleAssetType, path); return; } string name = Settings.UseTypeNameAsSourceFileName || string.IsNullOrEmpty(assetName) ? typeName : assetName; string sourceFilePath = null; string projPath = null; string slnPath = null; switch (module.Type) { case UnrealModuleType.Game: { string relativeSourceFilePath = null; if (EmulateGameFolderStructure(moduleAssetType)) { relativeSourceFilePath = Path.Combine(directory, name + ".cs"); } else { relativeSourceFilePath = name + ".cs"; } string baseCodeDir = Settings.GetGeneratedCodeDir(false); if (module.IsBlueprint) { baseCodeDir = Path.Combine(Settings.GetManagedDir(), Settings.GetProjectName() + ".Managed", "Blueprint", "Generated"); } if (moduleAssetType == UnrealModuleType.Unknown) { // Don't use root folders for native game code wrappers as root folders don't make much sense for them sourceFilePath = Path.Combine(baseCodeDir, relativeSourceFilePath); } else { sourceFilePath = Path.Combine(baseCodeDir, rootFolderName, relativeSourceFilePath); } slnPath = GameSlnPath; projPath = module.IsBlueprint ? GameProjPath : GameNativeGenerationProjPath; } break; case UnrealModuleType.EnginePlugin: case UnrealModuleType.Engine: { bool mergeAsPluginProj = false; bool mergeAsUnrealProj = false; switch (Settings.EngineProjMerge) { case CodeGeneratorSettings.ManagedEngineProjMerge.Engine: if (module.Type == UnrealModuleType.Engine) { mergeAsUnrealProj = true; } break; case CodeGeneratorSettings.ManagedEngineProjMerge.Plugins: if (module.Type == UnrealModuleType.EnginePlugin) { mergeAsPluginProj = true; } break; case CodeGeneratorSettings.ManagedEngineProjMerge.EngineAndPlugins: if (module.Type == UnrealModuleType.EnginePlugin) { mergeAsPluginProj = true; } else { mergeAsUnrealProj = true; } break; case CodeGeneratorSettings.ManagedEngineProjMerge.EngineAndPluginsCombined: mergeAsUnrealProj = true; break; } if (mergeAsPluginProj || mergeAsUnrealProj) { string projName = mergeAsUnrealProj ? "UnrealEngine.csproj" : "UnrealEngine.Plugins.csproj"; if (Settings.EngineProjMerge == CodeGeneratorSettings.ManagedEngineProjMerge.EngineAndPluginsCombined) { projPath = Path.Combine(Settings.GetManagedModulesDir(), projName); } else { projPath = Path.Combine(Settings.GetManagedModulesDir(), rootFolderName, projName); } } else { projPath = Path.Combine(Settings.GetManagedModulesDir(), rootFolderName, module.Name, module.Name + ".csproj"); } sourceFilePath = Path.Combine(Settings.GetManagedModulesDir(), rootFolderName, module.Name, name + ".cs"); if (Settings.ModulesLocation == CodeGeneratorSettings.ManagedModulesLocation.GameFolderCombineSln) { slnPath = GameSlnPath; } else if (Settings.ModulesLocation == CodeGeneratorSettings.ManagedModulesLocation.GameFolderCombineSlnProj) { slnPath = GameSlnPath; projPath = GameProjPath; } else { slnPath = Path.Combine(Settings.GetManagedModulesDir(), "UnrealEngine.sln"); } } break; case UnrealModuleType.GamePlugin: { if (moduleAssetType == UnrealModuleType.Unknown) { // Don't use root folders for native game code wrappers as root folders don't make much sense for them sourceFilePath = Path.Combine(Settings.GetGeneratedCodeDir(true), module.Name, name + ".cs"); } else { sourceFilePath = Path.Combine(Settings.GetGeneratedCodeDir(true), rootFolderName, module.Name, name + ".cs"); } slnPath = GameSlnPath; if (Settings.GameProjMerge == CodeGeneratorSettings.ManagedGameProjMerge.GameAndPlugins) { projPath = GameNativeGenerationProjPath; } else if (Settings.GameProjMerge == CodeGeneratorSettings.ManagedGameProjMerge.Plugins) { projPath = GamePluginGenerationProjPath; } else { projPath = Path.Combine(Settings.GetManagedDir(), rootFolderName, module.Name, module.Name + ".csproj"); } } break; } if (!string.IsNullOrWhiteSpace(sourceFilePath)) { sourceFilePath = Path.GetFullPath(sourceFilePath); } if (!string.IsNullOrWhiteSpace(projPath)) { projPath = Path.GetFullPath(projPath); } if (!string.IsNullOrWhiteSpace(slnPath)) { slnPath = Path.GetFullPath(slnPath); } if (string.IsNullOrWhiteSpace(sourceFilePath) || string.IsNullOrWhiteSpace(projPath) || string.IsNullOrWhiteSpace(slnPath)) { Log(ELogVerbosity.Error, "Unknown output location for '{0}' '{1}'", typeName, path); } else if (!ValidateOutputPath(sourceFilePath) || !ValidateOutputPath(projPath) || !ValidateOutputPath(slnPath)) { Log(ELogVerbosity.Error, "Invalid output path '{0}'", sourceFilePath); } else { //FMessage.Log(ELogVerbosity.Log, sourceFilePath + " | " + projPath + " | " + slnPath); try { if (UpdateSolutionAndProject(slnPath, projPath)) { if (!AddSourceFile(slnPath, projPath, sourceFilePath, code)) { Log(ELogVerbosity.Error, "Failed to add source file '{0}'", sourceFilePath); } } else { Log(ELogVerbosity.Error, "Failed to create sln/csproj '{0}' '{1}'", slnPath, projPath); } } catch (Exception e) { Log(ELogVerbosity.Error, "Exception when adding source file '{0}' {1}", sourceFilePath, e); } } }
public static ManagedUnrealClass CreateClass(Type type) { ManagedUnrealClass existingClass = FindClass(type); if (existingClass != null) { if (!FBuild.WithHotReload) { // TODO: Add support for hotreloading C# classes when WITH_HOT_RELOAD isn't available // - WITH_HOT_RELOAD will be false on shipping, monolithic and server builds // - Would need to make a copy of FHotReloadClassReinstancer (or just use it directly if // it doesn't depend on WITH_HOT_RELOAD and gets compiled into builds) // - Would likely break blueprint classes which depend on any C# classes reinstanced in this way return(existingClass); } existingClass.Clear(); HotReloadClassCount++; } if (!type.IsSubclassOf(typeof(UObject))) { return(null); } USharpPathAttribute pathAttribute = type.GetCustomAttribute <USharpPathAttribute>(); if (pathAttribute == null || string.IsNullOrEmpty(pathAttribute.Path)) { return(null); } IntPtr parentClass = GetStaticClass(type.BaseType); if (parentClass == IntPtr.Zero) { return(null); } string root, directory, moduleName, className, memberName; FPackageName.GetPathInfo(pathAttribute.Path, out root, out directory, out moduleName, out className, out memberName); string packageName = "/" + root + "/" + directory; if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(className)) { return(null); } IntPtr package = NativeReflection.FindObject(Native_UPackage.StaticClass(), IntPtr.Zero, packageName, true); if (package == IntPtr.Zero) { package = NativeReflection.CreatePackage(IntPtr.Zero, packageName); Native_UPackage.SetPackageFlags(package, EPackageFlags.CompiledIn); // TODO: Find how to create a proper guid for a package (UHT CodeGenerator.cpp seems to use a crc of generated code) using (System.Security.Cryptography.SHA256 sha256 = System.Security.Cryptography.SHA256.Create()) { byte[] hash = sha256.ComputeHash(Encoding.ASCII.GetBytes(packageName)); // Truncate the hash byte[] buffer = new byte[16]; Buffer.BlockCopy(hash, 0, buffer, 0, buffer.Length); Native_UPackage.SetGuid(package, new Guid(buffer)); } } ManagedUnrealClass managedUnrealClass = null; if (existingClass != null) { managedUnrealClass = existingClass; } else { managedUnrealClass = new ManagedUnrealClass(type, packageName, className, parentClass); } managedUnrealClass.StaticClass = USharpClass.CreateClassPtr( managedUnrealClass.PackageName, managedUnrealClass.ClassName, (uint)Native_UStruct.GetPropertiesSize(managedUnrealClass.ParentClass), EClassFlags.None, EClassCastFlags.None, managedUnrealClass.ConfigName, managedUnrealClass.ParentClass, managedUnrealClass.WithinClass, managedUnrealClass.ClassConstructor, managedUnrealClass.ClassVTableHelperCtorCaller, managedUnrealClass.ClassAddReferencedObjects); Native_UObjectBase.UObjectForceRegistration(managedUnrealClass.StaticClass); if (existingClass == null) { Classes.Add(type, managedUnrealClass); ClassesByAddress.Add(managedUnrealClass.StaticClass, managedUnrealClass); } managedUnrealClass.Initialize(); return(managedUnrealClass); }