Scripting Primer

From FHU Wiki
Jump to: navigation, search

Beyond the Wall of Scripts: Scripting in High Fidelity - Primer for Intermediate Programmers[edit]

Originally authored by Menithal in this forum post.

Intro[edit]

I’m writing this post in order to detail some of the discoveries I’ve made while venturing into the world of scripting for High Fidelity. This is sort of a Primer for everyone who wants to start scripting in High Fidelity, but cannot make sense of the tutorials that do not explain the whys and whats.

High Fidelity scripting runs on a Javascript engine that is provided with the Qt. The semantics are not important, but for those who have Advanced Javascript experience, it is not ECMA 6 Compatible, and some of the stuff, such as Cookies, Local Storages, or Databases simply aren't available… Unless you shim them through a Web window, but that's not covered here for now.

If you do not know what the above means: I’ll simplify: Be careful on the advice you see regarding JS made for any of the modern browsers or Node.js (circa 2015+ and later), as those may not always successfully work in High Fidelity, without a lot of adjustment.

Javascript[edit]

Javascript is a fairly easy to get into but hard to master the language to try out, but has been made easier with countless frameworks like Angular, React, Express, jQuery, Vue, Ramda, etc.

Unfortunately, we do not currently do not have the luxury of using most of them, as most frameworks are built around the Web Pages, not 3D environments such as High Fidelity. So we must rely on Vanilla Javascript…

A lot of the logic that applies in Javascript, can be brought perfectly fine into High Fidelity. So you may use a lot of the tutorials available Vanilla Javascript to learn the basics. Best ones may be Mozilla’s Basics of Javascript Tutorials, and moving all the way up to the prototype chain tutorials, as long as you skip the DOM Manipulation and ECMAScript 6/7 related stuff. As an added bonus you may also check out their Tutorials regarding the Canvas object in Javascript, as many of those concepts translate here, as canvas deals with manipulating objects to result in graphics

Javascript in High Fidelity[edit]

For now, Qt framework, which High Fidelity runs on top of only support ECMAScript 5 standard. Some ECMAScript 2015-2017 stuff is coming possibly in the future with Qt 5.12,

Alot of the functionality of the Javascript normally present on the Web, with Document Object is simply replaced by a myriad of new interfaces.

Lets briefly touch three of the Interfaces, which you will most likely use the most at first:

  • Entities: Massive Utility API. Allows you to manipulate the entities around you if you have access to do so. This means you can add, remove, edit entities using this Interface. However, everyone had access to get properties of an entity and can be used to find Entities in range, direction, collision, or raytrace.
  • AvatarList/AvatarManagerMyAvatar: Allows you to get information on an Avatar, or manipulate the Client only MyAvatar. The information here will be always the avatar information of the client running the script. Note AvatarList and AvatarManager are basically the same.
  • Script: This is your weapon, this is your friend. It allows you to connect callbacks from your client to script, such as functionality that's dependent on time (Script.update, Script.setTime, Script.setInterval etc), connect paths relatively to Assets (Script.relativePath), refer to other scripts (Script.include), or create events which occur when the script is turned off (Script.scriptEnding). Use this instead of (window.)setTimeout / (window.)setInterval.

There are many other interfaces also available, but I’ll get to those later: Note that some functions are still undocumented which are referred to here.

Types of Scripts[edit]

Scripts in High Fidelity are divided into four categories:

  • Client Scripts (including Avatar Scripts)
  • Entity Scripts
  • Entity Server Scripts
  • Assignment Client Script

Client Scripts and Entity Scripts are public scripts that are run on your client, meaning your client is doing the actions logically by each script, and your client is downloading and running those scripts. Currently, both have full access to your MyAvatar instance, but this may change in the future.

However:

  • Client Scripts are run by your client and can be stopped by it. They are not bound to any entity but can still affect anything you have permission to. Avatar Scripts are simply client scripts bound to an avatar (they get removed when you change avatar) Grammar Context from the domain Visitors. ‘I’ am in control.
  • Entity Scripts are loaded by the clients that load them. This means instances of it run all the other clients connected to the same server, and should be written stateless: If you must have a state, it should be kept in the userdata field to keep it in sync with the other clients. Additionally, you have to write these a bit differently from both Client and Assignment Client Scripts. Wording Context: ‘We’ are in control ‘at the same time’
  • Assignment Client (AC) Scripts are Client Scripts run on the server side. These scripts are private and closed so that only the server has access to it. Behaviour wise, its same as the Client scripts, but it has its own Access Interfaces to do things the Client scripts cannot do such as a Puppet Master for NPC are recognized as if actual avatars, or Game Logic you want to be hidden from prying eyes. Grammar Context from the domain Visitors: ‘They’ are in control.
  • Entity Server Scripts are Entity Scripts run only on the server side. The Server does not see all Entities, only Entities it has created, or that have an Entity Server Script in them. These scripts are public and bound to an entity, but only a Server runs these. Basically, Entity Scripts run on a single machine. Do note that physics is local, so any physics settings would not get simulated if no one else is present, so any interactions of others cannot be registered by this script. Context from the domain Visitors: ‘They’ are in control.

Client Scripts[edit]

Client scripts are scripts that simply run on your client. You have full control over when these scripts are started and stopped. This can be done through your Running Scripts window. Assignment Client Scripts are similar to this, with the exception that they exclusively run on the server side and have access to their own things.

Unrelated client scripts cannot directly interact with other client scripts, but they indirectly interact via the Messenger interface. Again, an advanced topic for later, however…


We are going to 0 to a 100 within a few paragraphs…

If you still do not know much about Prototypal Javascript, turn back and read on it. Because otherwise, it will be like looking into the ark of the covenant.


Entity Scripts[edit]

As said in the brief intro, the Entity Scripts instances are run on all clients at the same time, and should not contain state as this will then be different for each client. Instead, this should be taken from and stored from a common origin.

Entity Scripts need to be defined as an anonymous function that returns a new instance of an Object, that is called every time a new instance of the object is created.

In its simplest prototypal form:

<code>(function(){
  function MyObject(){
    // Can be used a constructor
  }
   MyObject.prototype = {
       preload: function(entityId){
         // When an entity instance has been loaded into the world, this is triggered.
       }
   }
   // This is called every time a new object is created with this script
   return new MyObject()
}) // Either use this or a return at the very end of the file.
</code>

You can also use shorthand, anonymous object form

<code>(function(){
return {
  preload: function(entityId){
     console.log("loaded on ", entityId);
  }
}
})
</code>

The reason object callbacks are used is because each Entity instance can then be called, without having to resort to using the Messenger.

For example.
Let's say MyObject has a custom callback called “customMethod” with some parameters.

<code>(function(){
  function MyObject(){}
   MyObject.prototype = {
       preload: function(entityId){}, 
       customMethod: function(entityId,args){}
   }
   return new MyObject()
})
</code>

You’ve created an entity with the id 36917abe-2bbe-4f3b-8926-215bf74fa90d. You can call the method customMethod in an unrelated script via:

Entities.callEntityMethod(“36917abe-2bbe-4f3b-8926-215bf74fa90d”, “customMethod”, [“1”,“2”,“3”])

The Last List is the list of arguments that will be passed to the custom method.
handControllerGrab.js is an example using Entities.callEntityMethod.

There are a lot of other things available here, but a lot of the discussions would revolve around logic which is the same in any language, so it is just a matter of translation to JS.

Advanced Javascript: Tips and Tricks[edit]

3D Math in JS?[edit]

Now the question would appear, how do you exactly do 3D Math operations in Javascript. We cannot simply add two vector Objects together. This is where two additional Interfaces come in useful.

  • Vec3 - Interface for Vector Operations
  • Quat - Interface for Quaternion Operations

Including External Scripts and JSON[edit]

To retrieve external js/JSON file you can use require, but for it to be successful, the script referred to must export a function or an object.

example.js

<code>module.exports = function () {
  console.log("refered");
}
</code>

main.js

<code>var external = Script.require(Script.resolvePath("example.js"));
external();
</code>

You can also use this to call external JSON files, without module exports.

<code>var json = Script.require(Script.resolvePath("filename.json"));
</code>

Alternatively, you can also use this to retrieve non-relative files to retrieve both JSON and js:

<code>var json = Script.require(url);
</code>

Examples of both relative JSON and script loading can be found in Clap App


Entity / Client script Hybridization[edit]

Sometimes, you may want to share A script to work both as an Entity script inclusion, and as a Client Script at the same time. You can then use Script.include :

For example, a preloader of Entity Animations of something controlled by a Client Script:

EntityScript.js

<code>preloadAnimation = function(clientScript) {  
    // Do things if on both
    [...]
    if (clientScript!== undefined) {
        // Do Client things, we are running on client side
        return animations;
    }
   // Do things on Entities should do
    function EntityObject() {};
    EntityObject.prototype = {};
    return new EntityObject(); //Appeasing the Entity Scripts.
};

(preloadAnimation )
</code>

Main Client Script:

<code>[...] 
var preloadAnimation ;
var animationPreloader = Script.resolvePath("EntityScript.js");
Script.include(animationPreloader);
var arguments = preloadAnimation (true)

[...]
Entities.addEntity({
[...]
      scriptUrl: animationPreloader,
[...]
}
</code>

I used most of the above tricks in my marketplace creation the Tiger, of which source is available here:

Messages System[edit]

Now the Messages API is a system that allows you any script to communicate with scripts that are subscribed to the same channel. For those familiar with Second Life, this is similar Using llSay and llListen.

<code>// Creates a Function that triggers every time a message is received on a subscribed chanel
Messages.messageReceived.connect(function (channel, message, senderID) {

});

Messages.subscribe("example"); // Subscribes to a channel "example"

Messages.sendLocalMessage('example', 'message') // Sends a Message to any local script instances Listening to the example channel. All other clients running the scripts arent synced.
Messages.sendMessage('example', 'message') // Sends a Message To every script instance connected to the server Listening to the example channel. All clients are synced.

</code>

This is especially important when you have to do communication between Entity Server Scripts and Entity Scripts (for example interactions).

Will expand on this as more things are found or commented on.