AMANDA: Hey, folks! Hundreds of Arabian
Nights-themed environment assets and hand-painted 2D
textures are now available to all Unreal
creators--for free. Join us in welcoming Uppercut
Games City of Brass and OTK Games The Vagrant
to The Marketplace Collection, an initiative which
brings high-quality content from Unreal Engine
games to the Marketplace and download them all. And while you're snagging
those new free assets, check out the deals
of the future, today, during the Unreal Engine
Marketplace February Sale! You can save big on a
plethora of cyberpunk-themed products--including a killer
robot samurai--in addition to amazing environments,
effects, and more! The sale ends Friday at 11:59
PM EST, so don't miss out! Last week, we shared a sneak
peek at the MetaHuman Creator, our new cloud-streamed app
for creating high-fidelity real-time humans--and
you met the MetaHumans! But did you know
they're equipped with a complete facial
rig to create a full range of human expressions? Cubic Motion's Adam Walton
created a presentation to walk you through
each and every control, so you can create everything
from the most subtle smirk to the broadest grin. Watch the video on
our YouTube channel and download the MetaHuman
sample via the Marketplace to get started. Explore the afterlife in the
puzzle-adventure I Am Dead, in which you assume the role
of the recently deceased Morris Lupton--he is the island of
Shelmerston's only hope against an impending volcanic eruption. Uncover how Richard
Hogg and Hollow Ponds crafted I Am
Dead's whimsical visuals, charming world, and inventive
gameplay in our interview: NVIDIA has now released the DLSS
Plugin for 4.26--DLSS is a deep learning neural network that
uses the power of the NVIDIA RTX Tensor Cores to boost frame
rates and generate sharp images in-game, while getting
performance headroom to maximize ray tracing settings
and increase output resolution. Find out more details
on the NVIDIA DLSS plugin on the Marketplace. And wrapping up
this week's news, we are proud to share that Epic
Games Lead Virtual Production Engineer Alejandro Arango
has been recognized with a Sci-Tech Award by The
Academy of Motion Picture Arts and Sciences for his
significant contributions to the widely adopted
Technoprops head-mounted camera system. Congratulations Alejandro! Now over to this week's
top karma earners! Many thanks to: Detach789, Everynone,
ClockworkOcean, Kehel18, Shadowriver, chrudimer,
LunaNelis, BenVlodgi, AnotherZach, and nofxboy1234 Diving into our Spotlights,
French indie studio ASM Games is working on their
turn-based RPG Oaths of Light, which tells the story of Eden,
a young girl who stumbles into a journey of
fate much bigger than she could
have ever imagined. Learn more about Oaths of Light
and support the small team on Kickstarter. And next up is
Project Depto 478, a gorgeous--and stylish--modern
apartment by Miguel Rueda built using Substance Painter,
3DS Max and Unreal. Head over to their YouTube page
to watch the full cinematic on apartment 478 and see
even more incredible abodes. The final spotlight
this week is a series of scenes built to
explore ray tracing and global illumination. These absolutely stunning pieces
were created by Saga Alayyoubi, with inspiration from
classic paintings, the Speed of Light demo, and others. Head to ArtStation
and take a closer look at these great works! Thanks for watching this week's
News and Community Spotlight! VICTOR: Hi, everyone. And welcome to another episode
of Inside Unreal, a weekly show where we learn, explore, and
celebrate everything Unreal. I'm your host Victor Brodin. And my guest today is technical
writer Michael Prinke. Welcome back. MICHAEL: Hello, Victor. I'm glad to be back. And this week, we have an
episode on the Chunk Downloader patching solution. Now, to start out, I want to
outline what exactly the Chunk Downloader solution is and what
it is designed to accomplish for you in your projects. So if you are building
a live service game or if you're
building a game that has to take frequent
updates, like an MMORPG, or if you are running
a mobile game that has to get patched
very frequently, especially a mobile
game, in fact, one of the major problems
that you can encounter is large file
sizes for your base executable on the
storefront that you're shipping your game on. So, basically, if you have a
really, really big executable, it just creates a
whole lot of problems. A lot of store fronts will
have either hard limits, where they won't let you put
an executable of a certain size on the storefront at all. Or they will have a soft
limit, where they will say, OK, after a certain size, you can
use Wi-Fi to download this, but you can't use
cellular data to do it. And even if you could do
it, then large file sizes are really inconvenient
on cellular data anyway. If you have a
multi-gigabyte sized file, it could take a lot
of time to download. It could start and stop
a whole lot of times throughout the day. And that's just
the kind of thing that makes users look at the
file size on the storefront and say, mm, no, I'm going
to pass on this application, I'd rather pick up something
that I can play right now. That's a fairly typical
kind of reaction. And while this seems a really
trivial, petty kind of criteria for whether or not people are
going to download your game, believe it or not, it makes a
really, really big difference. That aside, if you
are running just a general like live
service type of product, like an MMO or something more
on a modest scale than that even but that has to get
patched very frequently, updating very
frequently can be very painful and difficult to
sustain if every update requires you to push a new version
of the base executable to the user every single time. It is the kind of
thing that users get fed up with very, very quickly. It takes longer and longer
as the project grows in size. It's just not
really sustainable. So the common solution
to both of these problems is to separate out most
of your games assets, like the textures, sounds,
models, even maps, and things like that, and use
a content delivery system to add or
remove them at runtime as you need them instead of
putting them all in the base executable and making the
user install all of it all at the same time. Then you can reduce the
base executable size down to something
very small, something that you could measure maybe
in the megabytes instead of the gigabytes. And then they can start
that up almost immediately no matter what kind of
connection they're on. And then the patching solution
can grab files, basically, as the user needs them or as
the application needs them in order to deliver the
full user experience. And that is basically what
Chunk Downloader does. Chunk Downloader is
a patching system for delivering external
files and mounting them to your running
executable at runtime. So we're going to show
you how to implement it in a project and
all of the stuff that you need to do to prepare
your assets to work with it. So to begin, let's hop on
over to the Unreal Editor here for a second. The very first thing
that you need to do, obviously, is make sure
that the Chunk Downloader Plugin is enabled. And you can see,
we have it enabled. I think that it's actually
enabled by default in 4.26. This was added in
4.26, by the way, as a relatively new feature. To give a little background on
where Chunk Downloader comes from, it's actually
the patching solution that we used in Battle Breakers. It was built specifically
for Battle Breakers, which has a large number
of small files that it has to deliver for
all the different puzzles and characters, and things. And so it wound up being
a very clean solution for this type of thing. And it was good
enough to document and good enough to
put into the engine instead basically
as a replacement of HTTP Chunk Installer,
which is the older solution. We definitely
recommend going forward using Chunk Downloader
instead of HTTP Chunk Installer simply because it requires,
I think, as you'll see, a little bit less setup. And it's a bit more
user-friendly and easy to deal with. But, anyway, once
you've got the plugin for Chunk Downloader enabled,
we hop over to Visual Studio. And we go to the build.cs
file for your project. And you just have to add to
the private dependency module names the Chunk
Downloader module name. And then it will be available
for you to use in your C++ project. I've also added the HTTP
module here so that we can make a basic HTTP request. I'll go over what
that's for in a minute. Shout out real quick to
Sebastian Hernandez, who is actually the Evangelist
who helped prepare this sample project that we are
showing you today and that we will be able
to make available for you to download once this
presentation is all complete. Sebastian has been very helpful
with both this and the game playability system stuff that
I showed of a couple of weeks ago. So I want to give
credit where it's due. But, anyway. So once you've got
all of that stuff ready to roll, the
dependency module name setup and the
plugin enabled, you are ready to start implementing. But before we go into actually
implementing Chunk Downloader in code, I'm going to
talk about the cooking and chunking process. So chunking. Chunks are basically
groups of files that are packaged up into
external dot PAK files when you cook the
content for your game. What you do in order to
separate things into chunks-- we might have gone over this
during last week's stream. But I'll go over
it again anyway. Last week was all about
the Asset Manager. And this is all
fairly related stuff. What you do is you either go
into your Project Settings and go to the Asset Manager. And you set up your
primary asset types there. But you can also set
up primary asset rules, which are a lot of the
information that you need in order to assign chunk
IDs to groups of assets. Doing it through the Asset
Manager in the Project Settings is a little bit clunky. So it's not the way that
I prefer to do this. But you can do it
that way if you have some universal
rules that you always want to institute here. What, instead, I
would recommend doing is using primary asset labels. So to make a primary asset
label, all you have to do is right click in the Content
Browser, go to Miscellaneous, pick Data Asset because it
is a type of data asset. And then you pick Primary
Asset Label as your type. I've already got a primary
asset label in this folder for the bear character here. And this has all
of the information necessary to separate your
asset into a different chunk. By default, all assets
inside of the Assets folder are going to be grouped
into chunk 0, which is the chunk that is packaged
with the base executable. If you set the chunk
ID to something else, then the assets that belong
to this primary asset label will be assigned that
chunk ID instead. And they will be put into
a pack file corresponding to this chunk ID. This is set to Label assets
inside of this directory. And so everything
that is just inside of this folder for
the bear here is going to be put inside of chunk 2004. Another important thing to
note here is the Priority. The Priority actually
relates to the prioritization of secondary assets for
this primary asset label. So the primary assets
are going to decide who gets precedence of ownership
based on this Priority. If it is a higher
priority value for this, then it should take precedence. And if it has equal
priority, then both assets will actually-- both of them will take ownership
over the secondary assets that are assigned here. There are a couple
of neat things you can do with primary asset
labels, which I will talk about later in terms of extending
this functionality and adding more information
and metadata to this that you can use
for managing assets. But for right now,
we are just going to stick with the basic
application of a default primary asset label. And the way that we
have divided it up here, we have each of these
characters with a primary asset label, it is a unique
ID for each one. So for 2005, we have
greystone for 2003-- wait, no, I think 2003. Yeah, 2003 is greystone. Sorry, misspoke. 2005 is this troll character. Decker is 2002 and
so on and so forth. And they don't have
to all be unique. You can absolutely take more
than one primary asset label and give them the same chunk ID. And they will all contribute to
the same PAK file, basically. But this is an easy
way to make sure that each character is its
own chunk and its own PAK file in the end. There are also a couple
of other primary asset labels of note in this project. We have the Loader
Primary Asset label, which is explicitly assigned chunk 0. That's kind of like
a safety measure to make sure that
all of the core files that we need to start
up the application at all are all contained
inside of chunk 0. And then we have two
different variations of the user interface
that's going to show up when we load into our game. And they are in 5001 for variant
A and 5002 for variant B. And just so you know what they
look like, variant B looks like this, where it's
red and in the lower left corner with the text. And then variant A is blue. And it has the text in
the upper right so they are easy to distinguish. I'll go over what
exactly we're using those to demonstrate later. But that in a nutshell is
how you organize things into chunks. You want to think ahead when you
consider your chunking scheme. Basically, you want to make
sure that all of your files are reasonably digestible
and that they are all within a certain size
range and that if you need an asset from
inside of that PAK, you're not depending on
something outside of that PAK file. Battle Breakers has
an interesting scheme where they extended the
primary asset label class and made, basically, a
custom primary asset label class that has a concept
of a parent chunk. So it designates
not only the chunk ID for that primary asset
label but another chunk ID that that chunk depends
on in order to load. And so what they
would do is chunk 1000 would be the basic
stuff for characters. Chunk 1100 would be like
a specific character. And then chunk 1110 would
be a specific character with a specific skin
and stuff like that. And they wrote the asset
manager in such a way that it could look at
the parent chunk ID when they were trying to
load up that chunk and say, if I don't already have this
chunk, let's load the parent trunk as well. And it would walk all
the way up the ladder to get all of the chunks that
was dependent on right back to the basic stuff that makes
up the base of the game. There's actually a really cool
tech blog that you can find, I'll see if I can
dig up the link to it later, that describes
the system in detail. And it's a really,
really cool way of doing things if
you have these kinds of interdependencies
for your chunks. But by default,
all the PAKs will be fairly independent files. And in order to
generate your PAKs, you go to a Package Project,
you pick whichever platform you are going to package. In this instance, I
would hit Windows 64-bit. I'm not going to
make you sit here and wait for me to package it. And then I would go
to my Builds folder, where I have output
the packaged project, go into the game
folder, under Saved. And it will be-- no, it's not under Saved. It's under Content. Under Content, Paks. And this is the directory
that it outputs to. In fact, let me see if I
can hop up to one that I haven't modified-- Builds, Android18,
nope, nope, nope. Heck with it, I will just
go ahead and package it. Why not? Why not show you that? VICTOR: Easy enough. MICHAEL: Give it a
second, doesn't take very long. So there's a step after this
that I forgot about that where you actually take
the PAKs out of the folder. So we need to see the
PAKs in the folder first. But if you can't
find it in there, there is another resource
that you can look at. In the Project folder,
there is a save directory. And you can go to-- where is it? StagedBuilds. And then that will have the
Content, Paks folder in it as well. And you can see all of the
PAK files located in here. That is the place you
would look if you don't know where else to find it. For Windows, you should actually
see it inside of your Final Build folder, like so. And if you are using
a different platform, it will probably be under the
StagedBuilds folder inside of the saved directory here. So if I go into the
Android one, you can see that I have the Android
version of all these PAKs lined up inside of this directory. And that is the other place
where you can find those files. Now, just to make a quick
point about the file size, here is chunk zero inside of
the version of the project we just packaged. It is 108 megabytes in size. Earlier, I made a
different version of this that does not
have chunking enabled. And you can see that
this is a file that is over 2 gigabytes in size. It is a much larger file. And that is what I
would be asking the user to download to
their device all at once if I were delivering
that version of the project. It is just that easy to
separate things into PAK files. The hard part is not
making it output chunks. It is designing
your chunking scheme in a way that makes sense
for the final delivery of your content and that is
sustainable and organized so you know what is in
each of these files. And that's actually
a worthwhile thing to talk about in the
project settings. Make sure Generate Chunks is
turned on under Packaging. This is something that you
can turn off and on depending on the builds that
you're outputting and then things like that. But that is basically-- that is something a
prerequisite that you have to make sure that you have set. Otherwise, you are not going
to get any chunks at all. But now that we have
our PAK files generated, we need to build
a manifest file. So what chunk downloader does,
the first thing that it does is it grabs a manifest
file that is basically a list of all of the
PAKs that you expect to find on the user's
device, all the ones that are valid for a current
build of the game and that you want users
to be able to download. And this manifest
file must contain the following information. It is just a text file. And it contains
the number of PAK files that you need that you
are listing inside of this file so that it knows how many
entries to read through. And then it needs a build
ID, which is, basically, going to be either like a
deployment ID or a sandbox ID. So as an example, Patcher Demo
Live or Patcher Demo Testing or Patcher Demo Development. I'll call this one Development
and set it to eight PAK files because we have 1, 2,
3, 4, 5 6, 7, 8 PAKs that we've divided
this game up into. And then we need to make an
entry for each PAK consisting of the name of the PAK file. VICTOR: Is pakchunk0 not considered one of the entries? MICHAEL: Correct
pakchunk0 is shipped with the base executable. It is always available to your
initial executable to access. It is not something that
you need to concern yourself with in Chunk Downloader. But for the entries that we do
need to concern ourselves with, we put in the file name. Then we put in the size
of the file in bytes. And to do that, you can
just look at the properties. It will be the size,
not the size on disk. And we break that down without
any of the commas or anything, just like that, this
big ugly looking number. Then we have a version stream. This is an arbitrary string. This is something
where you come up with your own
convention for how you want to designate the
version name of all these different PAK files. And whatever
convention you use, it can just go into this arbitrary
string for what the version is. Then you need the chunk ID. And then you need
the relative path where this is going
to exist on the web host, which is
something that I'm going to get into in a minute. But I will just, for the sake
of argument, call this Windows. And I will put the name
of the PAK file here. And that is what's going
to be appended to a URL that Chunk Downloader
is going to use to try to fetch this file. It is relative to the location
of the build manifest. So whatever folder this
build manifest exists in, it has to be inside of the
folder relative to this. So what I will actually
go ahead and do is hop back up to
this base folder. And I've got a
PatchStaging folder here, where I have kind of pre-built
the directory structure for the website where
we're going to host this. And I've got a couple of extra
things that I put together. So I've got a built
manifest set up here with all the entries for all
these different PAK files. And then I have
a VariantA folder that contains all
of the files that are relevant for variant A and
a VariantB folder that contains all of the files that are
relevant for variant B. You probably don't necessarily
need to duplicate all of this. I could just as easily take
and have a universal folder here or a universal
Windows folder or something like that for
all the ones that they share. But, basically, you remember
that user interface thing where there was a variant
A user interface and a variant B
user interface, this is how I am separating them. So chunk 5001 is
variant A. And that is going to be the blue interface. And variant B with the red
interface uses chunk 5002. And these two manifests
do not include each other's exclusive files. I hope that is clear to explain. The usual use case for
something like this is if you had
separate platforms. So if this were Windows and
this were Mac or something or if one of these were going
to be on Android and one of them was iOS, that would
be a use case where you would set them up this way. But as it is-- I believe this is
variant B. Yes, it is. As it is, these are just
arbitrary for the sake of the demonstration at hand. So now we've got
that all set up, the last thing that we've got
set up in this Staging folder is this file called
ContentBuildID.txt. And all of this has in
it is the name of the build that we are going to get. It is PatcherLive. It corresponds to
this folder here that contains the build
that we are going to get. You'll understand why this
file exists in a minute. There are lots of different
ways you could approach what this is going to do. But this is one way of doing
it so that the user doesn't have control over it. The idea is that you have this
file that the user is accessing through the internet to
check what version you want to put on their device. So if I wanted to make a Patcher
Live Holiday for Christmas or something like that, then
I could just edit this file. And that would be
what eventually tells Chuck Downloader to
grab this folder instead of this folder. And that's something
that you could do. I think that's probably
crazy off label use that I would get yelled at for. But that is a way to use it
and a simple demonstration of how powerful it can
be to control things with this remotely hosted
file instead of hard coding it in the user's application. So now that that's
all set up, we have to actually put together
a locally hosted website where users will be able
to access this. So first thing
that you need to do is go to Turn Windows
features on or off. It brings up this window here. And you need to turn on
Internet Information Services. This gives you the ability to
put together a website locally on this machine. And once you activate
that and hit OK, it will take a minute before it
finishes updating your machine and getting everything set up. Once it's done, you can open up
Internet information Services Manager. And this will give
you the ability to look at your locally hosted
website, which, you can see, I've already got set up here. The first thing
that you will have to do in order to
make sure this works is go to this top level thing
here for the local host. And you will need to
open up your MIME Types. And in the MIME Types,
you need to add-- let's see where it is-- dot PAK. That will tell it to
recognize PAK files. And you need to give it a MIME
type of application slash octet stream, just like that. And then once you have added
that MIME type to your list, you can-- I didn't actually
mean to close that. Then it will recognize
that type of file. And you'll be able
to download those from your website just fine. And then all you have to do
is right click on the default website home, hit
Explore, and then you can create a folder
for your project. In this case, I've
called it PatcherDemo. And all I had to do for this
was take the stuff in my Staging folder-- darn it, I keep
clicking the wrong-- so much white space on
the screen to click on. All I had to do was
take all of the stuff from this PatchStaging
folder that I prepared with the variance, the PAK
files, the build manifests and everything, and this
ContentBuildID text file and copy it over into
this PatcherDemo folder. And then it is
available for me to use. It is on a website that is
running on this computer locally and that I can
access with the default refer to self IP address, the
127.0.0.1 IP address, which is just how to access yourself. Make sure that it
is all running, that it is not stopped
and everything. And then you should be good to
go on the website side of this. And then, finally, the last
bit of prep before we go into the C++ code and
everything, we need to go to-- it's showing Explorer. I'm really lazy about
opening my project folder. We need to go to Config,
go to DefaultGame.ini. And we need to add
this section here Script Plugins dot Chunk
Downloader and then the deployment name that you're
going to use for this build, which I've called Patcher-Live,
Patcher dash Live. This is specifically
a deployment ID that you are going to
feed to Chunk Downloader. It is not necessarily the same
as the build ID for the folder that you've got here. I just made it the same
as the build ID name for the sake of not having as
many names to keep track of. But you need to
add CDN Base URL is equals 127.0.0.1 PatcherDemo. And then it will look for this
website on your own computer and fetch that URL. And it will be able to access
that website for download. This is a piece of information
that Chunk Downloader will use internally in order
to access all of those files when you make patch
requests and, in fact, when you look for the
manifest file, I believe. Are there any questions
so far before I get into the implementation phase? I'll see if I can answer them. VICTOR: We have a couple. Yeah, if you want to go
through it, we can do it now. MICHAEL: This is a good
point to take a couple of them. VICTOR: Cool. MICHAEL: I'll see
if I can answer them. VICTOR: I'm sure you'll do your best. Someone with a Cyrillic name
that I unfortunately can't read is asking, "Is there any
difference from mobile patching system plug-in-- which is available in UE4-- that you can use to download
chunk files as well?" MICHAEL: This
is a separate patching system from that one. In fact, this is the one
that we would recommend over all the other patching
solutions we've engineered with the possible
exception of using Google Pad if you're doing
Android app bundles. Google Pad is specific
to Android app bundles and has a lot of automated
features for fast follow and on-demand delivery
of pack files as well. But yeah, the mobile
patcher is a separate system that you don't need to use in
order to use Chunk Downloader. VICTOR: ekiander asked, "How do you do versioning? Let's say, Chunk 200 is
released in version 1.0, and then data is changed in 1.1. Do you just re-upload a
chunk to your server, or do you make a patch?" MICHAEL: So what
you do is you just-- depending on how you want
to keep track of that, typically you would upload the
new version of the pack file to your serve, and
that is eventually what you're going to need to do. Where that lives and how you
are keeping track of versioning is kind of up to you, at least
that's as far as I understand it. You can kind of use
the manifest in order to control what chunks
are available to the user and keep track of a version
string for your own purposes, but that's obviously
not the same as passing differently versioned
copies of pack files around. But the idea is that
you replace things one pack at a time, as needed. And those are relatively small
downloads to ask users to do. When Chunk Downloader
looks at the manifest, it actually compares it against
a cached copy of the manifest to see which one is the newer
version and if they match. And it's using
this bite size here to determine if the file
that you're putting up is the same expected file size. So if it's not this
file size, then it's going to want to replace it. I hope that makes sense. VICTOR: I think so. Yeah, let's continue
with the questions. And then if we get
into the next part-- MICHAEL: One
or two more, yeah. VICTOR: OK, two more. We'll do two more. Epgenix asked, "Can
you manage an entire game over the internet with
a Chunk Downloader?" MICHAEL: Yeah. That's the idea. The idea is, you strip out
most of your game's content into pack files like this, which
is an extensive thing to do. If you do this for
an entire project, you could have
hundreds and hundreds of lines of chunks with
all kinds of different-- that's the reason why we're
not using Chunk001 or Chunk002. We're using 2,001
2,002 2,003 and so on. 2000 we have set aside
for characters here. And then the end number
is different characters. And then 5,000 is
these different UIs. And then 10,000 here we have
set aside for the level file, actually, the map that you
load into when you start up the game. That is the convention we are
using for this demonstration. You can decide to use
whatever convention you want. But however you set this
up, the manifest file relative to everything
else in your game is going to be a really,
really small file. If it measures in the megabytes,
it'll be kind of surprising. Sorry, I thought
I heard something. But yeah, that's
basically the idea, is that the base
executable you're giving the user is this
minimalistic start-up flow kind of executable. And then everything else that
you have is kind of delivered on demand or in
packages depending on what phase of the
application you're in. So you might make
a chunk that is dedicated to a
specific character, and be that granular about it. Or you might make a chunk
that is dedicated specifically to the first time user
experience and all the stuff that you would expect the
user to experience then, like the tutorials and the
first level of gameplay, and all that kind of stuff
that they would basically play in the first 15 minutes. In fact, it is a very
common convention to have an asset package like
that for the first time user experience in many of these
different types of games, and very quickly load it up
as one of the first things that you deliver during the
patching process in-game. VICTOR: Cool,
let's do one more. Giulio Dionisi asks, "Can we use the Chunk Downloader
to update config files or is it content only?" MICHAEL:
This is for assets. This is for anything that you
would put into a pack file. Config files are a
completely different-- I'm not actually
completely sure if there is a way you could do
that, but I don't think so. I will get back to you if I
turn up any information that says otherwise. But I think if you're
changing the configs, then you're stuck updating
the base executable. The good news is
that if you think ahead about how you are handling
chunking and things like that, updating the base
executable does not have to be that painful
and you shouldn't have to do it that frequently. VICTOR: Cool, yeah. We can do the next questions. MICHAEL:
But I would think that basically if it is not
inside of the assets folder and not visible to the content
browser or the asset manager, that is the key. It has to be visible
to the asset manager in order to be cooked
into a pack file. And very sorry about
that, Douglas Adams is barking in the background. There's probably a dog outside
that he wants to say hi to. We will have to
continue on anyway. VICTOR: Do we get to see
him on the stream again today? MICHAEL: We just might,
if he decides to come in here and be demanding. Dachshunds are
very stubborn, man. OK so, now we get to the fun
part of actually implementing the code for Chunk Downloader. Now that we've done
all of that set up-- which by the way, if
you get stuck on it and you fumble
around for a while, it is the most finicky part of
setting this whole thing up. A lot of information in
this depends on the name that you give the
website folder, the name that you put into the
config file, and all of that sort of thing. But fortunately, once
all of that is set up, the process of working with
Chunk Downloader in the game's code is relatively easy. That's going to drive me nuts. VICTOR: OK, he's
pretty low on our end. MICHAEL: OK, that's good. So the easiest way to
handle Chunk Downloader is to make a custom GameInstance
class and initialize and shut down
Chunk Downloader there. And then when you
want to implement different
Chunk Downloader functions and expose them to
Blueprint, or expose them to different
GameModes, or whatever else needs to have visibility
to Chunk Downloader, it is always in this class. And this class is
always available anywhere that you are
running your game. GameInstance is like
a singleton class that is Blueprint
accessible and that is just very easy to refer to no
matter what GameMode you're in, no matter what map you're in. Wherever you are, you
have GameInstance. So to walk through this one. So the process for setting
up Chunk Downloader is first, you need to get a TSharedRef
or a Shared Reference Pointer to Chunk Downloader using
FChunkDownloader get or create. Chunk Downloader is
a singleton class that exists in the background. And get or create will
either get a copy of it-- well, not a copy of it-- but
a shared reference to it, if it exists. Or it will create
an instance of it, if there isn't already an
instance of it running. And that's how you
instantiate Chunk Downloader. You then call the
Initialize function and give it a string with the
platform name that you're using and an integer value for the
number of concurrent downloads in flight that you want to
allow Chunk Downloader to use. You can set up a lot
of logic to decide what this number is going to be. We've got eight here
as just an arbitrary number that's pretty standard. But you can decide to
set this lower or higher, based on information
that you've got about the user's network or
the platform that you're using, or whatever. If you want to allow
more or fewer streams, that is something that
you can totally do. Once you have called
Chunk Downloader to Initialize, then it is up and running
and ready to be used. The first thing that you need
to do once it is up and running is call LoadCache to build
with the DeploymentName for your build,
which is going to be in this case Patcher-Live. This DeploymentName
is the same thing as what is written here in
defaultgame.ini in this section here for Chunk Downloader
That name should be the same as
this name, basically. Otherwise, when it looks for
the CDN URL or any other data that you need inside of
the default game config, it will try to find the
other ones you've got. If I put in
Patcher-Development, it would try to look
for this instead. But if I don't have that
there it won't find it, and we will get
errors and it will try to fall back to a local
version of the manifest file. But otherwise, LoadCacheBuild
with the DeploymentName, that will try to load up a
cached version of the manifest file for comparison
against the version that you are going to download. The next thing
that you need to do is actually run the
Update Build function. And that is going to take
the DeploymentName, that is going to take a BuildID-- which I'm going to
go over how we're acquiring that in a second-- and then a callback, which we've
written in a Lambda function here for what to do
when it is finished getting the new manifest file. And then basically,
once that is all done, Chunk Downloader is initialized,
it has the updated manifest file, and you are ready
to download files with it. To be clear,
UpdateBuild does not download any files
other than the manifest. It just gets the
manifest on your computer so that Chunk Downloader has this
clipboard that it can look at to see if it's a valid
set of chunk files that you're asking
it to download later. But we're going
back up for a minute and walk through the flow
of how this thing is set up. So what we've got here, we
have PDGameInstance in it, which is not really
doing anything here, but you could initialize
Chunk Downloader here if you wanted to. And then we have the
shut down function, where we call
Chunk Downloader ShutDown. So any time the running
GameInstance shuts down, it will also tell
Chunk Downloader to stop, unload itself, and all
of that good stuff. The ShutDown function actually
does a lot of different things under the hood. It is not just unloading
the Chunk Downloader module, it is also unmounted any
files that are currently mounted in memory. And it also stops any
downloads in progress, and then it will
shut down the module. So it is a nice,
safe method to call to just turn off everything
Chunk Downloader is doing. But the component
methods for that are all available inside
of Chunk Downloader as well. So those are the
basic things that you should make sure your game has
so that you are starting it up and shutting it down safely. Thereafter, the
rest of this stuff that we have defined in
this game instance class is all Blueprint callable
functions and Blueprint pure functions that are
designed to just expose things to Blueprint so that we
can manage the patching flow inside of here once all
of the setup is done. So I'll walk you
through the full flow. The first thing that
you end up calling here or that this application
calls is InItPatching, which would be like on
Begin Play or something like that in a game mode. We feed it this variant
name and that corresponds to these variant names. Variant A and
Variant B. And that is why we have appended
Variant A and Variant B to these different manifests. This is normally the platform
name that you're looking at, which is why I made the
point of renaming those for Windows and Mac, as a
little demonstration earlier. Usually you would tell it what
platform you're looking at, and then it would try to fetch
a manifest that corresponds to that platform,
and that would tell it to grab these files
from this platform folder and stuff like that. So that is stuff that is getting
fed in through Blueprints. I will go into-- not there, but in
LoaderCore in the GameMode. This is where it is set up. We do Begin Play and we
grab this GameInstance, set GameMode-- where are we calling that? There's InItPatching, right. So we actually have
a user interface for this where we have a button
for Variant A and Variant B, That's why it's set up in
that arbitrary kind of way instead of based on a platform. Then we call PatchVariant
in our GameMode class, that runs InItPatching
with the Variant name that we supply from
the user interface. And that will set up
this platform name. From there, we have
a basic HTTP request. None of this functionality
is Chunk Downloader stuff. This is just the basic HTTP
module doing a simple request to grab an object from
the remote website. And we feed it this
patch version URL, which is contained in here,
under the Protected Properties here. It is just an F
String with the URL. And inside of the Blueprints
is where I'm actually defining it under this Blueprint
version of the GameInstance. So this inherits from the
game instant class in C++, and the version URL is just
written in here along with an array of chunk IDs that
we want to download when the application starts up. So this path to the
content BuildID text file is what is getting called here. And all we do is we set up
this request with that URL. We set it up as a Get and
we set it up with this delegate here to run on
PatchVersionResponse. This is the one
piece of code here that is not exposed
to Blueprint, it is on PatchVersionResponse. This is a very common
design pattern. If you are not familiar with
web programming or network programming,
asynchronous processes are a very common
design pattern when you are grabbing information
from across a network. Where you tell it to make a
request to some remote host, and then the remote
host starts an operation or you start downloading
a file from it, and then you listen
for a response and then fire off a delegate
when it's done. Or in this case, when
the download is finished and this request
is all processed, it will fire off this delegate. This makes it so that you aren't
sitting there writing things in the Tick function waiting
for this to be finished. But once on PatchVersionResponse
is fired off, it will get the content
BuildID from that text file as a string. This is, again, this text file. So what it'll get is
Patcher-Live from this text file and it will tell
it, this is the folder that I want to get,
this is the BuildID name that I want to use. You could organize this any
number of different ways. I could change this to 1.0.1
or something like that, and then change this
folder to 1.0.1. I could have dozens of folders
with back versioned versions of this game live on here for
whatever purposes I deem fit. But in this case,
I just named it the same thing as
the DeploymentName, because I'm lazy and
get confused with names. But once you get
that content BuildID, we can feed it in
through UpdateBuild along with the DeploymentName. Otherwise, in
between those steps, we are just following the
basic initialization flow that I was talking about before
where we do get or create, initialize with the platform
name that we are designating. This is for its internal
management purposes. It can be an arbitrary
name if you feel like it. But basically, as long as
this is a consistent name for whatever platform
you're doing, that is what it's
expecting you to do. And then we run UpdateBuild
with this manifest complete callback, which is what we
call when the manifest finishes downloading, which is a nearly
instantaneous thing that fires off because the
manifest is such a small file. The one thing that
we are having this do, we are telling it that-- we're setting this flag
for this Boolean saying that the manifest is
or isn't up to date, and we are firing off a
delegate called OnPatchReady, which we have declared here. And all this is a
multicast delegate that is declared up here,
PatchCompleteDelegate Bool succeeded. You get the idea. And this is just a Blueprint
assignable function. And what that allows
us to do is it allows us to respond when
the patching system is fully initialized and the
manifest is completely up to date. Here in this GameMode is where
the OnPatchReady delegate is implemented. And we actually
start it up in Begin Play by binding that
event to the delegate by grabbing a copy
of the GameInstance. And we actually
have a cached copy of the game instance for that
purpose in this demonstration. That's what these are. These are grabbing that
OnPatchReady delegates out of the GameInstance and just
taking these Blueprint events and saying, this is what
you're going to fire off when that broadcast call happens. And then after that, we just go
right into calling PatchGame. I hope that this is
easy to follow so far, because I know it's a little
bit of jumping between different classes and different
implementations in Blueprint and C++ so far. But this is a fairly clean way
to do things just to get it up and running. You could of course, instead of
calling OnPatchReady.broadcast, write a C++ function that has
all of this stuff written to it already. I could just call PatchGame
like this, if I felt like it. Maybe with a couple
of extra checks to make sure that the
manifest is up to date and that other conditions
are met and we're all safe. But just to make this sample
easy to play around with, that's why it's all this
Blueprint implementable or Blueprint assignable
kind of model. So let's see here. So next InItPatching,
OnPatchVersionResponse, OnPatchReady. I went over ShutDown. So the Patch Game function
is what gets called next. Just right out of here,
based on whether or not OnPatchReady succeeded. And that is where we
demonstrate downloading files for the first time here,
for this demonstration. Patch Game is
basically a function that we have written to grab
the first batch of files that we need in order to
do anything with the game. In this case, it is
grabbing information from Chunk Download list-- which again is here under
the Chunk Download list that I've defined
inside of Blueprint. And we have chunks
1,001 5,001, and 5,002. That is the map that we
are going to go into. Not the loader map, but rather
the Blueprint office map. So you are here with 1,001. And that is the user
interface that we've got set up for each of
the different variants. And so, those are
the prerequisite files that it needs to download
and mount before we can move on to the next scene. And all this really is doing-- we obviously check to see if
the manifest is up to date. We have this kind of
safety check in place. We use Chunk Downloader
GetChecked in order to get a reference
to Chunk Downloader. Again, it is a Shared
Reference Pointer, so you don't need to
cache a reference to this. And then, just for each and
every one of these in the list-- well, we actually
call MountChunks, which is the function that
you call in order to mount every chunk in one big T array. Or rather, is it a T array? Is it actually a T array? Let me double check that. It is a T array. Cool. This is the function that
you call in order to-- getting a little
turned around here-- in order to process
all of the chunks in one big list of integers. And this is the way that it
references chunks by the ID number that is assigned to it. There are two functions of note
here for this kind of process. There is DownloadChunks,
which all it does is download all of
the chunks in the list and put them on your hard drive. And then there is
MountChunks, which will look to see if
you already have them. And if they are available,
it will mount them. If they are not available,
it will automatically call DownloadChunks and try to
download those chunks for you. It can seem like
a really good idea to just use mount
chunks everywhere, but a lot of the time, if
you want to preload stuff, or if you want to
think cleverly about it and kind of express
some forethought, DownloadChunks is the
way to go in order to make sure that
the user is prepared with the chunks already
available on their device. And again, this is starting
up an asynchronous process for downloading
these chunks, so it uses this
MountComplete callback, which we have written inside
of this Lambda function here. And it is simply calling
this OnPatchComplete delegate when it is finished to let
us know that this big master patch that we're asking it
to do with this whole list of downloads is finished. And there is exactly one place
that we are calling this, and that is inside of
the PatchingGameMode that runs on startup before
we jump into gameplay. And I will show you what I mean
by that in a couple of minutes. This is a lot of long waits
for payoffs, so I do apologize. But I feel it's important to
do the walkthrough of what conceptually is happening
in the code, then show it. So the other implementation
that is useful to know about is downloading single
files at a time. So that is why we have this
DownloadSingleChunk function declared exposing the
functionality for grabbing a single chunk at a time. And the only difference
here is that instead of calling MountChunks, we are
calling MountChunk, singular. And it only takes one
ID and does one file. Again, there is a
DownloadChunk function that does just the download
without the mounting. Important thing of
note that I might have glossed over earlier. So there are two steps to this. There's downloading the
chunk to your device and sticking it in the cache
resources folder for your game. And then there's
mounting the chunk, which is what makes it available. Mounting the chunk basically
it's what it sounds like. It mounts the chunk in memory. It takes this pack
file and it puts it in active memory for you so that
all of its files are available. As though you were using
the content browser inside of the editor, but not exactly. That is another reason why
it is important to express forethought in how you
organize your chunks, because you could just as easily
end up making a two gigabyte chunk file that just
takes up a ton of space once it's mounted in memory. Whereas, if you use a
bunch of smaller ones, you could manage
things maybe a little bit more efficiently and a
little bit more piecemeal. Just something to think about
when you are thinking ahead. But this also calls a
MountComplete callback, which is another Lambda. And it has its own
OnSingleChunkPatchComplete delegate that it fires
off when it's finished. And that very simply is
how you ask Chunk Downloader to download files for you. It is a little bit
chunky to write out. A little verbose, so to speak,
when you are writing it in code like this, because there's all
these levels of error handling that you're doing
before you get into it. And there's this need to do
these delegates and callbacks to respond when you have
finished downloads and mounting jobs. But generally speaking,
it is a very intuitive way to handle ChunkDownloads. Now that we've got these
functions written up, we have this method
exposed to Blueprint to doing really simple
ChunkDownload requests. So one last thing I
want to do before I jump into the Blueprint
side and show you a look at some of the
things going on there and show you how
this works live, I do want to call attention to
this function GetPatchStatus. Basically, it is retrieving
the loading stats with the Chunk Downloader
GetLoadingStats function and outputting it to
a special struct that converts the download size
of our files into megabytes. So normally, the information
that Chunk Downloader is tracking for download
progress is stored as UN64. Which in Blueprint,
you can't access UN64. So it's not something you can
display very easily to users. So we just have this
secondary, alternate version of that struct, that display
that we are converting that information to megabytes
to kind of make it smaller and quantize it. And then we are setting
it to an int 32 so that it is something that
Blueprint can actually read and so we're not as likely to
get overflow issues when we are displaying this information. But basically to get
the stats, all you do is you call
GetLoadingStats and it will tell you what the current
progress of downloads is. There is also a
BeginLoadingMode function that you can use to set
up special functionality for a loading screen. This demonstration kind of
skips that implementation in favor of just using
this GetPatchStatus thing to get that information. But BeginLoadingMode will start
a mode where it is continuously outputting information
through a callback, and then you can use
that to track information until the download is completed. And then otherwise, another
handy little utility function here IsChunkLoaded. That simply is checking
GetChunkStatus. GetChunkStatus will actually
output an enum for you, telling you the exact
status of the chunk. So we have Mounted for if
it is loaded in RAM, Cached where it is downloaded and
available on your hard disk, but not mounted yet. We have a DownloadInProgress. We have Partial, where you
have partially downloaded it. You have this incomplete
file that is not currently being downloaded. And then, statuses
for whether or not you actually know whether
the status is unknown and whether or not
there is actually a-- whether it's not on
your disk at all, but it is remotely available. So it's a useful
utility function here. We are simplifying
this simply to whether or not the chunk is
loaded, which is being used. Is it used anywhere in C++? I don't know that it's
actually used in the sample, but it's just a handy
thing to call attention to in terms of utilities
for checking out what the status on your patch is. So now, finally,
why don't I actually run this after all this
time talking about it. VICTOR: Yeah, let's see it. MICHAEL: --And show
you Chunk Downloader in action. Before I do this, I'm going
to go to the content folder and I'm going to delete all of
these PAK files here. Because, if you leave those
alone, it will detect them and it will try to load them up. So you don't really
want to do that. I also don't need this
BuiltManifest file that I put together
just to show you how to write a manifest file. I'm going to go into saved,
go to the persistent download directory, and delete these. I'm actually just going
to delete all of this, just to make sure we have
a clean start up on this. And I'm going to
run PatcherDemo.exe. I have to move the window here. OK, so this is the UI. When I click this
button, it is going to call the PatchGame function
with Variant A as my String. Now that it has
begun patching, it displays the loading bar,
which is using GetPatchedStatus to output the
information on tick to this user interface
as it downloads these megabytes of files. It should be getting
5,001 and 10,001, which is a level and a user interface. And now, we see UI
Variant A loaded up. And if I hop outside
of this and go to the saved persistent
download directory, you can see 5,001 and
1,001, just as I predicted. And we can look at
the local manifest, this is all of the stuff
that it has downloaded and you can see it
all listed here. And then we have the
CachedBuildManifest, which is the version that
it downloaded from our site from Variant A. And
this is what it's going to check against the next
time we try to do a download. It is going to use this
cached manifest file to determine whether or
not the file that we're trying to download is valid. Now, we have these
mysterious actors. What could these be, Victor? VICTOR: I don't
know, perhaps an on demand Chunk Downloader interface? MICHAEL: Yes,
as a matter of fact, that is exactly what these are. These actors have been set up to
basically act as placeholders, and they have a ChunkID and an
Actor class assigned to them. I'll talk about how to reference
things that are delivered in pack files in a minute. But basically, the Actor
class that it's referencing is contained inside
of that pack. And when it finishes
downloading, it will spawn that Actor and
get rid of the placeholder. So I hit E to download. And there we go, it
spawns this bear. And that is the actor that
was contained inside of it. If I hit this here, it
gives us the progress bar, gives us the troll. And I will check
this one as well. This is a much bigger file,
so we can see it in action. I believe this is
Dekker from Paragon that it should be getting. One caveat for people who
wind up looking at the sample, I know that there are a couple
of problems that come up if you try to start
multiple on-demand downloads at the same time. You should, in good
practice, probably be queuing them up
instead of trying to run them concurrently. I'm not sure what
the nature of that is, it's a heads up
that Sebastian gave me, and I am simply
heeding his words. And I have had a crash
once or twice trying to do it in this implementation. In a live game, you
would do a lot more error checking and a lot more-- you'd implement
more safety features in order to make sure that you
don't get your wires crossed when downloads are in flight. But you can see, it
loaded up Dekker, and she is now in our level and
available for the game to use. And if we hop back into our
persistent download directory-- and this is just in the saved
directory of our build output-- we can see that chunks 2,002,
2,004, and 2,005 have all been downloaded onto this device. Now if I hit Alt-F4
and close this out, I will actually do another
quick little demonstration. This is the wrong folder,
because I am a fool and closed the window. Show in Explorer. Builds. Pack cache, here we are. I will go ahead and
delete all of this again. And I will run it a second time. I hit Alt-F4 instead of
bringing up the console and resizing the window. And I will hit Variant B. Now,
it is patching once again. It is loading a
different user interface. And let's have a look at
the download directory and see what's going on here. So there it is, finished getting
pakchunk5001 and that is now available. Should it be 5001? I think so. Unless I did something
stupid with the manifest. I did something stupid with
the manifest, didn't I? Yes, I think I did something
stupid with the manifest because it's supposed to 5,002. Let us do a little bit
of debugging here, then. Hop over to PatcherLive, Variant
B, and it is listing 5,002. Interesting. 5,002 is present, OK. But the CachedBuildManifest
is listing 5,001 still. So let's go ahead
and delete those. They should be getting
cleaned up when I call for the new manifest. So it's a little bit strange
that that's going on, but it might be because
of the goofy way that I've got the pack files
organized, or something like that. It could be that
maybe you should have both files in the manifest
anyway instead of trying to-- I think I know of a way that I
can make this a little smarter. Hop into PatcherLive,
grab these out of here. take that BuildID. Maybe it's because they've
got the same BuildID is what I'm guessing. Let's give that a try
and see if that works. I am learning right alongside
all of you guys here today. And then we need to
go back to PatcherLive and stick these
manifests back in. So I know that it
will work based on deleting that manifest, let's
see if renaming this manifest's BuildID field is going to
make Chunk Downloader wake up. Is this the same folder? It's the same folder. OK. I've been jumping in and out
of these folders all day, so there are so
many of these open. All right, now let's
hop back to the exe. And I will hit Variant
A. OK, so it is actually expecting that to match. PatcherLive has the
DeploymentName, I believe. So let's go ahead and
put that back the way that it was and I will simply
do a little more research on the correct way to
format this kind of thing. I tried to get a
little too fancy here, so it's doing a couple
of silly things. Get rid of Variant B,
now that will correspond to the DeploymentNames
and everything that I'm actually using. Hit Variant A, and now
it's working again. A little bit of live
debugging and experimenting. VICTOR: It's always
good, you can learn quite a bit from seeing how someone
approaches a problem. MICHAEL: And I will
just go ahead and hit Alt-F4 in the middle of this, go to the
persistent download directory, and you'll see Chunk 10,001 is
not present because it never finished downloading. It basically cleaned
itself up, I believe. Or rather, Chunk Downloader
has some features to clean that sort of thing up. But I will go ahead and
run it one more time. Actually make sure
that I clean up the manifests that I've
got here as well, why not. And this should be a
proper demonstration of using Variant B. VICTOR: You want to
take a question while we wait for it to download? MICHAEL: Yes. VICTOR: Let's
see, ekiander asked, "Manually
making manifests seems very labor intensive. HTTP ChunkInstaller
makes those automatically, or at least setting build
HTML download does that. MICHAEL: Yes. I don't know if we've got a
way to automate that process built-in anywhere. But it would be-- There's Variant B. That's what
I would expect it to look like. Sorry about that. So, I believe that Battle
Breakers had their own utility for compiling manifests
based on the pack file-- you could write a batch
file, I think, that could do that for you. Or you could write any
kind of standalone utility to do that sort of thing. But I don't think
that there's anything built into the Editor that's
going to output the manifest for you just yet. But that is something that we
are taking into consideration. VICTOR: Cool, yeah. Was there more
presentation, Mike, or should we continue
with questions? MICHAEL: There is
more to the presentation, believe it or not. VICTOR: I figured. MICHAEL:
There's a lot of stuff happening to make this
incredibly simple demonstration work. OK. So to show a couple
of the things that were happening in the back--
why is this highlighted in black the way it is? To show a couple
of the things that were happening in the background
while this was running-- So OnPatchReady runs. That runs PatchGame. And then that will call
the ShowPatch function inside of the user interface,
which all that does is it shows the
visibility and it will update based
on the PatchStatus that you feed in
through UpdatePatch. And that is being called through
probably the tick function. Yep, on tick, it will
check if it's patching, and it will call
UpdatePatch on the UI and feed it the PatchStatus. So that is how it's
showing the progress bar for the initial patch. And again, all that
is this list of chunks that it's trying
to download based on this list in this
initial set up here, in the GameInstance class. And then, for the
user interface, the way that it's displaying
that user interface, it is actually caching it,
so we call PatchVariant from that user
interface and feed it the String for
the variant name, but we're also feeding
it to the UI class. And rather than being
a hard class reference, it is a soft class reference. Those are distinguishable by
the soft pink color here instead of the purple color here. And likewise, the startup
level for Blueprint Office is a soft object reference,
which we can find here at this OpenLevel function that
gets called once everything is finished loading. Let me see if I can find
the actual references, OK. So when you are separating
things out into pack files like that, you want
to use soft references to classes and objects. Because basically, you
don't have a guarantee that they are going
to be present. And soft object pointers
and soft class pointers give you a chance
to do error handling when you try to resolve
the reference to them. OpenLevelByObjectReference
is a class that just opens up levels like that. And then UI variant is
not handled in here. What happens is
in the character-- let me see if I can find
it, it should be under-- is it under Blueprint? That is the first
person character pawn. That's not the one I want. I want the third person pawn. There it is. This is actually implemented
in the third person pawn, I believe. On Posses I think. Yes. So on Posses, it reaches into
the PatchingDemo GameInstance, grabs this cached UI variant
that we loaded from the pack that we downloaded. We do an async load for it. And then once it is
finished loading, we can create the widget
and add it to the Blueprint. These are the extra
steps that you add for resolving
this class reference. And likewise, for soft
object references-- let me see if I can
make a new soft object. You can use ResolveSoftReference
if the Actor is available. If the object
reference is actually present on your device, then
it could actually resolve this into a hard reference. But otherwise, it
will give you a chance to check if this
is valid or not. We do have an Is Valid
Soft Object Reference function for taking care of that. That is the safety
feature that you need to implement any
time you are referring to that sort of thing. And we can see this in action
in the DownloadablePost object, which has a lot of stuff for
showing the UI when you get close, letting you hit the
Use button to activate it, and all of that stuff. It has its own
widget component that is attached above it that is
used for outputting progress. And that just updates
on tick the same way that the PatchStatus updates
in the start-up screen. No, not the way. This is the start-up screen. I'm sorry, I'm looking
at the wrong thing. That's very silly. Get Patch Status. OK, it is actually doing
something similar here. Yep, it is just the same thing. OK, it is the same thing,
I'm not going crazy. Maybe a little crazy. But the main feature
here is Start Download, where it gets the
download progress set up on that progress bar. And it will run the
Spawn and Replace function when it finishes a download. And it's just
doing an async load class, similar to how we
were doing it for the widget. But after you cast it
to the Actor class, you can actually spot it. So that is basically
how you set up this kind of thing, at least
a really simple implementation of this kind of thing. Another thing that was really
cool in Battle Breaker's implementation was the asset
manager had a default asset setup where if you
tried to fetch an asset and it wasn't available or it
wasn't finished downloading, it could display
like a question mark texture instead of a character's
portrait or things like that. So that is a nice best
practice that you could follow for that sort of thing. In a nutshell, that is a
dirt simple implementation of Chunk Downloader, or
at least with a couple of bells and whistles
that you would commonly use in a game of your own. If you wanted to expand
on this, then customizing the asset manager would be a
really good idea so that you can manage the different
download phases, you could add some metadata to
your own custom primary asset label classes and use that to
resolve like parent chunks, or even assign the
phases for downloading. If you want to have on demand
assets versus first time user experience assets, versus
title screen assets, you could add that
kind of metadata to it and use that to fetch entire
lists of chunks like that. There's all kinds of ways
that you can use this in order to manage your downloads. And there are still many other
features in Chunk Downloader that I have not covered in
terms of its cleanup, error handling, and things like that. But you can delve into-- basically, just look
at the API reference and it is actually pretty
reasonably well documented in terms of how the code
comments are set up. So the API reference has
some good information for it. Now, I guess I
will open the floor to Victor and more questions,
if I can answer them. VICTOR: For sure. We got a couple. There were a couple of
questions in regards to the documentation. I believe it is
the patching site. And that is all
up to date, right, with some of the information
you're talking about today? MICHAEL: It
should all be up to date. I know that we had a few
formatting issues that we needed to iron out this week. So there might be some
updates that have not yet been pushed that I corrected
between yesterday and the day before, basically. I do actually want to do
another pass at those tutorials. Because since diving into
this in depth for the stream, I've actually gotten
a clearer idea of how a lot of
these conventions are supposed to work. And some of the
naming conventions that we picked for the remote
website, and the deployment name, and the build ID, and
stuff like that, some of those could be more intuitively
named, I think. But yeah, it should be
reasonably up to date, barring any
lingering issues that have not been caught
in the updates in the last couple of days. VICTOR: saxhack
had a question. "Does info in the manifest
have to be separated by tab?" MICHAEL: Yes. It has to be tab separated. In fact, that was a formatting
error that we caught-- whoops-- in the sample stuff. This version String
had a space where it was just ver- space, and
then the number for the ChunkID. It should be a tab. VICTOR: Sorry, I was muted. edfedermeyer
asked, "If we limit the number of
concurrent streams, is there any guarantee for the
order they will be downloaded? For example, if we really
wanted the F to UE download to start downloading
for sure as the first one, and other stuff in
whatever order." MICHAEL:
Interesting question that I think will require a
more in-depth answer than I am prepared to give right now. I'm not sure how it is managing
the concurrent download streams under the hood, is the thing. I know that ideally, if you tell
it to start a download stream-- like whichever one you start
up first should probably take precedence. So it should just be a
matter of controlling which list you want to download first
by ordering it to download that one first, and maybe
using a set of booleans or callbacks in order to-- in fact, I'm remembering from
looking at the implementation now. Yeah, the way that I think
Battle Breakers handled this, they would have a
set of callbacks for each of those
different phases and they would call
them one at a time and wait for each one to finish
before starting the next one. I think, at least, that
that's how they handled it. But maybe I'm misremembering. That is the ultra, ultra
safe way to handle it. I think the concurrent
download streams probably refers to how many
individual pack files you're allowed to have in-flight. But maybe don't
quote me on that. Let me get back to you with
more precise information about what's happening
under the hood and how you can
expect it to work if you try to cue more than
one set of downloads at a time. But I think that the
likely intended usage is for you to hold off
until one list of things is fully downloaded. VICTOR: We can follow up
on the formal announcement post thread. MICHAEL: Yeah VICTOR: If we're
able to get some more information on that. m_9_ez asked,
"Is it smart enough to resume partial
downloads, or does it restart from the beginning?" MICHAEL: It'll
resume partial downloads if the manifest
matches, basically. It will cache the
partial download and try to keep it
around until you-- so I'm not sure
where it actually caches partial downloads. I know it's not in the final
Persistent Download Directory here. But I do remember that it keeps
the partial download around until the next time you try
to run the same download, as long as there isn't
a change to the manifest and everything is copacetic with
what the remote website says, it should be fine. In fact, I think
I remember seeing that while we were playing
around with some of these files here. Yeah, I remember
quitting out of this and then jumping
back in and finding these resuming their downloads. VICTOR: DarknessFX affects asks, "Is there
a security feature to block users to replace
the local manifest and chunk pack to
add hacked content?" MICHAEL: Hmm. So, security features are
not my personal forte. That is something I'm going
to have to get back to you on. VICTOR: It's
a good question, though, because it seems like-- MICHAEL: It's really-- I've been wondering
about it too, actually. I was half expecting
to get that question. So a lot of security
features I know are going to depend on how
your website is set up. This local website that I've got
is obviously not a very secure implementation. You would expect there to be
user accounts verification involved before users can
get at that sort of thing. And you might expect some
kind of encryption key in order to access those kinds
of files and download them. In terms of the local
files, I don't know. That's not my area of expertise. VICTOR: Chris Moerland asked, "What would be the process
for de-installing packs?" MICHAEL: For
uninstalling packs. So that is a very
interesting-- so when you get a new copy
of the manifest, if it does not match the old
manifest based on the BuildID that you are using, it will get
rid of files that do not match. And it will also
try to get rid of-- let me see-- let me grab
Chunk Downloaders functions here. Cache build, update build. OK, FlushCache will get rid of
anything that is not currently in the process of
being downloaded and anything that is
not actively mounted. Is that a public function? Yes, it is a public
function, so you can call it. And ValidateCache will
check their version hash, which I think-- well, I probably
shouldn't make guesses about how that is
working under the hood. But yeah, these
are the two methods you would mainly use if
you wanted to manually get rid of chunks that are not
needed on the user's device. FlushCache is going
to be fairly drastic. That's going to
probably clear out literally everything that
is not currently mounted and just clean the whole thing. ValidateCache is
going to get rid of anything that
is not up to date and not expected to be there. VICTOR: edfedermeyer had another question. "In the web
documentation example, there seem to be a lot of
issues with N32 versus N64 and parameter order in that
Get Loading Process function." Are you familiar
with that, Mike? MICHAEL: Let me see. So that's probably
the Quickstart guide, is that correct? N32 versus N64. I think I know exactly what
that's talking about, in fact. Yeah, this isn't up to date yet. This is another formatting
error that we fixed. This dollar sign should not
be here in the manifest file. This is supposed to be a
code title bar, similar to-- is this just-- maybe I
need to clean my cache out, because I remember
seeing this all. That's supposed to be a code
title bar that looks like this. That's disambiguating
what file you're in. But something went
wrong in formatting and the new update
to this page has not been pushed to
the live site yet. I will apologize for that. But the main thing that
you need to fix in order to get that N64
error to go away is you need to make sure
this is tab-separated, you need to get rid of
this dollar sign here, and then this will be a
correctly formatted version of a manifest file. Apologies for that. And I'd hoped to have that
update pushed by the time we were doing the stream. Alas. VICTOR: Alas. And with that said, I think
those were all the questions that we're going to take today. So Mike, thank you so much for
coming on and talking to us about the Chunk Downloader. MICHAEL: I hope
that was all helpful and I hope that was
easy enough to follow, because that was a lot. VICTOR: Quite a bit. Well, we can do a little update
on the formal announcement thread when the documentation
has been updated. We are also going to
upload the project for you to take a look at, give
or take a day, until we have that up for you. And then you can break
it down and take a look at all the assets that Michael
was working on here today. With that said, I do want to let
everyone know that next week we are going to have On
Point on the stream. They are not a game
studio, not a movie studio. They do live virtual theater
and they will actually be showing off a bit of
the workflow of how they're able to take actors
in mocap suits as well as a tracked
virtual camera and produce a live virtual
theater using Unreal Engine. We've also been
working on making sure the quality of that
stream is top notch, so I'm super excited about that. So tune in for that. Yeah, they're
actually going to have live actors doing a
little performance for us, which is pretty cool. That said, make sure that follow
us on social media for all news related to Unreal Engine. Twitter, Facebook,
all the places. There's plenty of them. We're still looking for
new countdown videos, in case you've been watching
us from the beginning today. Countdown videos are usually
30 minutes of development that you fast forward
up to five minutes and then send that to us to
community@unrealengine.com with your logo separate and we
will composite the countdown as well as your logo on the video
so we can promote he project. Yeah, with that said, I think,
Mike, thank you so much again. Is there anything else you want
to chat with before we sign off today? MICHAEL: I love you
all, thank you for showing up. This is a wonderful
opportunity and I'm honored to be here
describing the technology that the Battle
Breakers team originally put together for you. Thank you. VICTOR: Exciting. With that said, I wish you
all to stay safe out there. We'll see you again next week. Take care, everyone.