Tuesday, November 12, 2019

Yaga!

Yaga is a roguelike adventure though Russian folklore, made by the fine folks over at Breadcrumbs and Versus Evil.  We're proud to have worked with them to bring the title to all three consoles, plus Apple Arcade.  It's a fun action-adventure with a ton of personality that fans of top-down action should check out.



Check it out on Switch, Xbox, PS4, or on Epic Store!  If you have Apple Arcade, it's available there as well!

Wednesday, October 9, 2019

Unity Games Using CloudKit on MacOS (Part 2)

In the previous post, we discussed signing a development Unity build so it can access iCloud through CloudKit.  This time we're going to talk about signing for uploading to the App Store.  Note that this is current as of this posting, if you run into any major problems let us know.  You can find an address via the Work for Hire/Contacts page.

As before, a huge thank you to all the folks involved in putting together the Unity Apple Distribution Workflow.  It's been an invaluable resource.

You will need to have your project and App ID set up for CloudKit as described in the previous post.

Certificates

This time, you will need to create a distribution certificate for signing.  Specifically, you need a Mac App Distribution certificate.  This will need to be created at developer.apple.com.

You should also create an installer certificate.  Specifically, you need a Mac Installer Distribution certificate.  This is separate and does not involve a provisioning profile.  It is only used for creating an installer package after you've created and codesigned your build.

Provisioning Profile

As in part 1, you'll need to create a provisioning profile with this new distribution certificate and the correct App ID.  No test devices are needed.  Unlike development certificates, this provisioning profile can only be tied to one signing certificate, so make sure that certificate is installed on any machine you need to make a build from.

Entitlements

Entitlements are almost identical to the development version.  The only difference is that you need to point CloudKit at the production database.  When the key is not specified, it defaults to the development database.  The key is "com.apple.developer.icloud-container-environment" and the value is "Production" (make sure it is capitalized).  Once again, here is a sample entitlements file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.icloud-container-environment</key>

    <string>Production</string>    
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.files.user-selected.read-only</key>
    <true/>
    <key>com.apple.developer.aps-environment</key>
    <string>development</string>
    <key>com.apple.developer.icloud-container-identifiers</key>
    <array>
        <string>your container identifier</string>
    </array>
    <key>com.apple.developer.icloud-services</key>
    <array>
        <string>CloudKit</string>
    </array>
    <key>com.apple.application-identifier</key>
    <string>your team identifier.your game app ID</string>
    <key>com.apple.developer.team-identifier</key>
    <string>your team identifier</string>
</dict>
</plist>

Unity Build

Make sure to check Mac App Store validation in your Unity PlayerSettings before starting the build.  Verify your bundle identifier matches your App ID from your provisioning profile.

Make your Unity build with these settings.  The build process will generate an application bundle (<your game name>.app) which is really a folder that contains all the important files.

As before, you'll need to do the codesigning work on the Mac.
  1. Remove all the obviously non-App Store plugins that may have been included from other Unity Standalone versions of your game.  Apple's upload process detects some of these and will reject your build.  All plugins are located in <your game name>.app/Contents/Plugins/.  Ones we've found that cause issues include:
    1. Unity's Burst plugin - lib_burst_generated.bundle (a binary file included by default in Unity 2019 when you're using the Jobs system) (also lib_burst_generated.txt)
    2. Various Steam plugins (also delete x86 and x86_64 folders than contain Linux shared libraries that sometimes get included accidentally, and sometimes there's a libsteam_api.dylib file included)
    3. The GOG Galaxy plugin
  2. Delete all the .meta files in your plugins.  The Unity Apple Distribution Workflow details this out.  Codesigning will fail if you don't do this, because there are files in unexpected places (from Apple's perspective).
  3. Modify your Info.plist file (<your game name>.app/Contents/Info.plist) per the Unity Apple Distribution Workflow.  You need to add the key "ITSAppUsesNonExemptEncryption" set to false.
  4. Unlike in the development signing process, for App Store uploads you do need to follow the Unity Apple Distribution Workflow's recommendation and change the bundle identifier for all the plugins.  For each *.bundle folder in Contents/Plugins/, find the Info.plist file (e.g. <your game name>.app/Contents/Plugins/<some plugin>.bundle/Contents/Info.plist).  Change the value of CFBundleIdentifier to the game's bundle identifier (as specified in Unity's PlayerSettings).
    1. We occasionally got errors writing to the plugin Info.plist file, so we also make sure to modify its permissions ahead of time so it is definitely writable: chmod 777 "<your game name>.app/Contents/Plugins/<some plugin>.bundle/Contents/Info.plist"
  5. Copy over the correct provisioning profile to <your game name>.app/Contents/embedded.provisionprofile.
  6. Modify the Unity executable to link the CloudKit framework.
    1. You need to use the third party tool optool.
    2. Run the command optool install -c load -p "/System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit" -t "<your game name>.app/Contents/MacOS/<your game name>"
    3. This will modify the Unity binary to load the CloudKit framework at startup.  We found that without this - even though the CloudKit framework is linked in the Prime31 plugin - actual calls to CloudKit will fail with the error "connection to service names com.apple.cloudd was invalidated".
  7. Change file permissions for your game bundle: chmod -R a+xr "<your game name>.app"
  8. Codesign your build.  Follow the steps laid out in the Unity Apple Distribution Workflow.
    1. Sign all the .dylib libraries in <your game name>.app/Contents/Frameworks manually.  Do not include entitlements in the signing command.
      1. codesign --force --verify --sign "<your distribution signing id>" --preserve-metadata=identifier,entitlements,flags "<your game name>.app/Contents/Frameworks/<library name>.dylib
      2. Do this for all the libraries in Frameworks, both in the root folder and in sub-folders
    2. Some versions of Unity (particularly Unity 2019) will include additional .dylib libraries in the MacOS executable folder.  These also need to be manually signed like the ones in the Frameworks folder.  (Don't sign the actually binary executable, that comes later.)
      1. codesign --force --verify --sign "<your distribution signing id>" --preserve-metadata=identifier,entitlements,flags "<your game name>.app/Contents/MacOS/<library name>.dylib"
    3. Sign all the plugins in your game.  Do not include entitlements in the signing command.
      1. codesign --force --verify --sign "<your distribution signing id>" --preserve-metadata=identifier,entitlements,flags "<your game name>.app/Contents/Plugins/<plugin name>.bundle"
    4. Sign the final game application bundle following the Unity Apple Distribution Workflow.  We need to include the entitlements file listed earlier and this time sign with the "--deep" option, even though we did sign things individually.
      1. codesign --force --deep --verify --sign "<your distribution signing id>" --entitlements "<path to your entitlements file>" "<your game name>.app"
You can also generate a package installer.  You'll need to do this on a machine with the installer certificate that you created earlier.  Once the build is created and signed, run the following:

productbuild --component "<your game name>.app" "/Applications" --sign "<your installer signing id>" "<your game name>.pkg"

At this point, your game should be ready to upload to the App Store.  Because of the signing, you cannot run it to actually test things.

Also be aware of Apple's new notarization process, though we're not covering it here.

Tuesday, August 27, 2019

Controller Usage in a Signed MacOS Game

"Why doesn't my controller work with my Mac game," you might ask.  You've been working on a Mac App Store project, following the very helpful Unity Apple Distribution Workflow.  It works in the Unity editor, but not in the exported build.  You tried both a wired controller and a bluetooth one, and neither of them gets detected.  You added prints that'll show you absolutely any input and nothing's happening at all.  So what's the deal?

As with many things Apple, this issue is an entitlement, and you can find some documentation of all possible MacOS entitlements here.

Specifically, once your app is signed it's trapped in a sandbox environment, and the only light filters through gaps provided by those entitlements.  In the case of a gamepad, we're talking either USB (wired) or Bluetooth (wireless).  This means you'll want to add this to your MacOS app's entitlement file:

<key>com.apple.security.device.bluetooth</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>

Do that, rebuild, re-sign, and you should have working controllers in your build.

Saturday, August 10, 2019

Anode on the Nintendo Switch!

We've had this on the back burner for a while, but as of today the listing is finally up!  This is the same price as the Steam title, with the same fun arcade puzzle action.  Full joy-con support is in place for quick and easy multiplayer at any time.


Check it out here!  If you're a member of the press looking for clean copies of screenshots and artwork, you can grab a press kit here!

Wednesday, July 31, 2019

Mutazione!

We've been working with the fine folks over at Akupara and Die Gute Fabrik to bring Mutazione to more platforms, and the gate's been officially opened today!  Mutazione is a story-filled adventure with lovely hand-drawn backgrounds and musical gardens.


Check it out on the web, or browse Steam and place a pre-order!

Friday, June 14, 2019

Unity Games Using CloudKit on MacOS (Part 1)

We are in the process of developing several games for various Apple products, and we're using CloudKit to keep game saves in sync across the various platforms (iOS, tvOS, macOS).  There's a lot of good documentation out there for iOS and tvOS, and Unity provides good tools for exporting the projects to Xcode and letting you build things in Apple's environment with all of their tools readily available.

This is not the case with macOS.

Unity still uses a precompiled binary executable (even with IL2CPP builds) and does not generate an Xcode project that will handle all of your codesigning and entitlement needs.  All of that has to happen manually.  (There do seem to be plans to change this.)  Our task is doubly complicated because the game needs to be released not only on the Mac App Store, but also as a standalone product and still have access to CloudKit features.

Part 1 of this process will detail getting signing identities, App IDs, provisioning profiles, etc. set up, as well as the process of getting your game built and signed correctly to run in a development environment.  Part 2 will (eventually) detail changes you need to make to make the game viable to upload to the App Store and distribute generally.  (Edit: Link to part 2)

First off, a huge debt of gratitude to the folks who put together the Unity Apple Distribution Workflow document.  This guide is thorough and well-referenced.  Things with Apple keep changing, so the blog post you're reading now is accurate for the summer of 2019, but may become less so as Apple changes things in the future.

Also thank you to eppz! Appcrafting for their amazing work digging into the bits and pieces of Unity so they could point us at the tools we needed to finally solve CloudKit access.

Certificates

We are working on a development version for the moment, so you only need a development certificate.  You do specifically need a Mac Development certificate.  You can either create one yourself on developer.apple.com or have Xcode do it for you.

App ID

Our goal is to have iCloud support (through CloudKit) across all platforms: iOS, tvOS, and macOS.  You can only create App IDs that target either iOS/tvOS or macOS.  With iOS/tvOS being the flagship platforms, it probably makes more sense to create your primary App ID for them and have a secondary one for macOS.  But the secondary one will need to have a different name.

Both App IDs will need to have the iCloud/CloudKit capability set.  Again, you can do all the primary work on the iOS/tvOS App ID.  If you're also doing your Unity game on those platforms, make a build there, have it generate the Xcode project, and use Xcode to turn on the iCloud capability and set up CloudKit.  It'll handle creating your default container (which will be named for the iOS/tvOS App ID).

For the macOS App ID, you enable iCloud/CloudKit, but then for the container, point it at the existing container you made for iOS/tvOS.  This way you can share data across platforms.

Register Test Devices

You still need to register your Mac as a test device through Apple.  Xcode can do this for you if you make a temporary project and set your development team.  If you want to do it manually, your Mac's UDID is in Apple -> About This Mac -> System Report -> Hardware (first item selected) -> Hardware UDID.

Provisioning Profiles

You will need to make provisioning profiles that combine your signing certificate, App ID, and test devices.  For development, if you're using the Xcode-created signing certificate, you will need to make a temporary Xcode project, make a build, and then extract the provisioning profile from the build.  If you're using certificates you created on developer.apple.com, you can similarly create provisioning profiles there and download them.

Since we're doing a development build, create your provisioning profile with your development certificate, then make sure to set your Mac as one of the allowed devices before generating and downloading it.  Also make sure you've taken care of all the CloudKit setup before you generate the provisioning profile.

Entitlements

You need to create an entitlements file for use when signing your game.  The easiest way to do this is to create a temporary Xcode project, give it your credentials, and enable iCloud/CloudKit on it.  Make sure the iCloud container identifier is pointed at the correct one as described in the App ID setup previously.  Here's a sample:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.files.user-selected.read-only</key>
    <true/>
    <key>com.apple.developer.aps-environment</key>
    <string>development</string>
    <key>com.apple.developer.icloud-container-identifiers</key>
    <array>
        <string>your container identifier</string>
    </array>
    <key>com.apple.developer.icloud-services</key>
    <array>
        <string>CloudKit</string>
    </array>
    <key>com.apple.application-identifier</key>
    <string>your team identifier.your game app ID</string>
    <key>com.apple.developer.team-identifier</key>
    <string>your team identifier</string>
</dict>
</plist>

iCloud

Sign into iCloud on your test Mac.  Make sure you have iCloud Drive enabled, or CloudKit will not work.

Unity Build

Make your Unity build as normal.  This post won't get into integrating CloudKit into your game (we've used Prime31's iCloud Plugin); we'll assume that is taken care of.

You will need to do this work on a Mac.  You can make Unity builds on Windows, but you will not be able to perform the codesigning steps there.

The build process will generate an application bundle (<your game name>.app) which is really a folder that contains all the important files.


  1. Delete all the .meta files in your plugins.  The Unity Apple Distribution Workflow details this out.  Codesigning will fail if you don't do this, because there are files in unexpected places (from Apple's perspective).  Plugins are located in <your game name>.app/Contents/Plugins.
  2. Modify your Info.plist file (<your game name>.app/Contents/Info.plist) per the Unity Apple Distribution Workflow.  For the purposes of making a development build, you just need to add the key "ITSAppUsesNonExemptEncryption" set to false.  We found that for us (unlike the Unity Apple Distribution Workflow recommendation), we did not need to modify each plugin's Info.plist.
  3. Copy over the correct provisioning profile to <your game name>app/Contents/embedded.provisionprofile.
  4. Modify the Unity executable to link the CloudKit framework.
    1. Following from the eppz! blog, you need to use the third party tool optool.
    2. Run the command optool install -c load -p "/System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit" -t "<your game name>.app/Contents/MacOS/<your game name>"
    3. This will modify the Unity binary to load the CloudKit framework at startup.  We found that without this - even though the CloudKit framework is linked in the Prime31 plugin - actual calls to CloudKit will fail with the error "connection to service names com.apple.cloudd was invalidated".
  5. Change file permissions for your game bundle: chmod -R a+xr "<your game name>.app"
  6. Codesign your build.  Follow the steps laid out in the Unity Apple Distribution Workflow, but with some modifications that have worked for us for making a development build.
    1. Sign all the .dylib libraries in <your game name>.app/Contents/Frameworks manually.  Do not include entitlements in the signing command.
      1. codesign --force --verify --sign "<your development signing id>" --preserve-metadata=identifier,entitlements,flags "<your game name>.app/Contents/Frameworks/<library name>.dylib
      2. Do this for all the libraries in Frameworks, both in the root folder and in sub-folders
    2. Sign all the plugins in your game.  Do not include entitlements in the signing command.
      1. codesign --force --verify --sign "<your development signing id>" --preserve-metadata=identifier,entitlements,flags "<your game name>.app/Contents/Plugins/<plugin name>.bundle"
    3. Sign the final game application bundle.  Despite what the Unity Apple Distribution Workflow says, we've discovered that you have to include entitlements even in the development signing.  Also, because we signed everything individually, do not include the "--deep" flag, because it will mess with the existing codesigns we've done.
      1. codesign --force --verify --sign "<your development signing id>" --entitlements "<path to your entitlements files>" "<your game name>.app"
At this point your game should be signed and ready to test in development mode on the Mac you registered.

Note that the game will run in sandbox mode.  This means all of its files will be written to ~/Library/Containers/<your app ID>/Data/Library/.  Where the Unity documentation says the log file writes to ~/Library/Logs/Unity/Player.log, the sandboxed version is in ~/Library/Containers/<your app ID>/Data/Library/Logs/Unity/Player.log.  Also of note, even though the game is sandboxed, it will use the iCloud credentials that the current machine is using.