[TIP] Generating Game Build numbers for your project

Here is a quick tip on how to generate game build numbers for your C++ games. The only downside to this method is it will always increment the build number even if your project fails to build. But anyway, i hope this little info helps you. :slight_smile:

1 Like

Whoa that’s really neat RyanJon, thanks for sharing!

:slight_smile:

Rama

Wanted to change this to use a relative path (different users with C:\Users[USERNAME]\Documents\Unreal Projects[PROJECTNAME] as the project directory), but a quick glance at RulesCompiler.cs suggested TargetRules doesn’t have a lot of great methods for accessing the project path. The following appears to work (at least for a stock single-module project) until somebody has a better way:

string PROJECT_PATH = RulesCompiler.AllGameFolders[0];
string GAME_VERSION_FILE = PROJECT_PATH + “/Source/[PROJECTNAME]/Public/GameVersion.h”;
string GAME_BUILD_FILE = PROJECT_PATH + “/Source/GAME_BUILD.txt”;

Awesome stuff thanks!

An update regarding the use of relative paths.
It seems that RulesCompiler class no longer contains property AllGameFolders, however it is possible to retrieve the project directory of the game in this way:

string ProjectDirectory = this.ProjectFile.Directory.FullName;

Where “this” is your project TargetRules class.
Then concatenate the ProjectDirectory string to obtain the absolute path of your GameVersionFile and GameBuildFile.

UE5, I use this:

       if (ProjectFile is null)
        {
            throw new BuildException("No project file defined");
        }

        // The absolute path to Project source dir
        var projectDirectory = Path.Combine(ProjectFile.Directory.FullName, "Source", PROJECTNAME);
        // The absolute path to GameVersion header file
        var gameVersionFile = Path.Combine(projectDirectory, "Public", "GameVersion.h");
        // The absolute path to GameBuild text file
        var gameBuildFile = Path.Combine(projectDirectory, "GameBuild.txt");
        // The text to replace in GameVersion header file
        const string buildNumberDefinitionText = "#define GAME_BUILD_NUMBER";

        Console.WriteLine("... GameVersion Header File -> " + gameVersionFile);
        Console.WriteLine("... GameBuild Text File -> " + gameBuildFile);

        if (!File.Exists(gameVersionFile) || !File.Exists(gameBuildFile))
        {
            throw new BuildException("Failed to get GameVersion.h and GameBuild.txt. Make sure they exist!");
        }

        var gameVersionFileInfo = new FileInfo(gameVersionFile);
        var gameBuildFileInfo = new FileInfo(gameBuildFile);
        gameVersionFileInfo.IsReadOnly = false;
        gameBuildFileInfo.IsReadOnly = false;

        var gameBuildFileContent = File.ReadAllText(gameBuildFile);
        var buildNumber = gameBuildFileContent.Length > 0 ? int.Parse(gameBuildFileContent) : 0;
        buildNumber++;
        
        var gameVersionFileContent = File.ReadAllText(gameVersionFile);
        if (!gameVersionFileContent.Contains(buildNumberDefinitionText))
        {
            throw new BuildException("GameVersion.h doesn't contain \"" + buildNumberDefinitionText + "\"");
        }
        
        var gameVersionFileLines = new List<string>(File.ReadAllLines(gameVersionFile));
        var lineContainsBuildNumber = 0;
        for (var i = 0; i < gameVersionFileLines.Count; i++)
        {
            if (gameVersionFileLines[i].Contains(buildNumberDefinitionText))
            {
                lineContainsBuildNumber = i;
            }
        }

        if (lineContainsBuildNumber == 0)
        {
            throw new BuildException("Not found the line in GameVersion.h that contains \"" + buildNumberDefinitionText + "\"");
        }

        gameVersionFileLines.RemoveAt(lineContainsBuildNumber);
        gameVersionFileLines.Insert(lineContainsBuildNumber, (buildNumberDefinitionText + " " + buildNumber.ToString()));
        File.WriteAllLines(gameVersionFile, gameVersionFileLines);
        File.WriteAllText(gameBuildFile, buildNumber.ToString());

… is there a reason you wouldn’t use your master source control’s change identifier?

People who build games alone on their own computer probably don’t have an automated build server set up, that’s responsible for producing the shipping bits.
In that case, using something locally available seems easier.
Also, when users are looking for version numbers to file bug reports or whatever, they’re not going “I’m using version 1f60c1e381169199b12a99bc3408656607f06a89 and the Flaming Sword of Fire has an ice effect” – some more user-readable version/release number would be needed.
If you’re using something older with linear history, like Subversion or Perforce, then the change number from that system might work.

In general, it’s a best practice to have a release builder script that does all the steps of building a release, and this is generally different from your typical “change, rebuild, test-again” iteration loop, so that’s a good place to increment a “release number.” Or just use the YYMMDD format date – if you have to build and ship two successive releases in the same day, chances are, something else is the matter :slight_smile:

everything i’ve worked on in modern history, has used a Perforce CL#, a Plastic… whatever they call their identifiers, or the first 6 characters of the git hash for a build number, along with the date the build was made.