Skip to content
NickyWeber edited this page Sep 29, 2014 · 22 revisions

Packages

Packages are work in progress. Please report Sprite Builder bugs and feature requests to [Sprite Builder's github page](https://github.com/Sprite Builder/Sprite Builder/). Cocos2d bugs and feature requests to Cocos2d's github home. If you need help using packages the [Sprite Builder forum](http://forum.Sprite Builder.com/) or Cocos2d forum are good places to start. We are looking forward to getting your feedback.

There will be an example app and updated Sprite Builder project templates soon.

A package is a zipped folder created with Sprite Builder. The general idea is to add content to an app during runtime which is located on a remote host. A package can be used to add any kind of file to an app. A feature is to patch existing assets.

Within an app a package is unique by it's name and resolution. OS is implicitly determined.

Central organ of the packages module is the CCPackageManager. It's supposed to be used and accessed as a singleton. Use the class method sharedManager to get hold of it.

General Flow of the Package Manager

  • I. Download a package from a remote host using http
  • II. Unzipping after download
  • III. Install: Copy contents of unzipped archive to the installation folder, usually /Library/Caches/Packages/<package-identifier> (See naming convention)
  • IV. Enable in Cocos2d: Include new packages in search path, reload all sprite sheets and filename lookups

(Click to enlarge)

Package Manager Flow

Getting Started

First of all you will need a package to be downloadable on a host. Note down the URL. Let's assume a package archive name: DLC-iOS-phonehd.zip (See below for naming conventions). The full URL is http://foobar.com/packages/DLC-iOS-phonehd.zip. OS is iOS.

1. Add the Package to your App

To make the package available in your app the only the following lines are needed:

// 1.
[CCPackageManager sharedManager].baseURL = [NSURL URLWithString:@"http://foobar.com/packages"];
// 2.
[[CCPackageManager sharedManager] downloadPackageWithName:@"DLC"
// 3.
                                      enableAfterDownload:YES]
  1. Is setting the baseURL to the host, note that the package file name is removed. This is a one time setup, especially helpful if you have several packages ready for download. This method also returns a freshly created CCPackage instance if needed.
  2. This schedules a downlod as mentioned in the general flow of the Package Manager
  3. If enableAfterDownload: is set to NO the package is installed but not enabled in cocos2d

Note: The final URL is created as the OS and resolution is known. The resolution is determined by CCFileUtils' searchResolutionsOrder firs entry which is mapped to a Sprite Builder resolution(phone, phonehd, tablet or tablethd).

2. Loading and Saving Packages

Loading is done in [self setupCocos2dWithOptions:cocos2dSetup]. So you don't have to do anything.

Note: Calling the loadPackages method more than once won't do anything. If you need a full reset, look at the Use Cases chapter.

Saving is done for you in CCAppDelegate's methods:

- (void)applicationDidEnterBackground:(UIApplication*)application

- (void)applicationWillTerminate:(UIApplication *)application

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application

Note: If you implement those methods in your own AppDelegate, don't forget to call super!

3. Progress Feedback

Processing packages is time consuming and therefore done in the background. The CCPackageManagerDelegate protocol provides several methods to let you know when a certain step is finished(or failed). For fine grained feedback on downloads and unzipping there are optional delegate methods to give a regular update on progress.

There are more code examples at the end of this article for more use cases

Naming Convention

The <package identifier> should follow the naming convention <name>-<os>-<resolution>

  • name: freely chosen name, try to avoid white spaces
  • os: iOS, Android, Mac
  • resolution: usually phone, phonehd, tablet, tablethd

A zipped package is named <name>-<os>-<resolution>.zip

Anatomy of a Package

A zipped package should contain only one folder with the naming convention of the package identifier. That folder is the package folder. That folder can contain anything you'd like to integrate in your app. Usually the content will similarly structured like the Published folder created by Sprite Builder. That means there should be a spriteFrameFileList.plist file located at the top level within the package folder if you want to include sprite sheets in your package.

A fileLookup.plist should be present if files have been converted by Sprite Builder to a different format, like a wave file converted to ogg for Android. This file is usually generated by Sprite Builder during publishing.

The configCocos2d.plist is not used at the moment.

An example for a package called DLC targeted for iOS, resolution is phonehd:

DLC-iOS-phonehd.zip
	/DLC-iOS-phonehd
		/sounds
			mow.mp4
		/resources-phonehd
			spritesheet_spaceships.png
			spritesheet_spaceships.plist
			non_spritesheet_sprite.png
		spriteFrameFileList.plist
		fileLookup.plist
		configCocos2d.plist		

Packages created with Sprite Builder meet all these requirements.

Patching content

Patching content is simply using the same asset name as in the main bundle. The ordering of Cocos2d search paths is important. Usually the package manager will make packages take precedence over the main published resources folder. See also notes below regarding taking effect of patched scenes/sprites.

Notes

  • It is not recommended to install more than one resolution of a package on a device. This can lead to unwanted search path ordering and wrong assets being loaded.

  • After installing and enabling a package patched assets won't take effect in present scenes residing in memory. You will have to reload those scenes/assets.

  • From Apple's documentation regarding the /Library/Caches folde default location for installed packages:

    • Use this directory to write any app-specific support files that your app can re-create easily. Your app is generally responsible for managing the contents of this directory and for adding and deleting files as needed.
    • In iOS 2.2 and later, the contents of this directory are not backed up by iTunes. In addition, iTunes removes files in this directory during a full restoration of the device.
    • On iOS 5.0 and later, the system may delete the Caches directory on rare occasions when the system is very low on disk space. This will never occur while an app is running. However, you should be aware that iTunes restore is not necessarily the only condition under which the Caches directory can be erased.
  • /Library/Application Support might also be a good place to install packages to, the contents of this directory are backed up by iTunes.

More Use Cases

Create packages without starting a download

If you're in need to get hold of a CCPackage instance before starting any download, you can create that with the initializers found in CCPackage.h. Add the package to the package manager by calling addPackage:. The package is now registerd in the package manager and will be persisted. You can then start the download with CCPackageManager's downloadPackage:enableAfterDownload: method.

This can be handy if like to write a full fledged UI for package management.

Disabling (Enabling) a Package

If you just want to disable a package without deleting it permanently from disk use CCPackageManager's disablePackage: method. Scenes and sprites will have to reloaded to make changes visible. Same applies to enabling, use enablePackage:

Download from a custom URL, no baseURL

If packages are in a CDN there may be no baseURL and even a filename might not be available in the URL of a package. There are two ways to get this done:

Use CCPackage's initializer

- (instancetype)initWithName:(NSString *)name
                  resolution:(NSString *)resolution
                   remoteURL:(NSURL *)remoteURL;

and provide the full URL aby baseURL set will be ignored for this package. Then add and start downloading with the package manager.

A shortcut approach is to use CCPackageManager's

- (CCPackage *)downloadPackageWithName:(NSString *)name 
                            resolution:(NSString *)resolution 
                             remoteURL:(NSURL *)remoteURL 
                   enableAfterDownload:(BOOL)enableAfterDownload;

to start downloading immediately.

Pausing and Resuming Downloads

CCPackageManager provides several methods to pause and resume single packages as well as all downloads.

Unzipping on a Custom Queue

Set CCPackageManager's property unzippingQueue. Default is the global low priority queue.

Passwords and Zip Archives

Implement the optional delegate method passwordForPackageZipFile: to provide a password for a certain package. See also FAQ regarding zip and passwords.

Removing all Packages

Get a hold on all packages, iterate and call CCPackageManager's deletePackage: with every pacakge like this:

// 1.
for (CCPackage *aPackage in [[CCPackageManager sharedManager].allPackages copy])
{
    NSError *error;
    if (![[CCPackageManager sharedManager] deletePackage:aPackage error:&error])
    {
    	// 2.
        NSLog(@"Something went wrong: %@", error);
    }
}
  1. Use a copy of the array since it will be mutated while deleting packages.
  2. Pass an error object by reference to get more details about a failed delete operation

FAQ

Q: Why is a download's progress total bytes 0?

A: A server has to provide the Content-Length|Content-Range headers to let the download know how big it actually will be. However this does not prevent the download from downloading more if more data is sent. If the response does provide a lenght, then total bytes is 0. Current downloaded bytes is not affected by this.

Q: Why do Downloads Start Over instead of Resuming

A: A server has to support range requests. If that is not the case a paused download can only be restarted since the server will always send the full package.

Q: Why did my Password Protected Package get installed although I provided no password?

A: This is tricky: The SSZipArchive module and the underlying unzip.c do not test if the archive is password protected. The table of contents of a zip file can ALWAYS be read so SSZipArchive will actually create folders and files with 0 bytes or some meaninigless bytes. There is no workaround except putting alot of work into the unzip module, which we can't at the moment. Test properly. You can always add a textfile to your package and read the contents. If the contents of that file compares to something you hardcode unzipping was successful.

Q: I added alot of Packages and my App is becoming slow.

A: CCFileUtils is the bottlenet. Although it caches filelookups it will still search recursively in all search paths(Packages and Main Resources) for a non cached asset. At the moment there is only advice: Is the package containing additional content, then enable it only when needed. CCFileUtils may get some love in the future to become more performant.

Clone this wiki locally