Met many issues along the road to make my game run on Xbox. So I thought I could start documenting it.
I recommend checking out this UWP code for monogame by @michaelarts
License
-Applied to the ID@XBOX program (easy to google).
-I emailed them about games I wanted to release. In the beginning they thought my game weren’t showing of the power of the xbox. But they must have lowered their expectations since then, and considering I am a total bottom of the barrel developer, I would say than anyone has a chance today.
-I received a document to fill out. They wanted to know things like: how it plays, how it compares to similar games, is it console exclusive and a couple of screen shots.
Two things to note here: They wanted to try a demo, and they wanted the demo to support an xbox controller.
As part of the license I recieved two xbox dev kits for free (Xbox ONE X and Xbox ONE S). I had to sign a lot of legal papers to never mention them to anyone (only to see them lined up on the E3 showfloor two weeks later).
The mail tracking of the dev kits didnt work, so I had to hunt them down by calling the post office, so make sure to keep an eye on that.
Websites to visit
I got links to a couple of websites. They are all a bit of a half-finished mess, I am still pretty confused why it is this way and I am not sure they do either. Use Internet Explorer for the best experience. Tip is to archive all setup emails you get, I had to go back and read them a couple of time.
-
Xbox Dev Portal (XDP). So far Ive only used it to activate the consoles.
-
Game Developer Network (GDNP). There are some deeply buried tutorials here plus a developer forum that has 2 posts a month.
-
Xbox One Home Page. A new page that is supposed to replace GDNP, not complete but way easier to navigate.
-
Microsoft Account (MSA). I still dont know what I do on this page, but you need to create an account here I guess
-
Windows Dev Center. Manage your game submissions here. Your games end up under “Partner Dashboard”.
Xbox setup
I followed a tutorial I found on: Game Developer Network>Development>Hardware>Recovery and Developing> The Xbox One Dev Kit Quick Start Guide>Getting Started with Xbox One Development>Setting Up Your Xbox One Development Environment>Connecting the Xbox One Hardware
-Hook it up like a normal Xbox, but also plug a controller in the back USB ports (worked without a keyboard for me).
-Don’t let the Xbox update anything in Retail mode, this will remove the ability to boot the dev mode.
If the dev mode is lost, you can go to System→Console info: press LB, RB, LT, RT in sequence.
-The Xbox need to be updated by downloading a OS image called a “Recovery”. See Xbox One XDK & Recovery.
Computer setup
-On the xbox (Console settings>Launch settings) System>Console info>Console Id. Write that down.
-Use your link to the Xbox Dev Portal site. Manage> Console convertion. This site is tricky because it showed a green “READY!” message even when I did the wrong thing. In some instances I had to wait a half an hour for changes to take effect.
On the white sticker on your xbox there is a serial number that starts with “SN”. Select that console on the site and enter the Console ID, included “.” and the last two numbers.
- Install the XDK, was easy but is a download that takes hours.
-You get a “Xbox One Manager” app on your computer. I ran that with the xbox connected to the same network and the app found it immediately. I didn’t have to change anything in my firewall or router (ignoring the instructions to do that).
Visual Studio
I had to install VS2017, and then reinstall Monogame from the development branch. Make sure to include all templates in monogame.
-Start a Window10 Universal project. Right click References>Manage NuGet Packages…>install Microsoft.Xbox.Live.SDK.WinRT.UWP
-Change target platform from x86 to x64.
-Change debugging play arrow from “Local Machine” to “Remote Machine”. I found the Xbox suggested there (while running the Xbox and the Manager app).
-Your game must render something or it will just immediately close.
-The Content pipeline manager stopped working for some reason. You can start it from
C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools\Pipeline.exe
and select your project content file.
Here I was able to run a “hello world” test on my Xbox.
Porting my Game
-I setup a shared project solution so I could toggle between a OpenGL project for computer platforms and DirectX for Xbox. Referencing the Content was a bit tricky, read my solution here.
-I was using the Steam API for user settings and network. I put a layer of abstraction between Steam, Xbox and the rest of the code, example:
//Pseudo code
class User
{
#if STEAMGAME
SteamUser steamuser;
#elif XBOX
XboxLiveUser xboxuser;
#else
//non
#endif
public string Name
{
get
{
#if STEAMGAME
return steamuser.GetName();
#elif XBOX
return xboxuser.GamerTag;
#else
return “Player”;
#endif
}
}
}
-I had to replace “System.Threading.Thread” with “System.Threading.Tasks.Task”. They are very similar in use, just make sure to only let one Task at time access the storage.
-System.IO.Stream.Close() needs to be replaced with “Dispose()”.
-Some of the Shader Effect code needed adjustments
#if OPENGL
#define SV_Position0 POSITION
#define NORMAL0 NORMAL
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
#define VS_SHADERMODEL vs_4_0_level_9_1
#define PS_SHADERMODEL ps_4_0_level_9_1
#endif
struct VS_IN
{
float4 Position : SV_Position0;
float2 TexCoord : TEXCOORD0;
float3 Normal : NORMAL0;
float3 Tangent : NORMAL0;
};
technique Flat
{
pass Pass0
{
VertexShader = compile VS_SHADERMODEL VS_Flat();
PixelShader = compile PS_SHADERMODEL PS_Flat();
}
}
-I temporarily removed all save and load. The Xbox is using some storage container, you cant just save and load anywhere - unlike the PC.
Here I was able to run my game on XBOX, Woohoo!
-But non of the xbox user code works.
Some weird bugs/fixes
-When I reboot the xbox or the computer, I sometimes have to reselect the Remote Machine:
Right-click the UWP project → properties → Debug → Click the Remote Machine “Find…”-button.
If the “Remote Mashine” property is the name of the Xbox, replace that with the IP address instead.
-When debugging against the Xbox, sometimes the deploy just wont run, I fixed this by having a small “hello world” project I deploy on the xbox - and then it works again.
-Something in my new setup makes the visual studio crash if I open a Effect.fx file. But I just edit them in the notepad instead.
-When the Visual Studio crashes, I have to reset the Content build option in the Project.projitems file.
-The conditional compilation intellisense (the text between #if XBOX and #endif) breaks when changing startup project. A fix is to Right-click the other project and select “Unload Project” (and Reload it again if you want to)
-VS2017 was running very slow compared to 2015. Two changes made it much better:
- Tools → Options → Debugging → General → Enable Diagnostics: set to false.
- Help → Send Feedback → Settings… → set to “No”
New XboxLiveUser
I was stuck on this for a long time, and that seems to be a common headache. Because Ive been “pushing every button” for two weeks, I am not entirely sure what I did right - take this list with a grain of salt:
-
A lot of the start setup is made manually by the Xbox employees, I began my certification with some mail exchange.
-
The first choice is between UWP and ERA.
I couldn’t find any information comparing them, so this is what I guess they do:
-
Universial Windows Project - This is an app system that is the same on Xbox, Windows and mobile. Ive heard Xbox run Windows RT as a virtual OS on the console, and the apps on there.
-
Exclusive Resource Access - Runs as a classic xbox game on XboxOS (im just guessing!). Does Monogame support this - I don’t know. I picked this anyway.
-
Now I could find my game on Windows Dev Center Dashboard. Only viewable in Microsoft Edge Browser.
-
Start a submission on Partner Dashboard. I was asked to create a Sandbox first, but that was not possible for me because I wasn’t able to find it at this point.
-
Do the basic setup on the submission. From what I read, the Xbox Live Setup and Product Type are important to be able to test your game.
-
There will be a default Sandbox, but you will need to create a Test Account. It is on Partner Center, but is hidden if you dont use the direct link: https://partner.microsoft.com/en-us/xboxconfig/testaccounts/index.
Sandboxes can be managed from Xbox Dev Portal too. All tutorials I read was instructing me to use XDP, this didnt work and was a total deadend to me - probably outdated.
-
The easiest way to change the sandbox is to click the “Change Sandbox” button on the xbox. You will find the sandbox on Partner Center → Services → Xbox Live, will look something like “XABCDE.0”. Type that exact name, and the xbox will reboot. There will not be any confirmation.
-
Add you test account to the Xbox (XABCDE_Name0001@xboxtester.com). When you login you should get a “Hi Dev” greeting from the Xbox, otherwise there is something wrong with the setup. Sometimes you just need need to wait an hour and try again.
-
In Visual Studio. Right click the primary project, click Store > Associate App with the Store
-
On your StartUp project, right click and select Add > New Item.
Select the Text File type and name it “xboxservices.config”.
Enter this:
{
“TitleId” : “your title ID (JSON number in decimal)”,
“PrimaryServiceConfigId” : “your primary service config ID”,
}
Right click on the file, select Properties and ensure that:
Build Action is set to Content, andCopy to Output Directory is set to Copy Always.
The ID can be found on Partner Center → Services → Xbox Live → Xbox Live Setup. These will be hidden until the Submission is properly setup.
SignIn script example
This is a cleaned up version of the code that @Romans_I_XVI posted. Add “using Microsoft.Xbox.Services.System”, and replace “Debug.Log” with the log of your choice:
class XboxGamer
{
private bool attemptingSignIn = false;
public XboxLiveUser CurrentUser { get; private set; }
public XboxLiveContext CurrentContext { get; private set; }
public XboxGamer()
{
SignIn();
XboxLiveUser.SignOutCompleted += SignOutCompleted;
}
public async void SignIn(bool attempt_silent = true)
{
if (attemptingSignIn)
return;
attemptingSignIn = true;
CurrentUser = new XboxLiveUser();
if (!CurrentUser.IsSignedIn)
{
var coreDispatcher = Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.Dispatcher;
if (attempt_silent)
{
try
{
await CurrentUser.SignInSilentlyAsync(coreDispatcher);
}
catch
{
Debug.Log("SignInSilentlyAsync Threw Exception");
}
}
if (!CurrentUser.IsSignedIn)
{
Debug.Log("Silent Sign-In failed, requesting sign in");
try
{
await CurrentUser.SignInAsync(coreDispatcher);
}
catch
{
Debug.Log("SingInAsync Threw Exception");
}
}
}
WriteInfo();
attemptingSignIn = false;
}
private void SignOutCompleted(object sender, SignOutCompletedEventArgs e)
{
CurrentContext = null;
}
public void WriteInfo()
{
Debug.Log("############ Xbox Live Info ############");
Debug.Log(CurrentUser.Gamertag + " SignedIn(" + CurrentUser.IsSignedIn + ")");
Debug.Log(CurrentUser.XboxUserId);
Debug.Log(CurrentUser.WebAccountId);
Debug.Log("############ Xbox Live Info ############");
}
}
Other sandbox notes
This is a simulated environment for your game, if you wanna see how the game works in a different country and with no network permissions - you can setup a sandbox for that.
-The tutorial wanted me to edit the appxmanifest. But when reading up on it, it is easier to login on Visual Studio with the same Microsoft Live ID (outlook mail address) as you use on Dev Center - the rest should be automatic.
-You will probably not need to use the xbconfig.exe. The settings can be done from the Xbox Manager app (Some tutorials are outdated).
If you still need to run it, here is how:
Run Command Prompt as Admin.
Right-click the window top bar> Properties>Layout tab>Set “Screen buffer size Height” to 1000.
Type “cd C:\Program Files (x86)\Microsoft Durango XDK\bin” to enter the folder.
Type “xbconfig.exe” to boot the config. You should now be able to run scripts like “XBCONNECT”.
User and Controller pairing
If you worked on Xbox360, you might remember that each controller had a signed in user. Xbox ONE does not connect the User and the Controller - and you are allowed to handle that however you want. The User is seen as the person who started the app.
Certification Requirements
My game is very basic, just local multiplayer. The things that I had to work through was mainly to handle the Xbox User correctly:
-
Handle lost controller.
The Monogame input worked by default (Microsoft.Xna.Framework.Input.GamePadState.IsConnected) - so that was an easy task. -
Handle App suspend and resume.
Can be tested by hitting the Guide button, enter the Store, and then back again
_public XboxManager() {
Windows.ApplicationModel.Core.CoreApplication.Suspending +=
new EventHandler<Windows.ApplicationModel.SuspendingEventArgs>(appSuspendEvent);Windows.ApplicationModel.Core.CoreApplication.Resuming += new EventHandler<object>(appResumeEvent); Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.Activated += new Windows.Foundation.TypedEventHandler<Windows.UI.Core.CoreWindow, Windows.UI.Core.WindowActivatedEventArgs>(appWindowActivatedEvent);
}
void appSuspendEvent(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
//Quick save is recommended
Debug.Log(“----SUSPEND”);
}void appResumeEvent(object sender, object args)
{
//Check user change
Debug.Log(“----RESUME”);
}void appWindowActivatedEvent(Windows.UI.Core.CoreWindow window, Windows.UI.Core.WindowActivatedEventArgs args)
{
if (window.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.ActivatedInForeground)
{ //User pressed the guide button, pause the game
Debug.Log(“----GUIDE OVERLAY”);
}
} -
Handle if the user change during the game being in sleep mode.
Whenever I signed out with the User that started the app - the application rebooted. A second user may still sign in/out, and you can choose to handle that. -
Create one save for each user.
It looks like there is two ways to manage the storage. Either with the Windows.System.User or Microsoft.Xbox.Services.System.XboxLiveUser . The XboxOneXDK dll has no c# support, so I used the Windows storage. Does it matter for the end product? - I have no idea.
The save is automatically set in a container with the user. I tried changing user, and that user could not access the save files from the first user. An easy requirement to follow.
Save/Load file script
A reworked example from Windows Dev Center. Because all my save/load code (and network too) uses System.IO.BinaryWriter I had to convert that to Windows.Storage.Streams.DataWriter.
const string c_saveBlobName = "FileName";
const string c_saveContainerDisplayName = "YourGameName Save";
const string c_saveContainerName = "YourGameNameContainer";
public void debugStorage(bool save)
{
if (!gamer.user.IsSignedIn)
{
Debug.Log("Need to sign in");
return;
}
Debug.Log("----DEBUG STORAGE");
var task = System.Threading.Tasks.Task.Factory.StartNew(async () =>
{
var users = await Windows.System.User.FindAllAsync();
Windows.System.User mainUser = users[0];
var configId = XboxLiveAppConfiguration.SingletonInstance.ServiceConfigurationId;
GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(mainUser, configId);
GameSaveProvider gameSaveProvider;
if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
{
gameSaveProvider = gameSaveTask.Value;
}
else
{
Debug.LogError("Game Save Provider Initialization failed");
return;
}
GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
if (save)
{
IBuffer dataBuffer = dataStreamToIBuffer();
var blobsToWrite = new Dictionary<string, IBuffer> { { c_saveBlobName, dataBuffer } };
GameSaveOperationResult gameSaveOperationResult = await gameSaveContainer.SubmitUpdatesAsync(blobsToWrite, null, c_saveContainerDisplayName);
Debug.Log("SAVE SUCCESS");
}
else
{
string[] blobsToRead = new string[] { c_saveBlobName };
GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);
if (result.Status == GameSaveErrorStatus.Ok)
{
//prepare a buffer to receive blob
IBuffer loadedBuffer;
//retrieve the named blob from the GetAsync result, place it in loaded buffer.
result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);
if (loadedBuffer == null)
{
throw new Exception("Blob is empty: " + c_saveBlobName);
}
IBufferToDataSteam(loadedBuffer);
Debug.Log("LOAD SUCCESS");
}
else if (result.Status == GameSaveErrorStatus.BlobNotFound)
{
Debug.Log("There is no save file: " + c_saveBlobName);
}
else
{
Debug.Log("GameSave Error Status: " + result.Status);
}
}
});
try
{
task.Wait();
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
IBuffer dataStreamToIBuffer()
{
//Converting BinaryWriter to DataWriter
System.IO.MemoryStream s = new System.IO.MemoryStream();
System.IO.BinaryWriter w = new System.IO.BinaryWriter(s);
write(w);
DataWriter writer = new DataWriter();
writer.WriteBytes(s.ToArray());
IBuffer dataBuffer = writer.DetachBuffer();
return dataBuffer;
}
void IBufferToDataSteam(IBuffer dataBuffer)
{
DataReader reader = DataReader.FromBuffer(dataBuffer);
byte[] byteArray = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(byteArray);
System.IO.MemoryStream s = new System.IO.MemoryStream(byteArray);
var r = new System.IO.BinaryReader(s);
read(r);
}
void write(System.IO.BinaryWriter w)
{
w.Write(gamer.Name());
w.Write(31);
}
void read(System.IO.BinaryReader r)
{
string name = r.ReadString();
int testval = r.ReadInt32();
Debug.Log(name);
Debug.Log(testval.ToString());
}
Load Content script
This is for the content that you set “Build Action - Copy”. Once again I needed to convert from DataReader to BinaryReader.
void debugContentLoad()
{
var task = System.Threading.Tasks.Task.Factory.StartNew(async () =>
{
Debug.Log("----DEBUG CONTENT LOAD");
var storageFile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(
new Uri("ms-appx:///Content/level1.lvl"));
var dataBuffer = await Windows.Storage.FileIO.ReadBufferAsync(storageFile);
DataReader reader = DataReader.FromBuffer(dataBuffer);
byte[] byteArray = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(byteArray);
System.IO.MemoryStream s = new System.IO.MemoryStream(byteArray);
var r = new System.IO.BinaryReader(s);
readContent(r);
Debug.Log("----LOAD CONTENT COMPLETE");
});
}
void readContent(System.IO.BinaryReader r)
{
while (r.BaseStream.Position < r.BaseStream.Length)
{
Debug.Log(r.ReadByte().ToString());
}
}
B is Back bug fix
There is a bug where the Back button is triggered when you press B. (Issue link)
I wrote this fix:
int ButtonB_KeyUpTime = 0;
public void Update()
{
previousPadState = currentPadState;
currentPadState = GamePad.GetState(index);
#if XBOX
if (currentPadState.IsButtonUp(Buttons.B) && previousPadState.IsButtonDown(Buttons.B))
{
ButtonB_KeyUpTime = 3;
}
else
{
--ButtonB_KeyUpTime;
}
#endif
}
public bool BackButtonDownEvent()
{ //Locks the Back button three frames after B keyUp
#if XBOX
if (ButtonB_KeyUpTime > 0)
{
return false;
}
#endif
return currentPadState.IsButtonDown(Buttons.Back) && previousPadState.IsButtonUp(Buttons.Back);
}
Submit App Packet
-I needed to add a game icon to Package.appxmanifest → Visual Assets.
-Right-click the project→Properties→Application→Min version: had to set it to the latest version.
-Right-click Package.appxmanifest→View Code→ Change TargetDeviceFamily Name=“Windows.Universal” to “Windows.Xbox” (You will need to revert it if you want to debug UWP on the computer)
-Also needed to change target version: MinVersion=“10.0.14393.0”
(Expected min version is listed here)
MaxVersionTested=“10.0.x.0”, you will find the current version on Xbox→Settings→System→OS version. The revision (last number) should always be “0”.
-Make sure the game runs in build: Release.
-Right-click the project→ Store→Create App Packages. Click trough the steps.
I just needed the x64 build, so I unchecked the others.
-I pulled mygame.appx out of the mygame.appxbundle packet.
-The appx packet needed to be renamed. (“Couldn’t find external file reference” error)
Partner Center→MyGame→App management→App identity→Package Family Name: “12345My.Game_abcdefgh”.
Version number is inserted before the underline in the name, resulting in something like this: “12345My.Game_1.1.6.0_x64__abcdefgh.appx”
Achievements
Xbox titles must have at least 10 achievements. I am used to them being in 64x64 icon format - but the Xbox uses a full 1080p image. The only best practices I could find, was that nice images could pull new customers if they see their friends having them.
You can see games achievements from Xbox.com, select a game and click the “Game Hub” link.
I looked through a bunch of games. Most of the indie titles just have one fancy background and then a different icon in the middle for each achievement. This is from “Graveyard keeper”:
When achieved
When locked (the color saturation is automated)
Hidden (The player has access to the Locked Description)
-Can achievements be edited or removed before launch: Yes.
-Can released achievements be edited or removed: I don’t know.
-Can more achievements be added after launch: Yes.
-Can you reset unlocked achievements: No.
After adding achievements to the Dashboard, they need to be activated by Partner Center→YourGame→Submission→Xbox Live→Press the “Test” button.
“Xbox Live achievements” must be selected in Submission Properties.
public void AchievementTest(XboxGamer gamer)
{
const uint Complete = 100;
int AchievementId = 1;
var task = Task.Factory.StartNew(() =>
{ //Had to put it in a task, otherwise it froze the game
try
{
gamer.context.AchievementService.UpdateAchievementAsync(
gamer.user.XboxUserId, AchievementId.ToString(), Complete);
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
});
}
A few certification failures I had
-
Partner Center→YourGame→Submission→Pricing and availability→Markets: Can only have the Xbox markets selected. And also had to remove China.
In the end I had to remove all countries that don’t support English only, I was planning to localize - but time ran out.
List of countries and localization -
Submission Properties: Xbox Live features. Achievements and Cloud saves must be selected. Clubs and Presence should be removed (until you set them up).
-
I used icons for start and back that looks like the Xbox360 ones.
-
Broke Xbox Live Fine Grained Rate Limit. Basically the rich presence and achievements can’t be updated more than a couple of times per second. I guess the testers broke it by spamming back and forward in the lobby.
-
I was a bit too much on the edge when used I a rich presence that said “Dropping my balls” for the bagatelle mode.
Rich Presence
Even if I unselected presence in submission properties, my validation kept failing. I think you will have at least one Rich Presence string anyway, and it can’t be “”. Will this become a default presence text? - I don’t know.
public void SetRichPresence(string presenceName)
{
if (HasSignedInGamer)
{
try
{
PresenceData data = new PresenceData(gamer.context.AppConfig.ServiceConfigurationId, presenceName);
//TODO, add logic that prevents the presence to update too often (Fine Grained Rate Limit)
gamer.context.PresenceService.SetPresenceAsync(true, data);
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
}
…
Working on my second Certification.
Will update this as I go on…