Preloaded JavaScript
Overview
The ScriptCompiler
and ScriptProvider
APIs are designed to optimize the performance of your game’s UI by pre-compiling JavaScript files, significantly reducing the runtime overhead associated with initial JavaScript loading and enabling faster UI load times.
- The
FCohtmlScriptCompiler
class performs the reading/compilation of your JavaScript files - it takes a file path and transforms it into a ready-to-use bundle of raw and compiled file data. - The
ICohtmlScriptProvider
interface is used to “pass” your data to Prysm. There is a default implementation of the interface, calledFCohtmlScriptProvider
(seen in the example below) which should cover most simple use-cases related to storing and modifying a cache of compiled scripts. You can also provide your custom implementation of the interface and choose what/which compiled data to pass, depending on the specific needs of your game.
How to Use
For most developers, integrating script pre-compilation can be done in a few steps, namely:
- Make sure that the
System
has been created. This can usually be done throughFCohtmlSystemHolder::GetSystem()
, but in some cases you might need to create the System yourself. - Initialize the
FCohtmlScriptCompiler
object and your preferredICohtmlScriptProvider
object - this can be the defaultFCohtmlScriptProvider
implementation or your custom one. - Let Prysm know about your
ScriptProvider
object through theICohtmlPlugin::OnGetScriptProvider
delegate. - Compile your scripts using the
ScriptCompiler
and add (i.e. cache) them inside yourScriptProvider
. At this point theScriptCompiler
object should no longer be needed and can be destroyed. - Load your HTML page - Prysm will call
ICohtmlScriptProvider::GetScriptCacheEntry
whenever a resource is requested. If compiled data is found/returned, it will skip the usual read/compile steps and use the compiled data instead. - Dispose of the cached script memory after UI has loaded or the entire
ScriptProvider
object if you prefer to.
ICohtmlPlugin::OnCohtmlLibraryBeginDestroy
delegate to ensure proper behavior, or just clear your compiled scripts immediately after your UI has loaded.Here’s a simple example of how to pre-compile JavaScript files at startup and manage them during gameplay, using the default ScriptProvider
implementation.
// The System needs to be alive for script compilation
auto System = FCohtmlSystemHolder::GetSystem();
// Initialize the ScriptCompiler
TUniquePtr<FCohtmlScriptCompiler> ScriptCompiler(new FCohtmlScriptCompiler(System));
// Initialize the provided default implementation of the ScriptProvider.
// Only used as TSharedPtr here to make it easier to capture into a lambda.
// Ideally, you should have your own lifetime management.
auto ScriptProvider = TSharedPtr<FCohtmlScriptProvider>(new FCohtmlScriptProvider);
ICohtmlPlugin::Get().OnGetScriptProvider.BindLambda([ScriptProvider]() {
return ScriptProvider.Get();
});
// This can be a file visitor, directory watcher, etc.
auto FilesToPreload = GetFiles();
for (auto& FilePath : FilesToPreload)
{
// Read and compile the script
auto CompiledData = ScriptCompiler->CompileScript(FilePath);
// Add the compiled data to the ScriptProvider cache, which will take ownership of it
ScriptProvider->AddScriptCacheEntry(FilePath, CompiledData);
}
// Prysm will try to match scripts used by "my_page.html" with ones in your cache
GetView()->LoadURL("my_page.html");
// Clear the cache when the UI has loaded
ScriptProvider->ClearScriptCache();
ClearScriptCache
and RemoveScriptCacheEntry
to free up memory when scripts are no longer needed.Advanced Usages and Custom Flows
For developers with more complex requirements, such as compiling scripts during packaging or implementing custom caching strategies, the APIs offer extensive customization options.
Modifying and preserving the script cache between runs
You can extract, modify and “set” the script cache inside the default FCohtmlScriptProvider
implementation. For example, you could compile all of your scripts during packaging, extract and save them to disk, then load and re-insert them in the cache when your game starts.
// ...
// Compile scripts and add them to your cache following the example above
// ...
auto PrecompiledScripts = ScriptProvider->ModifyCachedScripts(); // Extract the scripts from the cache.
SaveScriptsToDisk(PrecompiledScripts); // Save them to the disk or inside your game package.
// When your game starts
auto PreloadedScripts = LoadFromDisk(); // Load the scripts from disk.
ScriptProvider->SetCachedScripts(PreloadedScripts); // Insert them back in the cache.
Custom ScriptProvider Implementation
If you need to load pre-compiled scripts from disk on demand or implement your own path resolution, you can override the default ScriptProvider
behavior. The only relevant method to focus on is ICohtmlScriptProvider::GetScriptCacheEntry
. This method gets called whenever your UI requests to read a file from disk - here you can provide your prepared raw and compiled JavaScript data, or return nullptr
and let the file be read/compiled as usual. The “default” FCohtmlScriptProvider
implementation that comes with Prysm is just a cache file paths, mapped to their respective raw/compiled data. When using a custom implementation you can organize and manage your compiled files however you want - for example you might not cache them at all, but instead read them from disk or fetch them from a server, if this suits your needs better.
Here is a sample implementation of a custom ScriptProvider
:
// Implement your custom ScriptProvider
class CustomScriptProvider : public ICohtmlScriptProvider
{
public:
// Implement your custom way to match file paths to their CohtmlCompiledScriptData
// return nullptr if and entry is not found
virtual FCohtmlCompiledScriptDataPtr GetScriptCacheEntry(const FString& FilePath) override
{
return GetMyPrecompiledData(FilePath); // Load data from disk, server, etc.
}
};
// Bind the OnGetScriptProvider delegate and pass your override to Prysm
ICohtmlPlugin::Get().OnGetScriptProvider.BindLambda([]() {
return MyScriptProviderManager::GetCustomScriptProvider();
});
Additional info
For more in-depth info on JavaScript preloading, please refer to the following pages:
- API Reference for the
FCohtmlScriptProvider
class - API Reference for the
FCohtmlScriptCompiler
class - The native JavaScript preloading documentation page. The
FCohtmlScriptProvider
andFCohtmlScriptCompiler
classes serve as wrappers for the the functionality described in this page.