Download

Publishing an Unreal Engine Project to Microsoft Store

We’ve started developing an Unreal Engine project and we decided the best distribution channel would be the Microsoft Store. Unreal Engine is a Win32 type of software and Microsoft Store didn’t support publishing such a format of applications until recently. The process of publishing Unreal Engine apps (and games) to Microsoft Store still seems to be not very well documented. I hope this short guide may help someone to get through the publishing process.

I’ve decided to write this guide because I’ve found this and this questions on the answer hub and this and this thread on these forums, neither of which were mentioning getting a UE project to the Microsoft Store as a real option, or at least by the time of writing this. Which almost turned me away. In fact by the time these topics and questions were written, the MS Store only supported a thing called UWP, aka Universal Windows Applications. Perhaps they were hoping they can force the world to a change (again). But in 2019 they changed their mind in an xbox.com news post:

Good news, nowadays publishing UE projects on the MS Store is possible, I have just done it with an alpha of our project.

What needs to be done in order to publish a UE project on MS Store:

  1. Create a Microsoft partner account, you will be asked to pay a fee.
  2. Build your Unreal Engine project for shipping.
  3. Create an installer for the app/game, e.g. using Inno Setup, video guide (the guy’s got a really bad mike but otherwise the tutorial is very much on point).
  4. Use the MSIX Packaging Tool to convert the installer to an MSIX package, which can be uploaded to the Store.

What the MSIX Packaging Tool does is basically running your regular Windows installer generated by Inno Setup and watching what it does. It then records the installer’s behavior into the MSIX package, so the Microsoft Store app can replicate it. Yes, it’s probably really necessary to create an installer just for the sake of running the MSIX Packaging Tool, or at least I haven’t found a way how to create such a package from the Unreal Engine built files directly.

Once you upload the MSIX package to the store, you can start your submission and apply for certification. The process should go rather smooth and the staff will likely ask you to clarify some information about your app before the first publish. Originally the Microsoft Store was designed for UWP (Windows Universal Apps), which were also able to run ARM platforms (like the Surface series). As far as I know the kind of Windows that’s running on ARM is called Windows 10 “S”. If you are building your UE project for regular desktop computers us, then the staff will require you to mention in your app’s description that it’s “Not supported on Windows 10 S”. So you can save your time mentioning that right away. Besides that, make sure you provide real info, icons, screenshots, privacy policy, etc. These can shorten your time from upload to the first publish.

And that’s it.

It sounds simple but comes with hours of trial and error figuring out how things like Unreal Engine launch profiles, Inno Setup, and the MSIX Tool work if you are not already familiar with any of these. Eventually, you will also want to automate this package building process, so you can focus on developing features in your iterations instead of manually building packages. So I have decided to write the detailed part using a continuous integration tool I have developed for this purpose, but you can also use the information to publish manually.

Automation with Dozer

Warning: nerdy Git + command line area ahead.

Dozer is a cloud-independent continuous integration / continuous development/deployment/delivery (whatever you call it), which you can use on your desktop or server to build Unreal Engine projects. I’ve developed it simply because I couldn’t find any cloud CI/CD solution that could handle building Unreal Engine. If you know any, please let me know.

Once you set everything up, this will be your workflow for every release:

  1. Develop your app, make Git commits after each change.
  2. When you are ready to release, open command line in your UE project and run dozer ci.yaml
  3. Magic happens and your MSIX package appears under the Distribution subfolder of your project. You can then directly upload it to the MS Store.

First, the following tools are required in your environment:

Please note that the following set up will automatically generate Git release tags for you. If you already work with Git tags for release in your repository, you may want to adjust the CI workflow to your needs, so your Git tags won’t get messed up. If you haven’t worked with Git tags before (or in your project’s Git repository), just continue.

Preparing the Repository

Because the MS Store (and all app stores) requires your app packages to come in versions, you would manually need to enter a version for Inno Setup and MSIX package every time you want to roll a release. Keeping your project versioned is a tedious task, especially if you also want to keep track of changes, not just increment numbers.

There is a standardized approach for this, it’s called the semantic versioning and mainly, there are powerful tools to help you, which directly integrate with git. One of them is Commitizen. Basically Commitizen will add a Git command git cz, which will guide you through a standardized approach of writing commit messages. Once you start committing with Commitizen (or you can even keep the commit message format manually, but git cz is easiest for beginners), you can then use a tool designed for calculating version and generating changelogs from your commits. This is what we’ll use semantic-release for and a couple of our own scripts to update the version for Inno Setup and MSIX Tool.

Before we get to build the Dozer MSIX package-generating workflow, we need to do a one-time setup in our repository to make it friendly with semantic versioning. Open a command line in the root directory of your project (assuming it’s the root of your Git repository as well). And run the following command:

npm init

This will create a package.json file where some important information will be stored, but not the basic information about the project you will be asked in the command line at this point. So just fill any name, any description, and so on.

Next, let’s make the git repository semantic-versioned with the following command:

commitizen init cz-conventional-changelog --save-dev --save-exact

Now if you type git cz, you should see the Commitizen command line UI. If Commitizen wasn’t set up properly in your Git repository, you will only be asked to fill a commit message the standard way.

This is it for initial setup. Now we can proceed to get Dozer to do the work for us.

Dozer Step 1: Generate Changelogs, Save Version as CI_VERSION Environment Variable

Update the package.json file with the following release section:


{
  // ...
  "release": {
    "repositoryUrl": "your repo URL",
    "plugins": 
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      
        "./semantic-release-env-version.js",
        {
          "varName": "CI_VERSION",
          "setOnlyOnRelease": false
        }
      ]
    ]
  }
}

Create a script file called semantic-release-env-version.js in the root directory and paste these contents into it:


const { spawnSync } = require("child_process")
let mod = {
  async analyzeCommits (pluginConfig, { lastRelease: { version }, logger }) {
    const setOnlyOnRelease = pluginConfig.setOnlyOnRelease === undefined ? true : !!pluginConfig.setOnlyOnRelease

    if (!setOnlyOnRelease) {
      const varName = pluginConfig.varName || 'nextRelease'
      console.log(`##${varName}=${version}
`)
    }
  },
  async prepare (pluginConfig, { nextRelease: { version }, logger }) {
    const varName = pluginConfig.varName || 'nextRelease'
    console.log(`##${varName}=${version}
`)
  }
}
module.exports = mod

Next, let’s create a file called ci.yaml in the same directory, from which Dozer will read what to do:


steps:
- displayName: 'Semantic Release'
  exec: npx
  args:
    - semantic-release
    - --no-ci

Now if you run dozer ci.yaml --gui in your project’s root, you should see how semantic-version is determining your project’s version and then saves it under the CI_VERSION variable. This will be 1.0.0 initially. Semantic-version will automatically create Git tags for each release, but only if new commits are present in the repository’s master branch, otherwise it will keep the version the same. So you can feel safe to run this Dozer pipeline repeatedly for testing purposes. The version won’t be incremented unless you actually commit changes to the project.

Later when you have semantic commits, a CHANGELOG.md file will generate each time you run Dozer in your project’s root. You may want to add it to your .gitignore](Git - gitignore Documentation) file.

Dozer Step 2: Build Unreal Engine Project For Shipping

First, create your Unreal Engine launch profile, in short:

  1. Open your project in the UE editor.
  2. Open project launcher. Screenshot_57.png
  3. Add a new custom launch profile.
  4. Set the following options:
  • Project > Which project would you like to use?* (select your project)*

  • Build > Do you wish to build? Build

  • Build > Build configuration: Shipping

  • Cook > How would you like to cook the content? By the book

  • Cook > Cooked platforms: WindowsNoEditor

  • Cook > Select your cooked cultures (*en *by default)

  • Cook > Cooked maps: (select your maps to cook)

  • Cook > Advanced Settings → Check Store all content in a single PAK file (UnrealPak)

  • Cook > Advanced Settings → Cooker build configuration: Shipping

  • Package > How would you like to package the build? Package & store locally

  • Package > Local directory path: should be set to yourProject/Saved/StagedBuilds, check twice

  • Package > Check Is this build for distribution to the public

  • Package > Check Include an installer for prerequisites packaged games

  • Deploy > How would you like to deploy the build? Do not deploy

  1. Click the *Back *button
  2. Run the launch profile.
  3. An .exe file of your game should appear under Saved/StagedBuilds/WindowsNoEditor, try to run it and test if everything is OK.
  4. Run the launch profile and when it starts, one of the first lines of the output list the command line arguments of the build tool you need:

Click this line and click the Copy button at the bottom. Once you’ve got this, the rest is simple.

Just add the following Unreal Engine Shipping Release step to ci.yaml with your own generated command line options.


steps:
- displayName: 'Semantic Release'
exec: npx
args:
- semantic-release
- --no-ci

- displayName: 'Unreal Engine Shipping Release'
exec: **C:\Program Files\Epic Games\UE_4.25\Engine\Build\BatchFiles\RunUAT.bat**
args:
- -ScriptsForProject=%cd%/**SigmaPlanner.uproject**
- BuildCookRun
- -project=%cd%/**SigmaPlanner.uproject**
- -noP4
- -clientconfig=Shipping
- -serverconfig=Shipping
- -nocompile
- -nocompileeditor
- -installed
- -utf8output
- -platform=Win64
- -targetplatform=Win64
# The following line confuses Dozer, leave it out, it is not required
#- -ue4exe="C:\Program Files\Epic Games\UE_4.25\Engine\Binaries\Win64\UE4Editor-Cmd.exe"
- -build
- -cook
- -map=Empty+Liberland+Valley+World
- -unversionedcookedcontent
- -pak
- -createreleaseversion=
- -distribution
- -compressed
- -prereqs
- -stage
- -package
- -cmdline=" -Messaging" -addcmdline="-SessionId=11777909483F0989D1E8A89C7EAC330A -SessionOwner='Mirek' -SessionName='Shipping' "

Just change the paths to your project folder and to your Unreal Engine’s RunUAT.bat file (bold). Then try to delete the contents of Saved/StagedBuilds/ and run dozer ci.yaml --gui in the command line again. You should see your built files appear under StagedBuilds.

Dozer Step 3: Update Windows Installer Template

Create a file called inno-installer.iss in your project’s root and paste the following contents in it (edit the bold values to suit your project):


; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "**Sigma Planner**"
#define MyAppVersion "1.0.0"
#define MyAppPublisher "**Transhuma**"
#define MyAppURL "**https://transhuma.tech/**"
#define MyAppExeName "**SigmaPlanner.exe**"

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{F1EEC2FF-F1BC-4C03-AAC0-16C8ACD01936}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; The [Icons] "quicklaunchicon" entry uses {userappdata} but its [Tasks] entry has a proper IsAdminInstallMode Check.
UsedUserAreasWarning=no
; Remove the following line to run in administrative install mode (install for all users.)
PrivilegesRequired=lowest
OutputDir=.\Distribution\
OutputBaseFilename=SigmaPlanerInstaller
SetupIconFile=.\sigma-planner-ue-icon.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode

[Files]
Source: ".\Saved\StagedBuilds\WindowsNoEditor\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent



This is a file which the Inno Setup reads to generate your regular Windows installer. Now we just need to add a script to update the MyAppVersion variable on each release.

Create another file in your project’s root called update-inno-installer.js and paste the following contents in it:


let parser = require('xml2json')
let fs = require('fs')

let file = 'inno-installer.iss'
let nextVersion = process.env'CI_VERSION']

let config = fs.readFileSync(file).toString()

config = config.replace(/\d+\.\d+\.\d+/g, nextVersion)
fs.writeFileSync(file, config)

console.log('Updated', file, 'with', nextVersion)

Finally, add this Update Inno Setup template step to ci.yaml:


steps:
- displayName: 'Semantic Release'
  exec: npx
  args:
    - semantic-release
    - --no-ci

- displayName: 'Unreal Engine Shipping Release'
  exec: C:\Program Files\Epic Games\UE_4.25\Engine\Build\BatchFiles\RunUAT.bat
  args:
    - -ScriptsForProject=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - BuildCookRun
    - -project=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - -noP4
    - -clientconfig=Shipping
    - -serverconfig=Shipping
    - -nocompile
    - -nocompileeditor
    - -installed
    - -utf8output
    - -platform=Win64
    - -targetplatform=Win64
    - -build
    - -cook
    - -map=Empty+Liberland+Valley+World
    - -unversionedcookedcontent
    - -pak
    - -createreleaseversion=
    - -distribution
    - -compressed
    - -prereqs
    - -stage
    - -package
    - -cmdline=" -Messaging" -addcmdline="-SessionId=11777909483F0989D1E8A89C7EAC330A -SessionOwner='Mirek' -SessionName='Shipping' "

- displayName: 'Update Inno Setup template'
  exec: node
  args:
    - update-inno-installer.js

Dozer Step 4: Generate Windows Installer

This one is easy, just add another step to ci.yaml:


steps:
- displayName: 'Semantic Release'
  exec: npx
  args:
    - semantic-release
    - --no-ci

- displayName: 'Unreal Engine Shipping Release'
  exec: C:\Program Files\Epic Games\UE_4.25\Engine\Build\BatchFiles\RunUAT.bat
  args:
    - -ScriptsForProject=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - BuildCookRun
    - -project=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - -noP4
    - -clientconfig=Shipping
    - -serverconfig=Shipping
    - -nocompile
    - -nocompileeditor
    - -installed
    - -utf8output
    - -platform=Win64
    - -targetplatform=Win64
    - -build
    - -cook
    - -map=Empty+Liberland+Valley+World
    - -unversionedcookedcontent
    - -pak
    - -createreleaseversion=
    - -distribution
    - -compressed
    - -prereqs
    - -stage
    - -package
    - -cmdline=" -Messaging" -addcmdline="-SessionId=11777909483F0989D1E8A89C7EAC330A -SessionOwner='Mirek' -SessionName='Shipping' "

- displayName: 'Update Inno Setup template'
  exec: node
  args:
    - update-inno-installer.js

- displayName: 'Inno Setup Compiler'
  exec: C:\Program Files (x86)\Inno Setup 6\ISCC.exe
  args:
    - inno-installer.iss

Just double-check the path to ISCC.exe is correct. Running dozer ci.yaml --gui now should get you a usable installer under yourProject/Distribution. Make sure it works correctly before proceeding.

Dozer Step 5: Update the MSIX Packaging Tool Template

Create a file called msix-tool-template.xml in your project’s root and paste the following contents in it (edit the bold values to suit your project):


<?xml version="1.0"?>
<MsixPackagingToolTemplate xmlns="http://schemas.microsoft.com/appx/msixpackagingtool/template/2018">
  <Settings AllowTelemetry="false" ApplyAllPrepareComputerFixes="false" GenerateCommandLineFile="true" AllowPromptForPassword="false" p4:EnforceMicrosoftStoreRequirements="true" p5:ServerPortNumber="1599" p6:AddPackageIntegrity="true" p7:SupportedWindowsVersionForMsixCore="None" xmlns:p7="http://schemas.microsoft.com/msix/msixpackagingtool/template/2004" xmlns:p6="http://schemas.microsoft.com/msix/msixpackagingtool/template/2001" xmlns:p5="http://schemas.microsoft.com/msix/msixpackagingtool/template/1904" xmlns:p4="http://schemas.microsoft.com/msix/msixpackagingtool/template/2007">
    <ExclusionItems>
      <FileExclusion ExcludePath="{CryptoKeys}]" />
      <FileExclusion ExcludePath="{Common AppData}]\Microsoft\Crypto" />
      <FileExclusion ExcludePath="{Common AppData}]\Microsoft\Search\Data" />
      <FileExclusion ExcludePath="{Cookies}]" />
      <FileExclusion ExcludePath="{History}]" />
      <FileExclusion ExcludePath="{Cache}]" />
      <FileExclusion ExcludePath="{Personal}]" />
      <FileExclusion ExcludePath="{Profile}]\Local Settings" />
      <FileExclusion ExcludePath="{Profile}]\NTUSER.DAT.LOG1" />
      <FileExclusion ExcludePath="{Profile}]\NTUSER.DAT.LOG2" />
      <FileExclusion ExcludePath="{Recent}]" />
      <FileExclusion ExcludePath="{Windows}]\Debug" />
      <FileExclusion ExcludePath="{Windows}]\Logs\CBS" />
      <FileExclusion ExcludePath="{Windows}]\Temp" />
      <FileExclusion ExcludePath="{Windows}]\WinSxS\ManifestCache" />
      <FileExclusion ExcludePath="{Windows}]\WindowsUpdate.log" />
      <FileExclusion ExcludePath="{Windows}]\Installer" />
      <FileExclusion ExcludePath="{PackageDrive}]\$Recycle.Bin" />
      <FileExclusion ExcludePath="{PackageDrive}]\System Volume Information" />
      <FileExclusion ExcludePath="{AppData}]\Microsoft\AppV" />
      <FileExclusion ExcludePath="{Local AppData}]\Packages" />
      <FileExclusion ExcludePath="{Local AppData}]\Temp" />
      <FileExclusion ExcludePath="{Local AppData}]\Microsoft\Windows" />
      <FileExclusion ExcludePath="{Common AppData}]\Microsoft\Microsoft Security Client" />
      <FileExclusion ExcludePath="{Common AppData}]\Microsoft\Microsoft Antimalware" />
      <FileExclusion ExcludePath="{Common AppData}]\Microsoft\Windows Defender" />
      <FileExclusion ExcludePath="{ProgramFiles}]\Microsoft Security Client" />
      <FileExclusion ExcludePath="{ProgramFiles}]\Windows Defender" />
      <FileExclusion ExcludePath="{ProgramFiles}]\WindowsApps" />
      <FileExclusion ExcludePath="{PackageDrive}]\Config.Msi" />
      <FileExclusion ExcludePath="{Local AppData}]\Microsoft\OneDrive" />
      <FileExclusion ExcludePath="{Local AppData}]\Temp" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Microsoft\Cryptography" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Microsoft\Microsoft Antimalware" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Microsoft\Microsoft Antimalware Setup" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Microsoft\Microsoft Security Client" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Policies\Microsoft\Microsoft Antimalware" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Microsoft\Windows\CurrentVersion\Explorer\StreamMRU" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\StreamMRU" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Microsoft\Windows\CurrentVersion\Explorer\Streams" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Streams" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Microsoft\AppV" />
      <RegistryExclusion ExcludePath="REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\AppV" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Microsoft\AppV" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]\Software\Wow6432Node\Microsoft\AppV" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]_Classes\AppID\OneDrive.EXE" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]_Classes\OOBERequestHandler.OOBERequestHandler" />
      <RegistryExclusion ExcludePath="REGISTRY\USER\{CurrentUserSID}]_Classes\SyncEngineFileInfoProvider.SyncEngineFileInfoProvider" />
    </ExclusionItems>
  </Settings>
  <PrepareComputer DisableWindowsSearchService="false" DisableWindowsUpdateService="true" />
  <SaveLocation PackagePath="" TemplatePath="**C:\Users\Mirek\Desktop\projects	ranshuma\SigmaPlanner\msix-tool-template.xml**" />
  <Installer Path="**C:\Users\Mirek\Desktop\projects	ranshuma\SigmaPlanner\Distribution\SigmaPlanerInstaller.exe**" Arguments="/VERYSILENT" />
  <PackageInformation PackageName="**35499MirekandTranshuma.SigmaPlanner**" PackageDisplayName="**Sigma Planner**" PublisherName="**CN=F7FB543B-7431-468A-81EA-1E73A61D6D05**" PublisherDisplayName="**Mirek and Transhuma**" Version="1.0.0.0" />
</MsixPackagingToolTemplate>

Alright, here we have a few tricky variables to figure out, which is PackageName, PublisherName, and PublisherDisplayName. These are specific for each app and each publisher on the MS Store. So mine will be different from yours. The bad news is that I don’t personally know how do you find out the right values. Maybe someone else does, check the comments. The good news is that if you upload your app to the MS Store with incorrect values, the uploader will reject your package and tell you what the expected values are. Either way you should be fine after a bit of trial and error.

Now let’s create a file called update-msix-tool-template.js, fill it with the following script and again, edit information maked as bold:


let parser = require('xml2json')
let fs = require('fs')

let file = 'msix-tool-template.xml'
let nextVersion = process.env'CI_VERSION']

let json = parser.toJson(fs.readFileSync(file), { object: true })

json.MsixPackagingToolTemplate.PackageInformation.Version = `${nextVersion}.0`
json.MsixPackagingToolTemplate.SaveLocation.PackagePath = `**.\\Distribution\\35499MirekandTranshuma.SigmaPlanner_${nextVersion}.0_x64__ynrec8ts70sx6.msix**`

fs.writeFileSync(file, parser.toXml(json))

console.log('Updated', file, 'with', nextVersion)

IMPORANT! Keep the ${nextVersion} part in the *PackagePath *as this is the best way to meet the MS Store’s requirement to have each uploaded package named differently.

The build suffix _ynrec8ts70sx6 is required and again is specific to your MS Partner account, as far as I know. You might also need to find out the hard way: uploading your package to the store and let the uploader tell you.

Finally, let’s add this as a Dozer step:


steps:
- displayName: 'Semantic Release'
  exec: npx
  args:
    - semantic-release
    - --no-ci

- displayName: 'Unreal Engine Shipping Release'
  exec: C:\Program Files\Epic Games\UE_4.25\Engine\Build\BatchFiles\RunUAT.bat
  args:
    - -ScriptsForProject=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - BuildCookRun
    - -project=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - -noP4
    - -clientconfig=Shipping
    - -serverconfig=Shipping
    - -nocompile
    - -nocompileeditor
    - -installed
    - -utf8output
    - -platform=Win64
    - -targetplatform=Win64
    - -build
    - -cook
    - -map=Empty+Liberland+Valley+World
    - -unversionedcookedcontent
    - -pak
    - -createreleaseversion=
    - -distribution
    - -compressed
    - -prereqs
    - -stage
    - -package
    - -cmdline=" -Messaging" -addcmdline="-SessionId=11777909483F0989D1E8A89C7EAC330A -SessionOwner='Mirek' -SessionName='Shipping' "

- displayName: 'Update Inno Setup template'
  exec: node
  args:
    - update-inno-installer.js

- displayName: 'Inno Setup Compiler'
  exec: C:\Program Files (x86)\Inno Setup 6\ISCC.exe
  args:
    - inno-installer.iss

- displayName: 'Update MSIX template'
  exec: node
  args:
    - update-msix-tool-template.js

Dozer Step 6: Generate MSIX package

This one is easy again, just add the file Dozer step:


steps:
- displayName: 'Semantic Release'
  exec: npx
  args:
    - semantic-release
    - --no-ci

- displayName: 'Unreal Engine Shipping Release'
  exec: C:\Program Files\Epic Games\UE_4.25\Engine\Build\BatchFiles\RunUAT.bat
  args:
    - -ScriptsForProject=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - BuildCookRun
    - -project=C:/Users/Mirek/Desktop/projects/transhuma/SigmaPlanner/SigmaPlanner.uproject
    - -noP4
    - -clientconfig=Shipping
    - -serverconfig=Shipping
    - -nocompile
    - -nocompileeditor
    - -installed
    - -utf8output
    - -platform=Win64
    - -targetplatform=Win64
    - -build
    - -cook
    - -map=Empty+Liberland+Valley+World
    - -unversionedcookedcontent
    - -pak
    - -createreleaseversion=
    - -distribution
    - -compressed
    - -prereqs
    - -stage
    - -package
    - -cmdline=" -Messaging" -addcmdline="-SessionId=11777909483F0989D1E8A89C7EAC330A -SessionOwner='Mirek' -SessionName='Shipping' "

- displayName: 'Update Inno Setup template'
  exec: node
  args:
    - update-inno-installer.js

- displayName: 'Inno Setup Compiler'
  exec: C:\Program Files (x86)\Inno Setup 6\ISCC.exe
  args:
    - inno-installer.iss

- displayName: 'Update MSIX template'
  exec: node
  args:
    - update-msix-tool-template.js

# Dozer has to be run with admin rights
- displayName: 'MSIX Packaging Tool'
  exec: MsixPackagingTool
  args:
    - 'create-package'
    - --template
    - msix-tool-template.xml
    - -v

From now on, you will have to run dozer ci.yaml --gui (or just dozer ci.yaml without GUI) with administrator rights for the MSIX Packaging Tool step to succeed. Eventually, if everything goes well, you should end up with your MSIX package under yourProject/Distribution/. You can take it and choke it directly to the MS Store’s app submission package uploader.

Outro

Thanks for reading. I hope this guide helped you.

  • Feel free to put your questions in the comments, I will try to help and update the guide if some information is unclear.
  • If you find a Dozer bug, please report it here.

Additionally, if you are feeling thankful for this guide, please check my humble effort to make the world better for everyone and consider donating. :slight_smile:

2 Likes

I was wondering if it was possible. Ive read many articles and forums and I was about to give up.
You just post a well explained guide 1 day before! Huge thanks!
I will try your solution!

Dozer step to update the ProjectVersion in Config/DefaultGame.ini

Works in case the file exists and the variable in the file does too. If either is missing, just go to the project settings and edit the version to “1.0.0” (the format “N.N.N” has to be kept).

Node.JS has to be installed.

**Dozer yaml **(add under steps:):


- displayName: 'Update UE config'
  exec: node
  args:
    - update-ue-config.js

update-ue-config.js:



let parser = require('xml2json')
let fs = require('fs')

let file = 'Config/DefaultGame.ini'
let nextVersion = process.env'CI_VERSION']

let config = fs.readFileSync(file).toString()

config = config.replace(/\d+\.\d+\.\d+/g, nextVersion)
fs.writeFileSync(file, config)

console.log('Updated', file, 'with', nextVersion)


I needed to change my project path to D:, which required changing paths along in my CI setup. Instead, to make the CI setup more adaptive in terms of paths, I decided to make the paths relative. (updated the original tut).

Hi, thank you for this information. But why you don’t tell anything about signing? I mean MSIX want any sertificate as pfx, cert or something else. Without this certificate package can’t install for testing because it show untrusted… how about this?

Thank you for this guide, it helped a lot!!!

But with submission to the MS App Store i got this message here and submission failed:

"App Policies: 10.1.2 Functionality Notes To Developer We have detected that your app is incompatible with Windows 10 S. Please update your app to support Windows 10 S if possible. Otherwise include a prominent statement anywhere in the Metadata that the app does not support Windows 10 S. This issue may affect merchandisability.

Please be sure to test your app for Windows 10 S: Test your Windows app for Windows 10 S - MSIX | Microsoft Docs Tested Devices: Acer Switch 3, High Performance Desktop, Microsoft Surface Laptop

→ i found in your text this here: “Not supported on Windows 10 S” - But where did you tell MS this in the Submission Text “Notes for certification”?

Finally it worked as mentioned above: The “Not supported on Windows 10 S” goes into “Notes for certification” field.