Submitting your TGB game to the Mac AppStore
by Aun Taraseina · in Torque Game Builder · 02/04/2011 (1:51 am) · 24 replies
With in a few days of the Mac AppStore opening I saw so many success stories about how an old app got ported to the Mac AppStore and sold hundreds. And to be honestly, if you have a working iPhone game or a TGB, TGE game it would be silly not to submit your game to the Mac AppStore. I have to tell you upfront that I'm not Mac guru, in fact I've just started working on the Mac so if the steps I've provide are not the best way to do so or you have some other cool ways that's easier, please let me know. The only thing I'm concern is getting the job done with no bugs in my game.
There are however, a piece of new code you'll have to add to your game in order to validate the users receipt. If the user have bought your game then start your game as normal, if the user have not bought your game then exit your game with code 173 which will tell the OSX to launch an iTune User and Password dialog box so the users can type in their account of which they've used to buy your game.
TGB when going into full screen doesn't check if the device supports the resolution or not. I got my game rejected due to this one since my device supports all kind of resolution but the iMac (the testers use) doesn't support it. In "macCarbOGLVideo.cc" locate
When starting your game in full screen mode and then switching to windowed mode, TGB creates another black console window. I can never figure out what caused this since I'm not sure if this is an OSX bug or what but I know that it happens when you call "setScreenMode" in the same call stack as "createCanvas" so my simple fix is in (Torque Script) "common/gamescript/canvas.cs" change it so it looks like this
Still in "enginesourcegamemain.cc" locate "DemoGame::mainInit(int argc, const char **argv)" where
After validating the receipt, we will notify the OS if the receipt pass validation. If the validation passes, run the game normally, if not, then exit the game with exit code 173. In "enginesourceplatformMacCarbmacCarbMain.cc" locate "main(S32 argc, const char **argv)" where
Next we will change the default location of our "console.log" so it can be saved in the application home directory or you can just change "setlog(0)" to disable the "console.log". I'm against disabling the "console.log" since if your users have any kind of trouble with your game, the only way to diagnose the problem is from the "console.log".
In "enginesourceconsoleconsole.cc" locate "static void log(const char *string)" where
Still in "enginesourceconsoleconsole.cc" locate "void setLogMode(S32 newMode)" where
Now, we'll set the the console.log to be enabled after we have initialize our game. The reason is because our game name and application home directory is created after our game is initialize so in script open up your main "main.cs" where
We will enable the console.log as soon as we know the application home directory. In "commonmain.cs" locate where
Wooo... that was a long process of getting everything linked up in this thread. Let me know if you guys have any problems with it. Good luck Guys!
You can grab the full engine source code changes from this thread Bubble Breaker Source code release
Mac AppStore
From the look of the Mac platform code, it seems to me that it is a bit more out dated than the window version. It has a few bugs handling full screen mode such as not checking the supported resolution of the monitor before going into full screen mode or having a black console window when the game starts in full screen(since these 2 bugs are critical to get your game approved, I have provided the fixes in the next section) and other bugs related to how events are handled on the Mac platform such as "cmd+tab" in full screen or losing focus doesn't seem to work properly.There are however, a piece of new code you'll have to add to your game in order to validate the users receipt. If the user have bought your game then start your game as normal, if the user have not bought your game then exit your game with code 173 which will tell the OSX to launch an iTune User and Password dialog box so the users can type in their account of which they've used to buy your game.
Part A : TGB Engine changes
There 2 parts in this topic which are1. Bug fixes
Lets start with fixing some of TGB bugs which will cause you to fail the review process without doing so- TGB window screen size is smaller that it should be by 90 pixels in height. (This fix was provided by Matt Fairfax at this link) At the top of "macCarbOGLVideo.cc" locate
#define OSX_MENUBAR_OFFSET 90And change it to
#ifdef TORQUE_TOOL #define OSX_MENUBAR_OFFSET 90 #else #define OSX_MENUBAR_OFFSET 0 #endif
bool OpenGLDevice::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint )
{
Con::printf(" set screen mode %i x %i x %i, %s, %s, %s",width, height, bpp,
fullScreen ? "fullscreen" : "windowed",
forceIt ? "force it" : "dont force it",
repaint ? "repaint" : "dont repaint"
);
// validation, early outs --------------------------------------------------
// sanity check. some scripts are liable to pass in bad values.
if(!bpp)
bpp = platState.desktopBitsPixel;
Resolution newRes = Resolution(width, height, bpp);And change it tobool OpenGLDevice::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint )
{
// validation, early outs --------------------------------------------------
// sanity check. some scripts are liable to pass in bad values.
if(!bpp)
bpp = platState.desktopBitsPixel;
Resolution newRes = Resolution(width, height, bpp);
// Aun : Find the closest best screen size that the device will support
if(fullScreen)
{
U32 resIndex = 0;
U32 bestScore = 0, thisScore = 0;
for(int i = 0; i < mResolutionList.size(); i++)
{
if(newRes == mResolutionList[i])
{
resIndex = i;
break;
}
else if(newRes.h <= platState.desktopHeight && newRes.w <= platState.desktopWidth)
{
thisScore = abs(S32(newRes.w) - S32(mResolutionList[i].w))
+ abs(S32(newRes.h) - S32(mResolutionList[i].h))
+ (newRes.bpp == mResolutionList[i].bpp ? 0 : 1);
if(!bestScore || (thisScore < bestScore))
{
bestScore = thisScore;
resIndex = i;
}
}
}
newRes = mResolutionList[resIndex];
}
Con::printf(" set screen mode %i x %i x %i, %s, %s, %s",newRes.w, newRes.h, bpp,
fullScreen ? "fullscreen" : "windowed",
forceIt ? "force it" : "dont force it",
repaint ? "repaint" : "dont repaint"
);function initializeCanvas(%windowName)
{
// Don't duplicate the canvas.
if($canvasCreated)
{
error("Cannot instantiate more than one canvas!");
return;
}
videoSetGammaCorrection($pref::OpenGL::gammaCorrection);
// Aun : hack so the screen will be positioned correctly, or else the screen will go full screen and be positioned uncorrectly
if($platform $= "windows")
{
%tempFullScreen = $pref::Video::fullScreen;
$pref::Video::fullScreen = false;
}
if (!createCanvas(%windowName))
{
error("Canvas creation failed. Shutting down.");
quit();
}
// [neo, 5/11/2007 - #3051
// Resolution is set to $Game::Resolution in levelEditor properties.ed.cs
//%goodRes = $Game::WindowResolution;>
%goodres = $Game::Resolution;
// Aun : hack so the screen will be positioned correctly
if($platform $= "windows")
{
$pref::Video::fullScreen = %tempFullScreen;
}
if($platform $= "windows")
{
setScreenMode(GetWord( %goodres, 0), GetWord( %goodres,1), GetWord( %goodres,2), $pref::Video::fullScreen);
}
$canvasCreated = true;
}2. TGB modification to submit your game for the Mac AppStore
- It is Apple's recommendation that you validate the user's receipt before any UI of your game displays. So we will add the receipt validation right after TGB libraries initialize. In "enginesourcegamemain.cc" at the top of the file where
#include "game/net/serverQuery.h" #include "game/demoGame.h" #include "platform/nativeDialogs/msgBox.h"Add
#include "game/net/serverQuery.h" #include "game/demoGame.h" #include "platform/nativeDialogs/msgBox.h" #ifdef MAC_APPSTORE #include "validatereceipt.h" #endifStill in "enginesourcegamemain.cc" where
extern bool gDGLRender; bool gShuttingDown = false;Add
extern bool gDGLRender; bool gShuttingDown = false; #ifdef MAC_APPSTORE int gExitCode = 0; #endif
if(!initLibraries())
return false;Add if(!initLibraries())
return false;
#ifdef MAC_APPSTORE
if (!validateReceipt())
{
shutdownLibraries();
gShuttingDown = true;
gExitCode = 173;
return false;
}
#endifprintf("performing mainInit()n");
platState.torqueThreadId = ThreadManager::getCurrentThreadId();
platState.windowSize.set(0,0);
platState.lastTimeTick = Platform::getRealMilliseconds();
Game->mainInit(platState.argc, platState.argv);
// start with foreground time.
printf("installing main loop timern");
MacCarbInstallEventLoopTimer(sgTimeManagerProcessInterval);
printf("starting RAEL with timern");
RunApplicationEventLoop();
printf("main loop overn");
#endifAddprintf("performing mainInit()n");
platState.torqueThreadId = ThreadManager::getCurrentThreadId();
platState.windowSize.set(0,0);
platState.lastTimeTick = Platform::getRealMilliseconds();
#ifdef MAC_APPSTORE
// Aun : So it could work with the Mac App Store code
if(Game->mainInit(platState.argc, platState.argv))
{
// start with foreground time.
printf("installing main loop timern");
MacCarbInstallEventLoopTimer(sgTimeManagerProcessInterval);
printf("starting RAEL with timern");
RunApplicationEventLoop();
printf("main loop overn");
}
else
{
extern int gExitCode;
if(gExitCode == 173)
{
exit(gExitCode);
}
}
#else
Game->mainInit(platState.argc, platState.argv);
// start with foreground time.
printf("installing main loop timern");
MacCarbInstallEventLoopTimer(sgTimeManagerProcessInterval);
printf("starting RAEL with timern");
RunApplicationEventLoop();
printf("main loop overn");
#endif
#endifIn "enginesourceconsoleconsole.cc" locate "static void log(const char *string)" where
// In mode 1, we open, append, close on each log write.
if ((consoleLogMode & 0x3) == 1)
{
consoleLogFile.open(defLogFileName, FileStream::ReadWrite);
}Change it to// In mode 1, we open, append, close on each log write.
if ((consoleLogMode & 0x3) == 1)
{
// Aun : Move the console.log to the pref directory
StringTableEntry filename = Platform::getPrefsPath(defLogFileName);
if (Platform::createPath (filename))
{
consoleLogFile.open(filename, FileStream::ReadWrite);
}
//consoleLogFile.open(defLogFileName, FileStream::ReadWrite);
}else if ((newMode & 0x3) == 2) {
// Starting mode 2, must open logfile.
consoleLogFile.open(defLogFileName, FileStream::Write);
}Change it toelse if ((newMode & 0x3) == 2) {
// Aun : Move the console.log to the pref directory
StringTableEntry filename = Platform::getPrefsPath(defLogFileName);
if (Platform::createPath (filename))
{
consoleLogFile.open(filename, FileStream::Write);
}
// Starting mode 2, must open logfile.
//consoleLogFile.open(defLogFileName, FileStream::Write);
}// Output a console log setLogMode(6);Remove this line so it looks like this
// Output a console log //setLogMode(6);
_loadGameConfigurationData( expandFileName("./commonConfig.xml") );Add_loadGameConfigurationData( expandFileName("./commonConfig.xml") );
setLogMode(6);Part B : Steps to Submit your game to the Mac AppStore
- Enroll for the Mac Developer Program and setup you account and tax info if you haven't done so.
- Update your Mac OSX to 10.6.6 (or later), this is required to be able to test your receipt code.
- Download the latest XCode (excluding any beta version) at this moment the latest version is 3.2.5 which can be downloaded from here.
- Download Apple's Application tool 1.1 which you will need to be able to "Build and Archieve" your application from XCode.
- You will then need to register your Application Id with Apple. To do this, you need to follow the steps here. This id will be unique for your application and needs to be added to your plist file. There's two feilds that you need to fill in which are
- Create your developer and installer certificates to sign your bundle and installer from here. The web interface tells you pretty much what you need to do so follow those steps carefully.
- Before start going any deeper, take a look of Apple Human Interface Guidelines and see if your game applies the guidelines. If not make sure you've made the changes so your game won't get rejected.
- Add your application in your iTune connect account.
- Now you ready to start developing for the Mac AppStore. Lets open your game in Xcode and change the following so it will meet their requirements.
- Now select your game target and open up it's info again(like #3). Choose the "build" tab and change the following values.
- Now go back to XCode and select "Build and Archive" to prepare to submit your game and test the validation and installation process.
- After the "Build and Archive" is completed --> open XCode organizer --> fill in the appropriate data about your archive. Then press "Validate".
- Don't forget to test your installer, by following the steps from this link. After testing the installation, if everything goes fine now your ready to submit your game. Now "Submit" your game and let us know how it goes!

* App ID Name - The name which will be displayed as a description for you app through out iTune connect web page.
* App ID - This will be a reverse-domain name style to prevent any naming collisions between other games in the AppStore. It should look something like this "com.yourdomain.yourgame".
* Add your application in iTune connect.

* Then choose "Mac OSX App".

* Prepare 5 screenshots for your game, make sure it tells what your game is about and most importantly make them look great.
* Prepare meta data for your game which are any kind of description that would sell your game.
* Now in the manage app page of your newly application,
* - Enter your app name
* - Sku number
* - Rights and pricing
* - Choose the availability date
* - Meta Data
* - Version number
* - App description
* - Categories
* - Keywords
* - Copyright info
* - Contact email address
* - Ratings
* - Add you 5 screen shots
* Continue until your game reaches the "Ready to upload" state in iTune.
1. Open TGB Xcode project file at "[Your TGB Install directory]enginecompilersXcodeTorque2D.xcodeproj".
2. ince we are using TGB default XCode project file, I would recommend you create a new target in XCode for your game in both release and debug version. So you can have many games submitted to the Mac AppStore with only one project file. To add a new target in xcode, you'll have to select the TGB target --> right click on it --> select Duplicate.


3. After creating a new target, make sure you rename your target to something meaningful and assign any appropriate data for your game (such as Product name or Build Products Path) to each of your target. To change any of the target default value, you'll have to select on the target --> right click --> Get Info.

4. Now lets try to compile your game to make sure that the *.dso files of your game are created. You have to make sure that the *.dso files for each of your scripts are created.
5. At this point, I would assume your game is completely done and doesn't require any script(*.cs, *.gui, *.t2d) changes at all. We need to create a new folder for your game where we will delete out the *.cs, *.gui and *.t2d scripts since we don't want to include these files in our game application file. So here's what you need to do
* 5.1 Create a new folder
* 5.2 Copy your game to the newly created folder
* 5.3 Delete anything that is related to Window such as *.exe or *.dll
* 5.4 Delete all your scripts such as *.cs(except the main "main.cs"), *.gui, *.t2d and anything you don't want to include in your application.
* 5.5 Delete your game application(*.app)
* 5.6 So there will only be *.dso, your game resources and the main "main.cs" left.
Then drag the whole folder to the resources group in XCode.

Make sure you added your game resources and *.dso to both your release and debug target. Also make sure that you selected the "Create Folder References for any added folders" option.
6.1 Change "Valid Architectures" so it has only 2 valid architectures which are "x86_64"and "i386". (do this for both your release and debug version)

6.2 Change the Debug Information Format to "DWARF with dSYM File". (do this for both your release and debug version)

6.3 In the Code Signing Identity select "3rd Party Mac Developer Application : Your Name" which is the certification you've got from Apple's web interface. (do this for both your release and debug version)

6.4 In the Other Linker Flag add "-lcrypto" we will need this to decode our receipt from iTune. (do this for both your release and debug version)

6.5 In the Preprocessor of your release target add "MAC_APPSTORE".

6.6 In the Preprocessor of your debug target add "MAC_APPSTORE" and "USE_SAMPLE_RECEIPT"
6.7 Select your game plist that was created by XCode when you created the new target. Change the default value in the plist to the appropriate data that represent your game.
* - Executable file - Your product name, make sure you do not contain any space in your Product Name.
* - Get Info String - Your Copy right string.
* - Icon file - Your Mac Icon file, you can create this file by using the Icon Composer included with XCode. Remember to always include the file extension(*.icns) in the plist or else Xcode Organizer will not reconize your icon.
* - Bundle Identifier - Is your App Id you got from Apple.
Also add the follow new values to the plist as it is Apple's requirement
* - LSApplicationCategoryType - This will be the category of your game, you can take a look of what type of category there are from this list.
* - Minimum system version - This is the minimal OSX version that can play your game, it should be set to 10.6.6 since any version prior 10.6.6 can not interpret the 173 exit code.

6.8 Now add the validation code to your project by creating a new group in XCode and drag "validatereceipt.h" and "validatereceipt.m" to it. Also add the following framework to your project.
* - Foundation.framework
* - IOKit.framework
* - Security.framework
To add the framework to your project select the newly created group you just created in XCode press the mouse right click --> Add --> Existing Frameworks

6.9 To be able to compile the validate receipt code you'll need to add "Torque2D_Prefix.pch" to your project. The "Torque2D_Prefix.pch" file from this link, add it to your project in XCode by pressing the mouse right click on your target--> Add --> Existing Files

Then assign "Torque2D_Prefix.pch" to "Prefix Header" in the debug and release target info by pressing the mouse right click on your target--> GetInfo --> Build (tab) find "Prefix Header" then assign "Torque2D_Prefix.pch" to it.

6.10 Now, compile your game debug target and see how it goes! If it builds correctly, it should exit at runtime with exit code 173 since we don't have any receipt to for the game to validate. To test our game we'll have to create a sample receipt using the sample receipt Apple gave us from this link. Create the given sample receipt to your desktop.

6.11 Now run your game again your should be able to run your game using the debug target.
6.12 As for the release target, if you press "Build and Run" in XCode and have no compile errors, your game should exit with code 173. At this point, detach the XCode debugger and create a test user in iTune connect by logging on iTune Connect --> Select Manage Users --> Test User --> Add New User --> Fill in the required data for your test user. We will be using this test users to see if our receipt validation code is working properly.


6.13 Now select your release game target in the Product group --> right click and select "Revel in Finder" --> when the finder opens up, double click your game so it starts executing.

6.14 If your game starts running, it should exit with exit code 173. In which the OS will be notify to display a iTune login, put in your Test User account you've just created.

6.15 After typing in your creditial, if the game runs that means you've validated the receipt correctly! Congratulations!!



Wooo... that was a long process of getting everything linked up in this thread. Let me know if you guys have any problems with it. Good luck Guys!
You can grab the full engine source code changes from this thread Bubble Breaker Source code release
About the author
COO for Kiragames, developer of Unblock Me. With over 60 million downloads and named #17 most download Application of all time. My work is to extend our IP to higher ground and find ways we can work with other awesome companies.
Recent Threads
#22
07/11/2013 (3:25 pm)
I'm using version 1.8 of TGB Pro. And when you mean no longer valid, does that mean I won't be able to get my TGB game on the App Store?
#23
07/11/2013 (5:39 pm)
I think you should still be able to get 1.8 TGB Pro to the App Store but the changes made here are no longer valid. You'll have to make the changes on your own.
#24
iTorque was intended for distribution to Apple (iOS) devices (hence the 'i') - I suppose the App Store carries Mac programs too?
And I'm going to go out on a limb and say that hopefully most of these fixes are in T2D MIT - which is why I said what I did. It deploys quite readily - we made sure it did during our development of 3 Step Studio.
<edit>
Oooh - just saw that I said TGB isn't Mac compatible - obviously wrong... lol - sorry about that....
07/11/2013 (7:48 pm)
Is it iTorque or TGB? They're different products. TGB has an Xcode project but I'm not entirely sure it'll deploy to iOS - I may be mistaken here, I avoid Apple products to the maximum extent possible....iTorque was intended for distribution to Apple (iOS) devices (hence the 'i') - I suppose the App Store carries Mac programs too?
And I'm going to go out on a limb and say that hopefully most of these fixes are in T2D MIT - which is why I said what I did. It deploys quite readily - we made sure it did during our development of 3 Step Studio.
<edit>
Oooh - just saw that I said TGB isn't Mac compatible - obviously wrong... lol - sorry about that....
Torque Owner Richard Ranft
Roostertail Games
Technically, it's not valid anyway - TGB isn't Mac/iOS compatible; iTorque is.