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


#1

Intro

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 an 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 arent available… Unless you shim them through a Web window, but thats 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 alot of adjustment.

Javascript

Javascript is a fairly easy to get into but hard to master 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…

Alot of the logic that apply in Javascript, can be brought perfectly fine into High Fidelity. So you may use alot 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 upto the prototype chain tutorials, as long as your 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 a objects to result in graphics

Javascript in High Fidelity

For now, Qt framework, which High Fidelity runs on top of only support ECMAScript 5 standard. Some ECMAScript 2015-2017 stuff is comming in the 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/AvatarManager, MyAvatar: 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 thats 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

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 effect 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 alll 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 abit 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 an Puppet Master for NPC are recognized as if actual avatars, or Game Logic you want 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

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 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

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 a 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:

(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.

You can also use shorthand, anonymous object form

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

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

Forexample.
Lets say MyObject has a custom callback called “customMethod” with some parameters.

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

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 customMethod.
handControllerGrab.js is an example using Entities.callEntityMethod.

There are alot of other things available here, but alot 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

3D Math in JS?

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

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

example.js

module.exports = function () {
  console.log("refered");
}

main.js

var external = Script.require(Script.resolvePath("example.js"));
external();

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

var json = Script.require(Script.resolvePath("filename.json"));

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

var json = Script.require(url);

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


Entity / Client script Hydridization

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

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

EntityScript.js

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 )

Main Client Script:

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

[...]
Entities.addEntity({
[...]
      scriptUrl: animationPreloader,
[...]
}

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

Messages System

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.

// 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.

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

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


Scripting Getting Started 2017
Q How democratic do you want highfidelity to be?
How to get started creating props?
Server Script Issues
Inworld computer
Javascript Office Hours 8/3/2016 11AM
Scripting Simple change color random (Difference between Client and Server scripts)
Same entity script on multiple entities
A helpful script for Unity developers!
Having issues with a server script that edits entity velocity
New developer -- I want to write quests!
Help with pickRay origin position
How do I read remote JSON file in script?
#2

First off; well done @Menithal.

To the rest of you: if this topic doesn’t get pinned, you’d better bookmark it on your own.

Favorite quote:

He’s not joking here…but I laughed so hard cause it’s so true. Scripting can at times make you want to throw your head through a window. Fortunately, there are simple tutorials on W3 Schools and Codeacademy.com that will help you learn the fundamentals.

In similar fashion to the Caledonian and Builders Brewery, I’m going to attempt to get guest speakers ((Alphas, and Developers)) to give a simple lecture on-board @open once a week if possible. I’ll see about getting that listed on the calendar. @Jess


#3

Oh if I can only say the number of times a missing curly brace, misplaced comma, or a missing paranthesis somewhere in a 1000+ line javascript has made me rage


#4

That;s not only a javascript problem. That happens with every programming language. Only ones with a good IDE, like pascal, delphi, C# tell you quicker what is wrong. And LSL and Javascript are harder to debug.


#5

Compilers tend to catch them in other programming languages well, identifying everything from the line to the type of issue; Then the errors are fairly decent…
… With Javascript the errors… Are not very useful, especially if you have multiple callbacks where the
references have been lost. and I am speaking from Experience, Having worked both Compiled languages and Run-time Scripts


#6

One reason why i always hated javascript on webpages. it’s terrible to write.


#7

PM me when you’ve got the details nailed down, and we’ll get it on the calendar!


#8

Re JavaScript errors, JSLint is your friend. And for book-inclined (esp. if not wanting tutorials) O’Reilly has a good set, starting with the classic, “JavaScript: The Good Parts”.


#9

Yep, JS Lint has been a life saver, although there are cases where even that wont find the issue though.

O’Reillys Good Parts is a really good source definitely!


#10

there’s an .eslintconfig file that is hifi compatible in the root directory of the github repository https://github.com/highfidelity/hifi/blob/master/.eslintrc.js

eslint is like jslint except not you’re not stuck w/crockford – named rules and you can add your own


#11

Thanks for trying but as an ex programmer of DAP16 Assembler, COBOL, C, and ISQL, SQL+, PL/SQL, DB Admin & COGNOS – I’ll just stick to LSL thanks. (plus TYPELESS languages are a step backwards, amongst other things).


#12

whoever is organizing javascript office hours, get in touch with me at james@highfidelity.io – i’m down to put something regular on the calendar. i love javascript!!

cheers,
james


#13

sent! and 20 characters…


#14

I think there are quite a few people who would appreciate something along the lines of “LSL to JavaScript”, as many scripters learned their programming with LSL and are thus hard wired now to think along those lines.


#15

Sorry to say that other than the smallest snippets of code, the architectural distance between the SL scripting run time environment and the HF scripting runtime environment would make such conversions practically infeasible. It is not about LSL vs JavaScript. It is all about code running server-side right next to the objects, directly accessing properties of objects versus code running client-side and multiple instances too, where access to entity (object) properties are asynchronous remote accesses. The closest analogy is having to write your LSL script solely using llMessage or dataserver calls to make changes and having to use an event to let you know when the change completed.


#16

I need to update this guide due to a recent change from last week.

Server side scripts were silently introduced that allow you to create scripts that are attached to an entity that only run on the server. Without having to create a separate assignment client script.

It makes some scripts work closer to an LSL, but not exact as the script is still not compiled to the .

However you are correct in regards of SL and the regular entity scripts.


#17

yes, that is an important improvement (I heard about it). It permits observer-less actions, although I do not know yet if physics was also rolled into the server. But in any case, the run-time is still quite different than LSL run-time (again not about language used but all about the relationship between modification of properties and user data.


#18

Ay, physics will still be client side: so while you can set the velocity values and the sort, it wouldnt get simulated unless there was a client doing the simulation.


#19

Low Fidelity. It’s simply amazing.
With some hope may this deficiency get fixed sometime this year.


#20

This is a wonderful starting point. Can I suggest to High Fidelity VIPs, in particular to @philiprosedale and or some evangelists interested in spreading HF a bit more, to try to collect these guides in a unique documentation point with VALID links and a real progression in simple scripts sequence as honest tutorials in building up some solutions?
Also this is not complete, I think there are still other chapters of this mid-level explanation, did @Menithal write many other chapters of this book? :slight_smile:

Regarding the lsl example maybe you cannot do a 1:1 equation, I agree, but you might follow a sequence of sample scripts like:

  1. a greeter giving written information and clickable links to people when they arrive or click on it
  2. a visitor counter for statistical purposes
  3. a guestbook where people can leave written messages
  4. a working chat (yes chat is REALLY needed especially by people not speaking English to write down simple names addresses etc,
  5. an example of communication between client entity script and server entity script
  6. scripting something similar to edit.js even with very smaller scope (yes it is REALLY needed since current create / edit property workshow is quite awkward and very slow.
  7. Scripting to align objects and to ease the selection / parenting of objects, yes this is needed to help in doing that in a human form,
  8. Scripting to automate che changing of textures / specular / normal maps in models, this can be quite useful,
  9. Scripting to ease the setting up of zone with some predefined settings, especially haze seems quite difficult to set in armonic way,
  10. Scripting to make something similar to holodeck, i.e. change rapidly a scene without doing it manually,

… That is just a brainstorming obviously, but I think that scripting can be in HF quite important as it was in SecondLife, and so far as I see it is much optimized here than in secondlife and 1000 times better than opensim…

Also some kind of advices on a good development system, maybe based on Visual Studio Code or IntelliJ or Eclipse with a good autocompletion and help system can be essential to avoid doing too many errors. LSLEditor in secondlife for instance was such a good system, and even the internal very simple lsl edit window had some elementary helping system to understand if a function existed and which parameters and constants did allow.

Here were my 5 cents…