DelayLoad Overview

General Notes

The delay load plugin allows for the injection of IMML into a scene at runtime. This offers numerous benefits:

  • The original IMML scene can be very lightweight and fast loading, allowing for things like progress bars and simple functionality to begin very quickly.
  • The users connection to the server can be established quickly, making it clear to others when users are entering the world. The basic chat functionality is also very lightweight and loads quickly, allowing users still at the load screen to take part in the in-world chat.
  • Functionality can be created to be usable in multiple parks and deployments. This not only allows for simpler scene and deployment management, but reduces the development time of new parks utilizing pre-made functionality. This also allows for the automated distribution of bug fixes and updates to all parks utilizing this functionality.
  • Find and replace methods can also be performed on directives, allowing the same IMML file to be loaded multiple times without problems with duplicate element names and scene variable clashes.
  • Functionality which utilities assets of large file size can be lazy loaded, allowing the user into the environment before all the functionality is ready.

Plugin Functionality

The plugin in the IMML does not require any parameters, and will do nothing until a directive is started.

Directives are created in a Lua script, and find and replace operations can be performed on these directives before being started. A script can also be specified to execute once the document has completed loading into the scene. If no script should be executed, this should be defined as nil.

Example:

mydirective = delayplugin:createdirective('http://example.com/my-imml-functionality.imml', 'CompletionScript')
mydirective:findreplace('Search String', 'Replace String')
mydirective:start()

Available properties for monitoring the state of the plugin are:

delayplugin.downloadrate  -- The download rate. There are some uncertainties about how accurate this value is.
delayplugin.bytesremaining  -- The bytes remaining to download.
delayplugin.downloads  -- The number of active directives.

Additional properties can also be checked regarding the state of the directive. These are:

mydirective.state  -- This returns the state of the directive; acquiring imml, acquiring elements or completed.
mydirective.loadedelements  -- The number of elements which have completed being added to the scene.
mydirective.totalelements  -- The total number of elements to be added by this directive.
mydirective.progress  -- The current directive progress percentage.

Usage Tips

There's a few things which need to be taken into account when creating IMML and plugins to be used in a delay loaded park:

Network Connection

The connection with the server is typically established long before most of the functionality has loaded. This means any communication being performed when a "NetworkConnectionEstablished" event is triggered will likely be received by new clients before having the functionality to handle it. It also means the newly loaded functionality will not catch this event, as it only occurs once.

In the generation 1 templates, information is sent to newly connected clients from the server on a "NetworkConnectionEstablished" event, including where to spawn their avatar, etc. In the new delay loaded functionality, a request is now made from clients to the server once the functionality has loaded, and the server responds.

Similar methods are utilized for the Office Plugin and Avatar Plugin, both notifying the server / other clients when they are ready to begin receiving information.

Network Enabled Elements

This is a two part issue:

Firstly, network enabled elements are sent from the server to the client once connection is established. This is important to note for network enabled elements containing things like "Loaded" and "Unloaded" triggers, since they will likely load before the client has the scripts these triggers attempt to fire, or any plugins monitoring element addition.

This can be seen when entering a delay loaded park and other users are present. You will likely see a list of errors related to avatar load triggers attempting to fire scripts which do not exist.

While the errors do no harm, it's generally important that these scripts fire on these elements, since they're obviously needed to do something useful. (In the case of avatars it's quite important, displaying the avatars in the scene!)

In the above example, this issue is solved by querying the scene for all models, and determining which models are avatars and passing these models into the load script. This occurs once the functionality becomes available to handle this. From then on, the load triggers behave as normal.

The second issue regarding network enabled elements is the creation of network enabled elements. Something like a chair node, used by the network mutex to determine whether the chair is unoccupied and available for seating, must exist in the scene. However, it must only be added to the scene once, as they have no network owner and will persist as long as the park remains online.

To avoid every user connecting to the park creating additional chair nodes each time, a request is made to the server to determine whether the chair nodes have been generated in the past. If no chair nodes have been created, the client is instructed to generate these nodes via a Lua script, and will notify the server once the process is complete so no other users will be instructed to create these objects. In this way, the first user into an environment will add the chair nodes, and all other users will use these nodes from then on.

Functionality Dependencies

One of the biggest benefits to the delay load functionality is how reusable IMML functionality becomes. However this does present some issues which must be kept in mind when creating the functionality.

It's typical for one piece of functionality to interact with another. Zooming on a screen may disable a toolbar button which is no longer of use, for example. However, since the screen IMML is designed to be used in many parks, some which may not contain this toolbar button, it needs to be smart enough to determine whether it needs to perform this action.

One of the simplest ways to check whether an element exists, and hence make some assumptions about whether any action should be taken, is using scene:containsname(). For example:

if(scene:containsname('TheButton')) then  -- First check if the button exists in the scene
   thebutton = scene:getelement('TheButton')
   thebutton.visible = false
end

Load Order

The above is also applicable to load order. Rarely will multiple directives started at the same time load in the same order twice.

Naming Conventions

While element naming conventions have always been important in scene management (Particularly as IMML files can grow into tens of thousands of lines) it's particularly important with delay loaded IMML, since element naming should always be independent. This is also particularly true when using findreplace() to load a single IMML file into the scene multiple times.