patternMinor
Downloading and Loading Remote Game Assets
Viewed 0 times
downloadinggameloadingassetsandremote
Problem
On advice from an answer to my previous question, I have written some code that will allow my game balance properties to be loaded from a remote server. Right now I am just hosting the raw property list file on a web server for simplicities sake.
My first question is whether there are any long term problems associated with hosting the file that way. Users will access the remote file whenever a GameBalance object is created, but it is only downloaded if the version is higher than the one saved to the local documents folder.
My second question is whether I provided enough protection in the event of a failed connection, or in the event that only part of the property list is downloaded due to some internet related error. Do packet loss and internet protocols come into play here, and can they mess this up?
My final question is that while I was able to do almost all of the work happening inside the initialization in plain C functions, I do have one call to
Here is the code to download the property list:
```
#pragma mark - Download Plist
int savedVersionNumber() {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"ServerGameBalance.plist"];
NSDictionary *settingsList = [[NSDictionary alloc] initWithContentsOfFile:filePath];
return [settingsList[@"VersionId"]intValue];
}
int remoteVersionNumber() {
NSString *stringURL = @"http://192.168.1.1:80/DownloadedBalance.plist";
NSURL *url = [NSURL URLWithString:stringURL];
NSDictionary *settingsList = [NSDictionary dictionaryWithContentsOfURL:url];
return [settingsList[@"VersionId"]intValue];
}
void downloadPlistFromServerAndSave() {
NSString
My first question is whether there are any long term problems associated with hosting the file that way. Users will access the remote file whenever a GameBalance object is created, but it is only downloaded if the version is higher than the one saved to the local documents folder.
My second question is whether I provided enough protection in the event of a failed connection, or in the event that only part of the property list is downloaded due to some internet related error. Do packet loss and internet protocols come into play here, and can they mess this up?
My final question is that while I was able to do almost all of the work happening inside the initialization in plain C functions, I do have one call to
[self loadSettingsFromList] in there. Is this acceptable or is it bad practice? The only thing this method is doing is assigning values to instance variables.Here is the code to download the property list:
```
#pragma mark - Download Plist
int savedVersionNumber() {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"ServerGameBalance.plist"];
NSDictionary *settingsList = [[NSDictionary alloc] initWithContentsOfFile:filePath];
return [settingsList[@"VersionId"]intValue];
}
int remoteVersionNumber() {
NSString *stringURL = @"http://192.168.1.1:80/DownloadedBalance.plist";
NSURL *url = [NSURL URLWithString:stringURL];
NSDictionary *settingsList = [NSDictionary dictionaryWithContentsOfURL:url];
return [settingsList[@"VersionId"]intValue];
}
void downloadPlistFromServerAndSave() {
NSString
Solution
There's two important big picture things to grasp here.
First, rather than downloading the entire plist just to check the version number, we'd be well served by having a separate location to check the version number. All we need to do is download a very small package and compare the version. Then we only need to waste bandwidth for both the user and our server if there version is actually out of date.
Second, accessing remote files takes time. Accessing remote files also can have errors. We could completely fail to fetch the file. And, given that these files are purely game balance tweaks, I'd argue that it's not necessary to make the user wait while the file downloads. We need to move this operation to the background.
The class that is in charge of checking for and fetching updates should also be completely different from the class that loads are already downloaded settings from a file.
Basically, we have a given file path where we store our game balance settings. On the apps first launch, we copy the plist into this file path. Now when the user starts a new game, we load the settings from this file path every single time. Meanwhile, we're asynchronously looking for updates. If we find that there are updates, we download the updated plist and write it to the predetermined file path where we know our other class fetches the settings from.
So, step one is to write some logic that moves the pre-included plist to a file path where you can overwrite it.
Step two, move all the logic of checking online for updates out of the class that loads the settings from this file path.
Step three, create a new class for checking for online updates.
This is a big step. We need to decide how frequently the app should check, and make sure we schedule the class to check with that frequency. If I play your app for 6 hours today, does my device need to check every 10 minutes? Unlikely. It's unlikely that you'll update more than once a day, right? And it's also unlikely that your game balance tweaks will be anything that needs an immediate tweak, right? Heck, you could even include in the game balance settings a value to determine when the next check for updates should be.
But when we create this class, we actually shouldn't do a whole lot in the
The class should also have a readonly
Now, we just provide a method, something like:
Additionally, we will want to either write the class so it has a
And rather than using
First, rather than downloading the entire plist just to check the version number, we'd be well served by having a separate location to check the version number. All we need to do is download a very small package and compare the version. Then we only need to waste bandwidth for both the user and our server if there version is actually out of date.
Second, accessing remote files takes time. Accessing remote files also can have errors. We could completely fail to fetch the file. And, given that these files are purely game balance tweaks, I'd argue that it's not necessary to make the user wait while the file downloads. We need to move this operation to the background.
The class that is in charge of checking for and fetching updates should also be completely different from the class that loads are already downloaded settings from a file.
Basically, we have a given file path where we store our game balance settings. On the apps first launch, we copy the plist into this file path. Now when the user starts a new game, we load the settings from this file path every single time. Meanwhile, we're asynchronously looking for updates. If we find that there are updates, we download the updated plist and write it to the predetermined file path where we know our other class fetches the settings from.
So, step one is to write some logic that moves the pre-included plist to a file path where you can overwrite it.
Step two, move all the logic of checking online for updates out of the class that loads the settings from this file path.
Step three, create a new class for checking for online updates.
This is a big step. We need to decide how frequently the app should check, and make sure we schedule the class to check with that frequency. If I play your app for 6 hours today, does my device need to check every 10 minutes? Unlikely. It's unlikely that you'll update more than once a day, right? And it's also unlikely that your game balance tweaks will be anything that needs an immediate tweak, right? Heck, you could even include in the game balance settings a value to determine when the next check for updates should be.
But when we create this class, we actually shouldn't do a whole lot in the
init method. The init method should really only do one thing: initialize an instance variable with the current version of the game.The class should also have a readonly
NSDate property to store the timestamp of its last successful check of the online file.Now, we just provide a method, something like:
fetchUpdates which first checks URL path A to compare the current version to the most up to date online version, and if it looks like the online version is more recent, then the method then initiates a connection to URL path B where it actually downloads the update.Additionally, we will want to either write the class so it has a
delegate to inform the update is complete or our fetchUpdates method should take a completion block argument that will fire once the updates our complete.And rather than using
NSData's dataWithContentsOfURL: method, we need to use a NSURLConnection object and make our class an NSURLConnection delegate so we can perform all the network operations asynchronously.Context
StackExchange Code Review Q#60186, answer score: 3
Revisions (0)
No revisions yet.