Game Development Community

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.

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 are

1. 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 90
    And change it to
    #ifdef TORQUE_TOOL
       #define OSX_MENUBAR_OFFSET 90
    #else
       #define OSX_MENUBAR_OFFSET 0
    #endif

  • 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
    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 to
    bool 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"
                   );
  • 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
  • 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"
    #endif
    Still in "enginesourcegamemain.cc" where
    extern bool gDGLRender;
    bool gShuttingDown   = false;
    Add
    extern bool gDGLRender;
    bool gShuttingDown   = false;
    
    #ifdef MAC_APPSTORE
    	int gExitCode = 0;
    #endif
  • Still in "enginesourcegamemain.cc" locate "DemoGame::mainInit(int argc, const char **argv)" where
  • if(!initLibraries())
          return false;
    Add
    if(!initLibraries())
          return false;
    
    #ifdef MAC_APPSTORE
       if (!validateReceipt())
       {
          shutdownLibraries();
          gShuttingDown = true;
          gExitCode = 173;
          return false;
       }
    #endif
  • 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
    printf("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");
    #endif
    Add
    printf("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
    
    #endif
  • 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
    // 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);
       }

  • Still in "enginesourceconsoleconsole.cc" locate "void setLogMode(S32 newMode)" where
  • else if ((newMode & 0x3) == 2) {
             // Starting mode 2, must open logfile.
             consoleLogFile.open(defLogFileName, FileStream::Write);
          }
    Change it to
    else 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);
          }
  • 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
  • // Output a console log
    setLogMode(6);
    Remove this line so it looks like this
    // Output a console log
    //setLogMode(6);
  • We will enable the console.log as soon as we know the application home directory. In "commonmain.cs" locate where
  • _loadGameConfigurationData( expandFileName("./commonConfig.xml") );
    Add
    _loadGameConfigurationData( expandFileName("./commonConfig.xml") );
    setLogMode(6);

Part B : Steps to Submit your game to the Mac AppStore


  1. Enroll for the Mac Developer Program and setup you account and tax info if you haven't done so.

  2. Update your Mac OSX to 10.6.6 (or later), this is required to be able to test your receipt code.

  3. images.apple.com/mac/app-store/images/update_mac_store20110106.jpg
  4. Download the latest XCode (excluding any beta version) at this moment the latest version is 3.2.5 which can be downloaded from here.

  5. Download Apple's Application tool 1.1 which you will need to be able to "Build and Archieve" your application from XCode.

  6. 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

  7. * 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".

  8. 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.

  9. 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.

  10. Add your application in your iTune connect account.

  11. * Add your application in iTune connect.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/itune/1.png
    * Then choose "Mac OSX App".

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/itune/2.png
    * 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.

  12. 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.

  13. 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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/1.png
    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/2.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/7.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/3.png
    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.

  14. Now select your game target and open up it's info again(like #3). Choose the "build" tab and change the following values.

  15. 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)

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/4.png
    6.2 Change the Debug Information Format to "DWARF with dSYM File". (do this for both your release and debug version)

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/5.png
    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)

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/6.png
    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)

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/8.png
    6.5 In the Preprocessor of your release target add "MAC_APPSTORE".

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/10.png
    6.6 In the Preprocessor of your debug target add "MAC_APPSTORE" and "USE_SAMPLE_RECEIPT"

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/9.png

    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/11.png
    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

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/14.png
    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

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/22.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/xcode/23-th.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/16.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/itune/4.png
    www.mayansoftware.com/t2dcompletegamekit/document/content/image/itune/3.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/19.png
    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.

    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/17.png
    6.15 After typing in your creditial, if the game runs that means you've validated the receipt correctly! Congratulations!!

  16. Now go back to XCode and select "Build and Archive" to prepare to submit your game and test the validation and installation process.

  17. www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/21.png
  18. After the "Build and Archive" is completed --> open XCode organizer --> fill in the appropriate data about your archive. Then press "Validate".

  19. www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/15.png
    www.mayansoftware.com/t2dcompletegamekit/document/content/image/xcode/13.png
  20. 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!


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.

Page «Previous 1 2
#1
02/04/2011 (7:18 am)
Awesome checklist. The bugfixes are important even if you're not releasing on MAS.
#2
02/04/2011 (9:52 am)
Amazing Aun!
#3
02/04/2011 (10:17 am)
Wow, great work writing all this up! GarageGames.com needs a "like" button, or a rating system, or something along those lines. :-)
#4
02/05/2011 (12:52 am)
Thanks guys! Hopefully, it should help people getting their TGB games in the MAS a lot faster.
#5
02/05/2011 (7:27 am)
This is truly deserving to be made "sticky," so it doesn't get buried under a growing pile of newer posts.
#6
02/10/2011 (2:53 am)
I'll second that this should be stickied!
#7
02/21/2011 (9:20 pm)
I made the changes and compiled (Mac) without any problems, only problem is my game now starts in 320x320x32.


The black console window is gone so that worked.

is there anything I can do?

Thanks
#8
02/21/2011 (11:34 pm)
Try opening up "common/preferences/defaultPrefs.cs" and change

$pref::Video::resolution = "800 600 32";
$pref::Video::windowedRes = "800 600 32";

to
$pref::Video::resolution = "[Your default Game Window Size]";
$pref::Video::windowedRes = "[Your default Game Window Size]";

And in "commongameScriptsproperties.cs" make sure that

$Game::Resolution       =  "[Your default Game Window Size]";

#9
02/22/2011 (4:11 am)
Thank you for taking the time to compile this checklist and fix list to get onto the App Store, very much appreciated!

#10
02/23/2011 (10:48 am)
Hi Aun, Thanks for the info, I made the changes but it did not work. The startup window is still at 320x320.

I have a Mac mini with the GMA 950 chip.

#11
02/23/2011 (7:14 pm)
@George - I'm using a Mac Mini too :D Have you tried deleting the xml file at "common/commonConfig.xml" and any other "prefs.cs" files you might be using ?
#12
03/15/2011 (2:32 pm)
Everything is working fine now, thank you for all your help :)


#13
06/25/2011 (2:50 pm)
You are da man!
#14
07/06/2011 (3:53 pm)
Awesome thanks
#15
02/13/2012 (6:20 pm)
Do the changes listed here take care of apple "sandboxing" concerns, as talked about in this article?

Inside Sandboxing: how Apple plans to make the Mac App Store as secure as iOS
#16
02/13/2012 (6:41 pm)
Sandbox was a requirement since the Mac AppStore went live, if I didn't miss anything. So if they didn't add any new requirements this should apply to the sandbox rule.

Let me know how it goes for you.
#17
02/14/2012 (1:21 pm)
OK Thanks Aun for the quick reply !
#18
07/20/2012 (6:50 am)
I think I've found the cause for the black "console" window.

IsValidWindowPtr() is used within MacCarbFadeAndReleaseWindow() function to early-out if a 'window' parameter passed in is not an actual window reference. It looks like, since window creation is still in progress when the game switches into full screen, this function returns false and fade-and-release never happens.

It is a good idea to validate parameters, but IsValidWindowPtr() is "legacy", "cpu intensive" and "for debugging purposes" all at once, so I have it removed and now kind of looking for alternative way to check if window reference is valid.
#19
10/20/2012 (7:39 pm)
@Rpahut cool, I just had sometime back to update my game. Will look if there's any alternative as you suggested. Thanks!
#20
05/22/2013 (9:23 am)
I'm having a bit of a problem. I can't seem to find the "enginesourcegamemain.cc" or any of the other files your telling me to edit. I feel like I've looked everywhere that it should be I can't find them. Can you help me out?
Page «Previous 1 2