Inside Unreal: Adding Mod Support with the Simple UGC Plugin

This UGC Example Repo has disappeared.
https://github.com/EpicGames/UGCExample

Can someone provide a new link?

If you see a 404 on GitHub for Unreal Engine resources it’s most likely due to one of the two following reasons:

  1. You haven’t connected your Epic Games and GitHub accounts
  2. You’re not logged in to GitHub

(confirmed the repository is still available)

I compile a mod for the game, and this error comes out

Hey @Fusion_The_Best, it’s best to post the entire error log. We can’t see anything past “CookOnTh…”!

Okay this seems like a stupid question to ask but I can’t find the solution anywhere.
When trying to build the UGC example project for windows in shipping configuration I get the error: “UnrealBuildTool : error : Found ‘MyFirstTestMod’ plugin in two locations (D:\Unreal\UnrealEngine\Projects\UGCExample\Mods\MyFirstTestMod\MyFirstTestMod.uplugin and D:\Unreal\UnrealEngine\Projects\UGCExample\Mods\MyFirstTestMod\MyFirstTestMod.uplugin). Plugin names must be unique.”
It is the same location and I only used the UGC Buttons to create the Mod.
Anyone know why this happens and maybe can give a short explanation?
I Hope others have the same issue for the sake of finding a fix.
I created a seperate forum thread for this matter: UGC Build Errors

Has anyone else built this for 4.27? I downloaded the 4.27 release code from GitHub, as well as the UGCExample project. I followed the UGC Plugin quick start instructions carefully, and successfully compiled all the required Visual Studio projects, along with the UGCExample project. I could open the UGCExample project in 4.27 without any issues. So far, so good. I could add mods via the UGC plugin buttons, add content, and package that mod up seemingly without issues.

Because this is 4.27, I had to cook and package the UGCExample to have a compatible packaged game to test my mods in. This went as expected without any issues. I added all the needed files from the cooked game to the correctly named folders for the UGC plugin to do it’s thing, so as I said, everything seemed to work just fine. I could open the UGCExample game that was packaged for 4.27 and everything looked as it should. I had my mod there and I could open the mod’s map from the menu. The mod’s map loaded up without problems, but there were no materials on any of the objects. Just the gray checkerboard base material. I could play this in PIE with my materials all showing up and looking as they should. Well, I thought to myself that I must have missed a step or did something wrong. After hours of working on figuring that out, I decided to just delete the UGCExample project and start from scratch. I thought maybe there were some issues between the engine version that the sample project was made in and 4.27. It was a long shot, but I figured it couldn’t hurt.

After recompiling the AutomationTool to clear up anything there, I used my build of 4.27 to create a fresh project with starter content. I went through each and every step in the quickstart guide to make sure I wasn’t making any mistakes. After quite a bit of time waiting on VS to finish compiling everything, I had my shiny new project, ready to go. I created a new mod and in that mod I made a single blueprint object with a single, very simple, material. This material has no textures so that I could eliminate any issues with textures (I don’t know what issues there could be, but it’s better to be safe). I ran the game in PIE without any problems. I could see my material, just as I could in the UGCExample project. But once again, when I packaged the mod up and opened it in the packaged game the materials were not there, and all that was there was the gray checkerboard base material. I knew that I had done everything correctly this time, so I decided to experiment a little more. I should have done that from the beginning, of course, but I was under the assumption that I had made a mistake (it was the most likely scenario, in my opinion).

To test a little more, I created a simple Blueprint script to make the object bob up and down on Tick. In PIE mode, everything looks fine so I package the mod and tested in the packaged game. Sure enough, the script is working fine and the object is bobbing slowly up and down…but still no materials. I decided the next step was to see if the mesh was being changed, so I removed the sphere from my test object and added a static mesh set to SM_Arrows. I packaged the mod again and ran it in the packaged game. When the mod was applied, the sphere in the game was successfully replaced with the arrows static mesh…but still no materials. As a last test, I thought I would apply one of the materials from the base game itself. I could see the chairs in my test level and they looked fine, so I chose the material for the chairs. I applied the M_Chair material to the arrows mesh, tested in PIE, packaged the mod and tried it in the packaged game. The arrows showed up just fine, bobbing up and down, with the material from the chairs applied exactly as they were in PIE. So, for some reason, the materials from the mod are either not being packaged or they are not being loaded. Materials from the base game will work fine, but not from the mod. Everything else makes it over just fine.

I apologize for such a long-winded post, but I wanted to be clear that I DID follow the quickstart guide closely and everything was working correctly, except the materials. To test this, I don’t believe you need to create a new project. The issue doesn’t appear to be in the sample project (no surprise there). If you download the latest release (currently 4.27.0) and follow the steps in the quick start guide, you should be able to reproduce this. I haven’t flagged this as a bug because I am still not 100% certain that the problem isn’t me. Nope, I don’t trust me one bit. If anyone could give this a go and see if this is indeed a bug, I would REALLY appreciate it. I mean, not enough to send you cookies or anything, but still.

I’ve downloaded all the files needed to test in 4.26.2, and I can confirm that this is a 4.27 issue. In 4.26.2 everything works just fine and all the materials and textures show up exactly how you would expect. I will submit this as a bug on the UGCSample projects GitHub page.

Just in case anyone’s wondering why they still can’t see the project on Github (e.g., a 404 error in its place) even after clicking all the right things on the UE and Github sites, it may be because you didn’t check your email and click the final join link after. Just happened to me.

I have compiled successfully in 4.27, but THERE will be an error when I package: Failed to find command PackageUGC, has anyone encountered it? Thank you

I fixed the problem, which was caused by not compiling the engine source code,Need to build the project according to the https://github.com/EpicGames/UGCExample/blob/release/Documentation/QuickStart.md instance, constructs the complete engine,And in the construction of the engine needed to change the configuration scheme to Develoment

I’m using the UGCExample project in 4.27.1 - how do I package the game without any mods. I tried to put the mod folder as a directory never to cook in the package settings but does not work complety.

The content does not show up in the package but the package still thinks there is a mod there because the mod title is there when I run it.

Thanks,

Has anyone edited the automation code so that it would builds the mod’s .pak files for linux too? I just looked into the code this night, but I only managed to edit the code so that it would build for only linux or windows. But I would like it to build for both linux and windows when clicking the “Package UGC” and also compressing the packaged mod to the .zip file. Currently I really lack programming skills so I decided to ask here if some of you have done it. I will be updating this if I get it working. :+1:

I think I got it now.
Here is my PackageSimpleUGCPlugin.cs that creates .pak files for both Linux and Windows.

// Copyright Epic Games, Inc. All Rights Reserved.

using AutomationTool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnrealBuildTool;
using Tools.DotNETCommon;

using Microsoft.Win32;
using System.Diagnostics;


namespace SimpleUGC.Automation

{
	public class PackageUGC : BuildCommand
	{
		static public ProjectParams GetParams(BuildCommand Cmd, string ProjectFileName, out FileReference PluginFile)
		{
			string VersionString = Cmd.ParseParamValue("Version", "NOVERSION");

			// Get the plugin filename
			string PluginPath = Cmd.ParseParamValue("PluginPath");
			if (PluginPath == null)
			{
				throw new AutomationException("Missing -PluginPath=... argument");
			}

			// Check it exists
			PluginFile = new FileReference(PluginPath);
			if (!FileReference.Exists(PluginFile))
			{
				throw new AutomationException("Plugin '{0}' not found", PluginFile.FullName);
			}

			string ReleaseVersion = Cmd.ParseParamValue("BasedOnReleaseVersion", "UGCExampleGame_v1");

			FileReference ProjectFile = new FileReference(ProjectFileName);

			ProjectParams Params = new ProjectParams(
				RawProjectPath: ProjectFile,
				Command: Cmd,
				ClientTargetPlatforms: new List<TargetPlatformDescriptor>() { new TargetPlatformDescriptor(UnrealTargetPlatform.Win64) },
				Build: false,
				Cook: true,
				Stage: true,
				Pak: true,
				Manifests: true,
				DLCIncludeEngineContent: true, // Need this to allow engine content that wasn't cooked in the base game to be included in the PAK file 
				BasedOnReleaseVersion: ReleaseVersion,
				DLCName: PluginFile.GetFileNameWithoutAnyExtensions(),

				RunAssetNativization: true
				);



			Params.ValidateAndLog();
			return Params;
		}
		
		static public ProjectParams GetBuild(BuildCommand Cmd, string ProjectFileName, out FileReference PluginFile)
		{
			string VersionString = Cmd.ParseParamValue("Version", "NOVERSION");

			// Get the plugin filename
			string PluginPath = Cmd.ParseParamValue("PluginPath");
			if (PluginPath == null)
			{
				throw new AutomationException("Missing -PluginPath=... argument");
			}

			// Check it exists
			PluginFile = new FileReference(PluginPath);
			if (!FileReference.Exists(PluginFile))
			{
				throw new AutomationException("Plugin '{0}' not found", PluginFile.FullName);
			}

			string ReleaseVersion = Cmd.ParseParamValue("BasedOnReleaseVersion", "UGCExampleGame_v1");

			FileReference ProjectFile = new FileReference(ProjectFileName);

			ProjectParams LinuxParams = new ProjectParams(
				RawProjectPath: ProjectFile,
				Command: Cmd,
				ClientTargetPlatforms: new List<TargetPlatformDescriptor>() { new TargetPlatformDescriptor(UnrealTargetPlatform.Linux) },
				Build: false,
				Cook: true,
				Stage: true,
				Pak: true,
				Manifests: true,
				DLCIncludeEngineContent: true, // Need this to allow engine content that wasn't cooked in the base game to be included in the PAK file 
				BasedOnReleaseVersion: ReleaseVersion,
				DLCName: PluginFile.GetFileNameWithoutAnyExtensions(),

				RunAssetNativization: true
				);



			LinuxParams.ValidateAndLog();
			return LinuxParams;
		}
	
		public override void ExecuteBuild()
		{
			int WorkingCL = -1;
			FileReference PluginFile = null;
			string ProjectFileName = ParseParamValue("Project");
			if (ProjectFileName == null)
			{
				ProjectFileName = CombinePaths(CmdEnv.LocalRoot, "SimpleGame", "SimpleGame.uproject");
			}
			LogInformation(ProjectFileName);


			ProjectParams Params = GetParams(this, ProjectFileName, out PluginFile);
			ProjectParams LinuxParams = GetBuild(this, ProjectFileName, out PluginFile);



			// Check whether folder already exists so we know if we can delete it later
			string PlatformStageDir = Path.Combine(Params.StageDirectoryParam, "WindowsNoEditor");
			bool bPreExistingStageDir = Directory.Exists(PlatformStageDir);

			string LinuxPlatformStageDir = Path.Combine(LinuxParams.StageDirectoryParam, "LinuxNoEditor");
			bool bPreExistingLinuxStageDir = Directory.Exists(LinuxPlatformStageDir);

			PluginDescriptor Plugin = PluginDescriptor.FromFile(PluginFile);

			FileReference ProjectFile = new FileReference(ProjectFileName);

			// Add Plugin to folders excluded for nativization in config file
			FileReference UserEditorIni = new FileReference(Path.Combine(Path.GetDirectoryName(ProjectFileName), "Config", "UserEditor.ini"));
			bool bPreExistingUserEditorIni = FileReference.Exists(UserEditorIni);
			if (!bPreExistingUserEditorIni)
			{
				// Expect this most of the time so we will create and clean up afterwards
				DirectoryReference.CreateDirectory(UserEditorIni.Directory);
				CommandUtils.WriteAllText(UserEditorIni.FullName, "");
			}

			const string ConfigSection = "BlueprintNativizationSettings";
			const string ConfigKey = "ExcludedFolderPaths";
			string ConfigValue = "/" + PluginFile.GetFileNameWithoutAnyExtensions() + "/";

			ConfigFile UserEditorConfig = new ConfigFile(UserEditorIni);
			ConfigFileSection BPNSection = UserEditorConfig.FindOrAddSection(ConfigSection);
			bool bUpdateConfigFile = !BPNSection.Lines.Exists(x => String.Equals(x.Key, ConfigKey, StringComparison.OrdinalIgnoreCase) && String.Equals(x.Value, ConfigValue, StringComparison.OrdinalIgnoreCase));
			if (bUpdateConfigFile)
			{
				BPNSection.Lines.Add(new ConfigLine(ConfigLineAction.Add, ConfigKey, ConfigValue));
				UserEditorConfig.Write(UserEditorIni);
			}

			Project.Cook(Params);
			if (!bPreExistingUserEditorIni)
			{
				FileReference.Delete(UserEditorIni);
			}

			Project.Cook(LinuxParams);
			if (!bPreExistingUserEditorIni)
			{
				FileReference.Delete(UserEditorIni);
			}

			Project.CopyBuildToStagingDirectory(Params);
			Project.Package(LinuxParams);
			Project.Archive(Params);
			Project.Deploy(Params);

			Project.CopyBuildToStagingDirectory(LinuxParams);
			Project.Package(LinuxParams, WorkingCL);
			Project.Archive(LinuxParams);
			Project.Deploy(LinuxParams);




			// Get path to where the plugin was staged
			string StagedPluginDir = Path.Combine(PluginFile.GetFileNameWithoutAnyExtensions(), PlatformStageDir, Path.GetFileNameWithoutExtension(ProjectFileName));
			string StagedLinuxPluginDir = Path.Combine(PluginFile.GetFileNameWithoutAnyExtensions(), LinuxPlatformStageDir, Path.GetFileNameWithoutExtension(ProjectFileName));
			File.Delete(PlatformStageDir + "/" + Path.GetFileNameWithoutExtension(ProjectFileName) + "/" + "Mods/" + PluginFile.GetFileNameWithoutAnyExtensions() + "/" + PluginFile.GetFileNameWithoutAnyExtensions() + ".uplugin");
			string ZipFile = Path.Combine(Params.StageDirectoryParam, "Win" + PluginFile.GetFileNameWithoutAnyExtensions());
			string LinuxZipFile = Path.Combine(Params.StageDirectoryParam, "Linux" + PluginFile.GetFileNameWithoutAnyExtensions());
			string ZipReleaseFile = Path.Combine(Params.StageDirectoryParam, PluginFile.GetFileNameWithoutAnyExtensions());
			CommandUtils.DeleteFile(ZipFile);
			CommandUtils.DeleteFile(LinuxZipFile);

			System.IO.Compression.ZipFile.CreateFromDirectory(StagedPluginDir, ZipFile + ".zip");
			System.IO.Compression.ZipFile.CreateFromDirectory(StagedLinuxPluginDir, LinuxZipFile + ".zip");


			//Exracting the .Zip files to the same path so we can create one file for both Win and Linux.
			System.IO.Compression.ZipFile.ExtractToDirectory(Params.StageDirectoryParam + "/Linux" + PluginFile.GetFileNameWithoutAnyExtensions() + ".zip", Params.StageDirectoryParam + "/");
			System.IO.Compression.ZipFile.ExtractToDirectory(Params.StageDirectoryParam + "/Win" + PluginFile.GetFileNameWithoutAnyExtensions() + ".zip", Params.StageDirectoryParam + "/");

			//Compresses the all the needed files for Win and Linux to one .Zip file
			System.IO.Compression.ZipFile.CreateFromDirectory(Params.StageDirectoryParam + "/Mods", ZipReleaseFile + ".zip");
			CommandUtils.DeleteDirectory(Params.StageDirectoryParam + "/Mods");



			if (!bPreExistingStageDir)
			{
				CommandUtils.DeleteDirectory(PlatformStageDir);

			}
			if (!bPreExistingLinuxStageDir)
			{
				CommandUtils.DeleteDirectory(LinuxPlatformStageDir);


			}
		}
	}
}


This plugin is great!

1 Like

Hi, this is very nice, after few days struggling, I add mod support for my project. And, I want to push it a little further, can I add cpp code for a mod ?

I have another question, how can I package main game without any mods? As far as I can see, I think Mods build is build after main game manually, instead of pack with the main game. In other word, Should I delete all my Mods when I package Main game?

im getting a 404 and its linked and im signed in

Hi! I had the same problem as you and on top of that my mods packaged using the “Package-UGC”-Button did not show up on my mod list. I fexed both by renaming the Mods folder before opening the editor and packaging my game. I think this has to be a bug or something.

Is this plugin dead?

I can’t find it anywhere under plugins in Unreal Engine 4.27.2.

There’s no code example from any Unreal Engine develoeprs on youtube, except for the 1 hour and 30 min vid from UE team.

There’s no documentation anywhere for this on UE4 documentation website.

1 Like

How did you get the UGC plugin and example projects?

How do you Package C++ scripts.??
Creating PAK doesnt seem to include C++ Scripts in it.