Quellcode durchsuchen

Updated manual about Live Update

JCash vor 1 Jahr
Ursprung
Commit
ce43440939

BIN
docs/en/manuals/images/live-update/05-liveupdate-settings-zip.png


+ 122 - 0
docs/en/manuals/live-update-aws.md

@@ -0,0 +1,122 @@
+# Setting up Amazon Web Service
+
+To use the Defold Live update feature together with Amazon services you need an Amazon Web Services account. If you don't already have an account you can create one here https://aws.amazon.com/.
+
+This section will explain how to create a new user with limited access on Amazon Web Services that can be used together with the Defold editor to automatically upload Live update resources when you bundle your game, as well as how to configure Amazon S3 to allow game clients to retrieve resources. For additional information about how you can configure Amazon S3, please see the [Amazon S3 documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html).
+
+1. Create a bucket for Live update resources
+
+    Open up the `Services` menu and select `S3` which is located under the _Storage_ category ([Amazon S3 Console](https://console.aws.amazon.com/s3)). You will see all your existing buckets together with the option to create a new bucket. Though it is possible to use an existing bucket, we recommend that you create a new bucket for Live update resources so that you can easily restrict access.
+
+    ![Create a bucket](images/live-update/01-create-bucket.png)
+
+2. Add a bucket policy to your bucket
+
+    Select the bucket you wish to use, open the *Properties* panel and expand the *Permissions* option within the panel. Open up the bucket policy by clicking on the *Add bucket policy* button. The bucket policy in this example will allow an anonymous user to retrieve files from the bucket, which will allow a game client to download the Live update resources that are required by the game. For additional information about bucket policies, please see [the Amazon documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-iam-policies.html).
+
+    ```json
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "AddPerm",
+                "Effect": "Allow",
+                "Principal": "*",
+                "Action": "s3:GetObject",
+                "Resource": "arn:aws:s3:::defold-liveupdate-example/*"
+            }
+        ]
+    }
+    ```
+
+    ![Bucket policy](images/live-update/02-bucket-policy.png)
+
+3. Add a CORS configuration to your bucket (Optional)
+
+    [Cross-Origin Resource Sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) is a mechanism that allows a website to retrieve a resource from a different domain using JavaScript. If you intend to publish your game as an HTML5 client, you will need to add a CORS configuration to your bucket.
+
+    Select the bucket you wish to use, open the *Properties* panel and expand the *Permissions* option within the panel. Open up the bucket policy by clicking on the *Add CORS Configuration* button. The configuration in this example will allow access from any website by specifying a wildcard domain, though it is possible to restrict this access further if you know on which domains you will make you game available. For additional information about Amazon CORS configuration, please see [the Amazon documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html).
+
+    ```xml
+    <?xml version="1.0" encoding="UTF-8"?>
+    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+        <CORSRule>
+            <AllowedOrigin>*</AllowedOrigin>
+            <AllowedMethod>GET</AllowedMethod>
+        </CORSRule>
+    </CORSConfiguration>
+    ```
+
+    ![CORS configuration](images/live-update/03-cors-configuration.png)
+
+4. Create IAM policy
+
+    Open up the *Services* menu and select *IAM* which is located under the _Security, Identity & Compliance_ category ([Amazon IAM Console](https://console.aws.amazon.com/iam)). Select *Policies* in the menu to the left and you will see all your existing policies together with the option to create a new policy.
+
+    Click the button *Create Policy*, and then choose to _Create Your Own Policy_. The policy in this example will allow a user to list all buckets, which is only required when configuring a Defold project for Live update. It will also allow the user to get the Access Control List (ACL) and upload resources to the specific bucket used for Live update resources. For additional information about Amazon Identity and Access Management (IAM), please see [the Amazon documentation](http://docs.aws.amazon.com/IAM/latest/UserGuide/access.html).
+
+    ```json
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "s3:ListAllMyBuckets"
+                ],
+                "Resource": "arn:aws:s3:::*"
+            },
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "s3:GetBucketAcl"
+                ],
+                "Resource": "arn:aws:s3:::defold-liveupdate-example"
+            },
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "s3:PutObject"
+                ],
+                "Resource": "arn:aws:s3:::defold-liveupdate-example/*"
+            }
+        ]
+    }
+    ```
+
+    ![IAM policy](images/live-update/04-create-policy.png)
+
+5. Create a user for programmatic access
+
+    Open up the *Services* menu and select *IAM* which is located under the _Security, Identity & Compliance_ category ([Amazon IAM Console](https://console.aws.amazon.com/iam)). Select *Users* in the menu to the left and you will see all your existing users together with the option to add a new user. Though it is possible to use an existing user, we recommend that you add a new user for Live update resources so that you can easily restrict access.
+
+    Click the button *Add User*, provide a username and choose *Programmatic access* as *Access type*, then press *Next: Permissions*. Select *Attach existing policies directly* and choose the policy you created in step 4.
+
+    When you've completed the process you will be provided with an *Access key ID* and a *Secret access key*.
+
+    ::: important
+    It is *very important* that you store those keys since you will not be able to retrieve them from Amazon after you leave the page.
+    :::
+
+6. Create a credentials profile file
+
+    At this point you should have created a bucket, configured a bucket policy, added a CORS configuration, created a user policy and created a new user. The only thing that remains is to create a [credentials profile file](https://aws.amazon.com/blogs/security/a-new-and-standardized-way-to-manage-credentials-in-the-aws-sdks) so that the Defold editor can access the bucket on your behalf.
+
+    Create a new directory *.aws* in your home folder, and create a file called *credentials* within the new directory.
+
+    ```bash
+    $ mkdir ~/.aws
+    $ touch ~/.aws/credentials
+    ```
+
+    The file *~/.aws/credentials* will contain your credentials to access Amazon Web Services through programmatic access and is a standardised way to manage AWS credentials. Open the file in a text editor and enter your *Access key ID* and *Secret access key* in the format shown below.
+
+    ```ini
+    [defold-liveupdate-example]
+    aws_access_key_id = <Access key ID>
+    aws_secret_access_key = <Secret access key>
+    ```
+
+    The identifier specified within the brackets, in this example _defold-liveupdate-example_, is the same identifier that you should provide when configuring your project's Live update settings in the Defold editor.
+
+    ![Live update settings](images/live-update/05-liveupdate-settings.png)

+ 128 - 0
docs/en/manuals/live-update-scripting.md

@@ -0,0 +1,128 @@
+# Scripting Live Update
+
+The api only consists of a few functions:
+
+* `liveupdate.add_mount()`
+* `liveupdate.remove_mount()`
+* `liveupdate.get_mounts()`.
+
+## Get mounts
+
+If you are using more than one live update archive, it is recommended to loop over each mount
+at startup and determine if the mount should still be used.
+
+This is important as the content may be not be valid for the engine anymore, due to file format changes.
+
+```lua
+local function remove_old_mounts()
+	local mounts = liveupdate.get_mounts() -- table with mounts
+
+    -- Each mount has: mount.uri, mount.priority, mount.name
+	for _,mount in ipairs(mounts) do
+
+        -- This requires the file name to be unique, so that we don't get a file from a different archive
+        -- This data is created by the developer as a way to specify meta data for the archive
+		local version_data = sys.load_resource("/version_" .. mount.name .. ".json")
+
+		if version_data then
+			version_data = json.decode(version_data)
+		else
+			version_data = {version = 0} -- if it has no version file, it's likely an old/invalid archive
+		end
+
+        -- verify the archive version against the version supported by the game
+        if version_data.version < sys.get_config_int("game.minimum_lu_version") then
+            -- it was invalid, so we'll unmount it!
+            liveupdate.remove_mount(mount.name)
+        end
+	end
+end
+```
+
+## Scripting with excluded collection proxies
+
+A collection proxy that has been excluded from bundling works as a normal collection proxy, with one important difference. Sending it a `load` message while it still has resources not available in the bundle storage will cause it to fail.
+
+So before we send it a `load`, we need to check if there are any missing resources. If there are, we have to download the archive containing those assets and then store it.
+
+ The following example code assumes that the resources are available via the url specified in the setting `game.http_url`.
+
+```lua
+
+-- You'll need to track which archive contains which content
+-- In this example, we only use a single liveupdate archive, containing all missing resource.
+-- If you are using multiple archive, you need to structure the downloads accordingly
+local lu_infos = {
+    liveupdate = {
+        name = "liveupdate",
+        priority = 10,
+    }
+}
+
+local function get_lu_info_for_level(level_name)
+    if level_name == "level1" then
+        return lu_infos['liveupdate']
+    end
+end
+
+local function mount_zip(self, name, priority, path, callback)
+	liveupdate.add_mount(name, "zip:" .. path, priority, function(_uri, _path, _status) -- <1>
+		callback(_uri, _path, _status)
+	end)
+end
+
+function init(self)
+    self.http_url = sys.get_config_string("game.http_url", nil) -- <2>
+
+    local level_name = "level1"
+
+    local info = get_lu_archive_for_level(level_name) -- <3>
+
+    msg.post("#", "load_level", {level = "level1", info = info }) -- <4>
+end
+
+function on_message(self, message_id, message, sender)
+    if message_id == hash("load_level") then
+        local missing_resources = collectionproxy.missing_resources("#" .. message.level) -- <5>
+
+        if #missing_resources then
+            msg.post("#", "download_archive", message) -- <6>
+        else
+            msg.post("#" .. message.level, "load")
+        end
+
+    elseif message_id == hash("download_archive") then
+		local zip_filename = message.info.name .. ".zip"
+		local download_path = sys.get_save_file("mygame", zip_filename)
+        local url = self.http_url .. "/" .. zip_filename
+
+        -- Make the request. You can use credentials
+        http.request(url, "GET", function(self, id, response) -- <7>
+			if response.status == 200 or response.status == 304 then
+				mount_zip(self, message.info.name, message.info.priority, download_path, function(uri, path, status) -- <8>
+					msg.post("#", "load_level", message) -- try to load the level again
+				end)
+
+			else
+				print("Failed to download archive ", download_path, "from", url, ":", get_status_string(status))
+			end
+		end, nil, nil, {path=download_path})
+
+    elseif message_id == hash("proxy_loaded") then -- the level is loaded, and we can enable it
+        msg.post(sender, "init")
+        msg.post(sender, "enable")
+    end
+end
+```
+
+1. The `liveupdate.add_mount()` mounts a single archive using a specified name, priority and a zip file.
+2. You need to store the archive online (e.g. on S3), where you can download it from.
+3. Given a collection proxy name, you need to figure our which archive(s) to download, and how to mount them
+4. At startup, we try to load the level.
+5. Check if the collection proxy has all resources available.
+6. If there are resources missing, then we need to download the archive and mount it.
+7. Make a http request and download the archive to `download_path`
+8. The data is downloaded, and it's time to mount it to the running engine.
+The next time the game is restarted, the mount is automatically readded.
+
+With the loading code in place, we can test the application. However, running it from the editor will not download anything. This is because Live update is a bundle feature. When running in the editor environment no resources are ever excluded. To make sure everything works fine, we need to create a bundle.

+ 42 - 278
docs/en/manuals/live-update.md

@@ -13,6 +13,14 @@ When bundling a game, Defold packs all the game resources into the resulting pla
 
 The Live update functionality expands the concept of the collection proxy with a mechanism allowing the runtime to fetch and store resources to the application bundle that were intentionally left out of the bundle at build time.
 
+It allows you to have your content split up into multiple archives:
+
+* _Base Archive_
+* Level Common Files
+* Level Pack 1
+* Level Pack 2
+* ...
+
 ## Preparing content for Live update
 
 Suppose we are making a game containing large, high resolution image resources. The game keeps these images in collections with a game object and a sprite with the image:
@@ -23,102 +31,28 @@ To have the engine load such a collection dynamically, we can simply add a colle
 
 This is done by simply checking the *Exclude* checkbox in the collection proxy properties, telling the bundler to leave any content in *monalisa.collection* out when creating an application bundle.
 
+Note that any resources referenced by the base game package, will not be excluded.
+
 ![Collection proxy excluded](images/live-update/proxy-excluded.png)
 
 ## Live update settings
 
 When the bundler creates an application bundle it needs to store any excluded resources somewhere. The project settings for Live update govern the location for those resources. The settings are found under <kbd>Project ▸ Live update Settings...</kbd>. This will create a settings file if none exists. In *game.project*, select which liveupdate settings file to use when bundling. This allows for using different liveupdate settings for different environments, for example for live, QA, dev etc.
 
-![Live update settings](images/live-update/aws-settings.png)
+![Live update settings](images/live-update/05-liveupdate-settings-zip.png)
 
 There are currently two ways that Defold can store the resources. Choose the method in the *Mode* dropdown in the settings window:
 
-`Amazon`
-: This option tells Defold to automatically upload excluded resources to an Amazon Web Service (AWS) S3 bucket. Fill in your AWS *Credential profile* name, select the appropriate *Bucket* and provide a *Prefix* name. [See below for details how to set up an AWS account](#setting_up_amazon_web_service).
-
 `Zip`
 : This option tells Defold to create a Zip archive file with any excluded resources. The archive is saved at the location specified in the *Export path* setting.
 
-
-## Scripting with excluded collection proxies
-
-A collection proxy that has been excluded from bundling works as a normal collection proxy, with one important difference. Sending it a `load` message while it still has resources not available in the bundle storage will cause it to fail.
-
-So before we send it a `load`, we need to check if there are any missing resources. If there are, we have to download them and then store them. The following example code assumes that the resources are stored on Amazon S3, in a bucket called "my-game-bucket" with the prefix `my-resources`.
-
-```lua
-function init(self)
-    self.resources_pending = 0 -- <1>
-    msg.post("#", "attempt_load_resources")
-end
-
--- This function is called whenever we have tried to store a downloaded resource
--- necessary for our collection proxy to load.
-local function resource_store_response(self, hexdigest, status)
-    if status == true then
-        -- Successfully loaded resource
-        print("Resource data stored: " .. hexdigest)
-
-        -- One less resource to go...
-        self.resources_pending = self.resources_pending - 1
-
-        -- That was all of them, time to load the proxied collection.
-        if self.resources_pending == 0 then
-            msg.post("#proxy", "load") -- <8>
-        end
-    else
-        -- ERROR! Failed to store the data!
-        print("Failed to store resource data: " .. hexdigest)
-    end
-end
-
-function on_message(self, message_id, message, sender)
-    if message_id == hash("attempt_load_resources") then
-        local missing_resources = collectionproxy.missing_resources("#proxy") -- <2>
-
-        -- initiate a download request for each of the missing resources that has not yet been tried.
-        for _,resource_hash in ipairs(missing_resources) do
-            msg.post("#", "attempt_download", { resource_hash = resource_hash})
-        end
-
-        self.resources_pending = #missing_resources -- <3>
-
-        -- if we're running from editor all resources are there from the start.
-        if self.resources_pending == 0 then
-            msg.post("#proxy", "load")
-        end
-    elseif message_id == hash("attempt_download") then
-        local manifest = resource.get_current_manifest() -- <4>
-        local base_url = "https://my-game-bucket.s3.amazonaws.com/my-resources/" -- <5>
-        http.request(base_url .. message.resource_hash, "GET", function(self, id, response)
-            if response.status == 200 or response.status == 304 then -- <6>
-                -- We got the response ok.
-                print("storing " .. message.resource_hash)
-                resource.store_resource(manifest, response.response, message.resource_hash, resource_store_response) -- <7>
-            else
-                -- ERROR! Failed to download resource!
-                print("Failed to download resource: " .. message.resource_hash)
-            end
-        end)
-    elseif message_id == hash("proxy_loaded") then
-        msg.post(sender, "init")
-        msg.post(sender, "enable")
-    end
-end
-```
-1. A simple counter that tells us how many resources we have still to download and store before we can load the proxy collection. Note that this code does not deal with errors at all so production code would need to do a better job at tracking the download and store operations.
-2. Get any resources that we need to download and store.
-3. Store the number of missing resources so we can count them down.
-4. We need the current manifest since it lists all resources in the bundle, and if they are available or not.
-5. We store our resources on Amazon S3. If you create a Zip archive with resources, you need to host the files somewhere and reference their location when downloading them with `http.request()`.
-6. Amazon returns status 304 when files are cached.
-7. We have data at this point. Try to store it.
-8. Storage was successful and we have decreased the resource counter to zero. It is now safe to send a "load" message to the collection proxy. Note that if download or storage fails at some point, the counter will never reach zero.
-
-With the loading code in place, we can test the application. However, running it from the editor will not download anything. This is because Live update is a bundle feature. When running in the editor environment no resources are ever excluded. To make sure everything works fine, we need to create a bundle.
+`Amazon`
+: This option tells Defold to automatically upload excluded resources to an Amazon Web Service (AWS) S3 bucket. Fill in your AWS *Credential profile* name, select the appropriate *Bucket* and provide a *Prefix* name.  You can read more on how to setup an AWS account in this [aws guide](./live-update-aws.md)
 
 ## Bundling with Live update
 
+_NOTE:_ Currently, the editor `Build & Run` does not support Live Update. Thus this bundling step is required for testing.
+
 To bundle with Live update is easy. Select <kbd>Project ▸ Bundle ▸ ...</kbd> and then the platform you want to create an application bundle for. This opens the bundling dialog:
 
 ![Bundle Live application](images/live-update/bundle-app.png)
@@ -127,218 +61,46 @@ When bundling, any excluded resource will be left out of the application bundle.
 
 Click *Package* and select a location for the application bundle. Now you can start the application and check that everything works as expected.
 
-## The manifest
-
-The manifest is an internal data structure that holds a list of all resources included in a build as well as the hash value of each resource. The Live update functionality uses the manifest to track what is part of the built game, list what can be loaded from external sources, and if that happens, make certain that the loaded data is intact.
-
-From the user's perspective, the manifest is a numeric handle, leaving the details of how it's managed to the engine.
-
-## Updating the manifest with Live update
-
-With Live update a new manifest can be stored locally at runtime. The local manifest will be used at app startup instead of the one bundled with the application. This is useful for modifying or adding Live update resources to a published game that were not known at build time, without having to publish a full release.
-
-When publishing Live update resources to either Amazon Web Service or to a zip archive, the manifest will be included in that package next to the resources. The name of the manifest file will be `liveupdate.game.dmanifest`.
-
-Starting the engine for the first time after a manifest has been stored will create a bundle identifier file `bundle.ver` next to the manifest. This is used to detect whether the bundle has changed since the manifest was stored, for example after a full app store update. If this is the case the stored manifest will be deleted from the filesystem and the newer bundled manifest will be used instead. This means that a full app store update will delete any previously stored manifest. Any existing Live update resources will however remain untouched.
-
-### Manifest verification
-When storing a new manifest the manifest data will be verified before it is actually written to disk. The verification consists of a number of checks:
-
-* Correct binary file format.
-* Supports the currently running engine version or any other supported version entry from the settings.
-* Cryptographic signature.
-* Signed using the same public-private key pair as the bundled manifest.
-* Verify that all resources the manifest expects to be in the bundle actually are in the bundle.
-
-From the user's perspective the verification process is completely opaque but it is important to note the steps involved to avoid the most common pitfalls.
-
-::: important
-If you see a "ERROR:RESOURCE: Byte mismatch in decrypted manifest signature. Different keys used for signing?" error in the console on HTML5 builds it is likely that your webserver doesn't serve the excluded content or updated manifest file using the correct MIME type. Make sure the MIME type is `application/octet-stream`. You can add a `.htaccess` file with a single `AddType application/octet-stream .` line to the folder where the live update resources are downloaded from.
-:::
-
-### Supported engine versions
-A manifest will always support the Defold version used when generating it. If you want to support any additional engine versions, add them to the list in the Live update settings. This can be useful if your live game uses a different Defold version than the one you are using to generate the manifest with.
-
-![Manifest supported engine versions](images/live-update/engine-versions-settings.png)
-
-### Generating keys for signing
-The manifest signature is used to verify that no one with malicious intent has tampered with the content, and that the bundled manifest and the new manifest were signed using the same keys. The signing is done as a part of the bundling process.
-A public/private key pair is used to cryptographically sign the manifest. Signing uses 512/1024/2048-bit RSA keys in `.der`-format that the user needs to supply. You can generate these using the `openssl` tool:
-
-```sh
-$ openssl genrsa -out private_raw.key 1024
-$ openssl pkcs8 -topk8 -inform pem -in private_raw.key -outform der -nocrypt -out private.der
-$ openssl rsa -in private_raw.key -outform DER -RSAPublicKey_out -pubout -out public.der
-```
-This will output `private_raw.key` (can be safely deleted), `private.der`, and `public.der`. To use the keys for signing open the Live update settings view and point respective fields to the generated keys.
-
-![Manifest signature key-pair](images/live-update/manifest-keys.png)
-
-### Scripting with Live update manifest
-Adding to the scripting example above, we add the following callback function
-
-```lua
-local function store_manifest_cb(self, status)
-    if status == resource.LIVEUPDATE_OK then
-        print("Successfully stored manifest!")
-    else
-        print("Failed to store manifest, status: ", status)
-    end
-end
-```
+## The .zip archives
 
-and the following to ```on_message``` to handle message ```attempt_download_manifest```:
+A liveupdate .zip file contains files that were excluded from the base game package.
+The .zip file also contains a manifest file that describes the meta data for each resource within the .zip file.
 
-```lua
-...
-elseif message_id == hash("attempt_download_manifest") then
-    local base_url = "https://my-game-bucket.s3.amazonaws.com/my-resources/" -- <1>
-    http.request(base_url .. MANIFEST_FILENAME, "GET", function(self, id, response)
-        if response.status == 200 or response.status == 304 then
-            -- We got the response ok.
-            print("verifying and storing manifest " .. MANIFEST_FILENAME)
-            resource.store_manifest(response.response, store_manifest_cb) -- <2>
-        else
-            -- ERROR! Failed to download manifest!
-            print("Failed to download manifest: " .. MANIFEST_FILENAME)
-        end
-    end)
-end
-```
-1. The manifest will be stored on S3 next to the rest of the Live update resources. As before, if you create a Zip archive with resources you need to host the files somewhere and reference their location when downloading them with `http.request()`.
-2. Similar to how resources are downloaded and stored, the call to `resource.store_manifest` takes the downloaded manifest data and a callback as arguments. The function will verify the manifest and persist it to local storage.
+While our current pipeline only supports creating a single .zip file, it is in fact possible to split that zip file into smaller .zip files. This allows for smaller downloads for a game: level packs, seasonal content etc.
 
-If `resource.store_manifest` succeeds, the new manifest is now located in local storage. The next time the engine starts this manifest will be used instead of the one bundled with the game.
+## Content verification
 
-### Caveats
-There are a few gotchas that might be good to know if you plan to use this feature to store a new manifest with Live update.
+One of the major features of the our live update system, is that you can use now use many content archives, potentially from many different Defold versions.
 
-* It is only possible to add or modify resources referenced by collection proxies that are tagged as `Exclude` in the new manifest. No changes should be made to already bundled resources or resources not in excluded collection proxies. For example, doing changes in a script that is referenced by a bundled collection will cause the resource system to look for that resource in the bundled data archive. But since the shipped game bundle has not changed (only the manifest has) the changed script will not be found and consequently cannot be loaded.
+The `liveupdate.add_mount()` default behavior, is to add an engine version check when attaching a mount.
+This means that both the game base archive and liveupdate archive(s) need to be created at the same time with the same engine version. This will invalidate any previously downloaded archives by the client, forcing them to redownload the content.
 
-* Even though this feature allows you to quickly push bug fixes or new features to a live game without doing a full app store release, it should be used with care. Pushing out a new manifest should involve the same processes as when doing a full release with everything that that entails (testing, QA, etc.).
+This behavior can be turned off with an options flag.
+When turned off, the content verification responsibility lies entirely with the developer, to guarantuee that each liveupdate archive will work with the running engine.
 
-## Setting up Amazon Web Service
+We recommend storing some metadata for each mount, so that _directly upon startup_, the developer can decide if the mount/archive should be removed.
+One way to do so is to add an extra file to the zip archive after the game has been bundled. For instance by inserting a `metadata.json` with any relevant information that the game requires. Then, at startup, the game can retrieve with `sys.load_resource("/metadata.json")`. _Note that you will need a unique name for each mount's custom data, or the mounts will give you the file with the topmost priority_
 
-To use the Defold Live update feature together with Amazon services you need an Amazon Web Services account. If you don't already have an account you can create one here https://aws.amazon.com/.
+Failure to do so, you may end up in a situation where the content is not compatible with the engine at all, forcing it to quit.
 
-This section will explain how to create a new user with limited access on Amazon Web Services that can be used together with the Defold editor to automatically upload Live update resources when you bundle your game, as well as how to configure Amazon S3 to allow game clients to retrieve resources. For additional information about how you can configure Amazon S3, please see the [Amazon S3 documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html).
+## Mounts
 
-1. Create a bucket for Live update resources
+The live update system can use multiple content archives at the same time.
+Each archive is "mounted" to the engine's resource system, with a name and priority.
 
-    Open up the `Services` menu and select `S3` which is located under the _Storage_ category ([Amazon S3 Console](https://console.aws.amazon.com/s3)). You will see all your existing buckets together with the option to create a new bucket. Though it is possible to use an existing bucket, we recommend that you create a new bucket for Live update resources so that you can easily restrict access.
+If two archives have the same file `sprite.texturec`, the engine will load the file from the mount with the highest priority.
 
-    ![Create a bucket](images/live-update/01-create-bucket.png)
+The engine doesn't keep a reference to any resource in a mount. Once a resource is loaded into memory, the archive may be unmounted. The resource will remain in memory until it is unloaded.
 
-2. Add a bucket policy to your bucket
+The mounts are automatically readded upon engine restart.
 
-    Select the bucket you wish to use, open the *Properties* panel and expand the *Permissions* option within the panel. Open up the bucket policy by clicking on the *Add bucket policy* button. The bucket policy in this example will allow an anonymous user to retrieve files from the bucket, which will allow a game client to download the Live update resources that are required by the game. For additional information about bucket policies, please see [the Amazon documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-iam-policies.html).
+_NOTE:_ Mounting an archive doesn't copy or move the archive. The engine only stores the path to the archive.
+Thus, the developer can remove the archive at any time, and the mount will also be removed at next startup.
 
-    ```json
-    {
-        "Version": "2012-10-17",
-        "Statement": [
-            {
-                "Sid": "AddPerm",
-                "Effect": "Allow",
-                "Principal": "*",
-                "Action": "s3:GetObject",
-                "Resource": "arn:aws:s3:::defold-liveupdate-example/*"
-            }
-        ]
-    }
-    ```
+## Scripting with Live Update
 
-    ![Bucket policy](images/live-update/02-bucket-policy.png)
-
-3. Add a CORS configuration to your bucket (Optional)
-
-    [Cross-Origin Resource Sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) is a mechanism that allows a website to retrieve a resource from a different domain using JavaScript. If you intend to publish your game as an HTML5 client, you will need to add a CORS configuration to your bucket.
-
-    Select the bucket you wish to use, open the *Properties* panel and expand the *Permissions* option within the panel. Open up the bucket policy by clicking on the *Add CORS Configuration* button. The configuration in this example will allow access from any website by specifying a wildcard domain, though it is possible to restrict this access further if you know on which domains you will make you game available. For additional information about Amazon CORS configuration, please see [the Amazon documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html).
-
-    ```xml
-    <?xml version="1.0" encoding="UTF-8"?>
-    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
-        <CORSRule>
-            <AllowedOrigin>*</AllowedOrigin>
-            <AllowedMethod>GET</AllowedMethod>
-        </CORSRule>
-    </CORSConfiguration>
-    ```
-
-    ![CORS configuration](images/live-update/03-cors-configuration.png)
-
-4. Create IAM policy
-
-    Open up the *Services* menu and select *IAM* which is located under the _Security, Identity & Compliance_ category ([Amazon IAM Console](https://console.aws.amazon.com/iam)). Select *Policies* in the menu to the left and you will see all your existing policies together with the option to create a new policy.
-
-    Click the button *Create Policy*, and then choose to _Create Your Own Policy_. The policy in this example will allow a user to list all buckets, which is only required when configuring a Defold project for Live update. It will also allow the user to get the Access Control List (ACL) and upload resources to the specific bucket used for Live update resources. For additional information about Amazon Identity and Access Management (IAM), please see [the Amazon documentation](http://docs.aws.amazon.com/IAM/latest/UserGuide/access.html).
-
-    ```json
-    {
-        "Version": "2012-10-17",
-        "Statement": [
-            {
-                "Effect": "Allow",
-                "Action": [
-                    "s3:ListAllMyBuckets"
-                ],
-                "Resource": "arn:aws:s3:::*"
-            },
-            {
-                "Effect": "Allow",
-                "Action": [
-                    "s3:GetBucketAcl"
-                ],
-                "Resource": "arn:aws:s3:::defold-liveupdate-example"
-            },
-            {
-                "Effect": "Allow",
-                "Action": [
-                    "s3:PutObject"
-                ],
-                "Resource": "arn:aws:s3:::defold-liveupdate-example/*"
-            }
-        ]
-    }
-    ```
-
-    ![IAM policy](images/live-update/04-create-policy.png)
-
-5. Create a user for programmatic access
-
-    Open up the *Services* menu and select *IAM* which is located under the _Security, Identity & Compliance_ category ([Amazon IAM Console](https://console.aws.amazon.com/iam)). Select *Users* in the menu to the left and you will see all your existing users together with the option to add a new user. Though it is possible to use an existing user, we recommend that you add a new user for Live update resources so that you can easily restrict access.
-
-    Click the button *Add User*, provide a username and choose *Programmatic access* as *Access type*, then press *Next: Permissions*. Select *Attach existing policies directly* and choose the policy you created in step 4.
-
-    When you've completed the process you will be provided with an *Access key ID* and a *Secret access key*.
-
-    ::: important
-    It is *very important* that you store those keys since you will not be able to retrieve them from Amazon after you leave the page.
-    :::
-
-6. Create a credentials profile file
-
-    At this point you should have created a bucket, configured a bucket policy, added a CORS configuration, created a user policy and created a new user. The only thing that remains is to create a [credentials profile file](https://aws.amazon.com/blogs/security/a-new-and-standardized-way-to-manage-credentials-in-the-aws-sdks) so that the Defold editor can access the bucket on your behalf.
-
-    Create a new directory *.aws* in your home folder, and create a file called *credentials* within the new directory.
-
-    ```bash
-    $ mkdir ~/.aws
-    $ touch ~/.aws/credentials
-    ```
-
-    The file *~/.aws/credentials* will contain your credentials to access Amazon Web Services through programmatic access and is a standardised way to manage AWS credentials. Open the file in a text editor and enter your *Access key ID* and *Secret access key* in the format shown below.
-
-    ```ini
-    [defold-liveupdate-example]
-    aws_access_key_id = <Access key ID>
-    aws_secret_access_key = <Secret access key>
-    ```
-
-    The identifier specified within the brackets, in this example _defold-liveupdate-example_, is the same identifier that you should provide when configuring your project's Live update settings in the Defold editor.
-
-    ![Live update settings](images/live-update/05-liveupdate-settings.png)
+To actually use the liveupdate content, you need to download and mount the data to your game.
+Read more about about how to [script with liveupdate here](./live-update-scripting.md).
 
 ## Development caveats
 
@@ -356,6 +118,8 @@ Forcing re-download of resources
 
   Defold creates a folder with the name of the hash of the created bundle on the device in the application support folder. If you delete the files in this folder, the application will invalidate the resources from the manifest and you can download and store them again.
 
-  ![Local storage](images/live-update/local-storage.png)
+  The file liveupdate.mounts is located under the "local storage", and it's path is output to the console at start "INFO:LIVEUPDATE: Liveupdate folder located at: ..."
 
   The location of the application support folder depends on the operating system. It can be found with `print(sys.get_save_file("", ""))`.
+
+  ![Local storage](images/live-update/local-storage.png)