Scripts allow us to make interactive and reactive content in Sansar. They are written in C# but are designed to be used and reused without scripting knowledge or even seeing the code. Once uploaded or purchased through the Sansar Store, scripts can be added to objects while editing a scene. They can then be configured through properties specified by the script creator.

The script interface to Sansar is referred to as the Script API. It is constantly evolving as new features are added for interacting with objects, users, and other scripts.

Scripts run on the scene server, where they have access to server authoritative information such as the physics system, the users in the scene, and other scripts. Through the server’s connection to the users it is also possible, with some latency, to do some limited, client-focused interactions.

Getting set up

After you install Sansar, there is a Client\ScriptApi folder inside your Sansar install directory (C:\Program Files\Sansar\ by default). This contains the technical API documentation as well as some example scripts. If you have VisualStudio, you can copy the entire ScriptApi/ directory to a more convenient place and open ScriptApi/Examples/ScriptExamples.csproj. This project is set up to understand the Sansar API and includes all the examples.

The easiest way to get started with a script is to use the SceneObjectScript base class, like this:

public class MyScript : Sansar.Simulation.SceneObjectScript{}

This script doesn't do anything- but it will compile! You can upload it by following the directions in the article Importing items to Sansar and then attach it to an object. It doesn’t do anything yet, so let’s make it say something. For this case, we are going to write to the script log, once, on initialization.

Now when we upload this script, then add it to an object and build then visit the scene, we can find this message in the script log (Ctrl+D). Lastly, for our very simple starter script we are going to make things a little neater. There are a couple of script interfaces we are almost always going to use, so adding a few ‘using’ lines at the top lets us use them without writing out the full name every time. We're adding more than strictly needed for this example but this will make it easier to extend the example for more complex code.

The full scripting API docs can be found in the Sansar install directory in Client/ScriptApi/index.html, or in the Script API Documentation section of this Knowledge Base. This is a brief overview of how the API is laid out:

Sansar.Utility contains the GenericEnumerable class used to create enumerables for other APIs.

An object oriented API

Sansar’s script API is very object oriented. This means that nearly all APIs are accessed through a specific instance of an interface. A script gets access to those API instances through the base class it extends- in our example, that is SimpleScript. Previously we just casually used Log.Write(“Hello World!”); so let’s take a little bit of a closer look at it. SimpleScript has a member property Log of type Sansar.Script.Log. This log returns the instance of the logging API for the scene. The script then calls the Write method specifically on that instance of Sansar.Script.Log. Different scripts could actually get a different instance of Sansar.Script.Log - and when multiple script owners in a scene become possible they will! For now, however, all scripts in a scene are owned by the scene owner and thus all share the same Log.

Three of the largest APIs have multiple instances based on how much access they give; some can do more things than others. Those APIs that end in Public (ScenePublic, AgentPublic, and ObjectPublic) are the least powerful as they are intended to be used “by anyone”. Those that end in Private (ScenePrivate, AgentPrivate, and ObjectPrivate) are more powerful as they offer access to more private internals. Scripts that are on objects built into the scene get access to ScenePrivate while visiting scripts would only get access to ScenePublic.

SimpleScript contains the other API access points for the script; directly itself or inherited from its base class ScriptBase. Those members and some notable APIs are:

ScenePrivate: Access to the Scene APIs and a way to find many other APIs.

FindAgent to get AgentPrivate APIs to the visitors of the scene.

FindObject to get ObjectPrivate APIs to other objects in the scene.

Chat to subscribe to chat messages and send chat messages to users and other scripts.

User to subscribe to events when users enter or exit the scene.

CreateCluster to add new objects to the scene.

ObjectPrivate: Access to APIs for the object the script is attached to:

Memory: For information on how much memory is available to scripts, how much is used, and subscribing to events related to memory use levels.

Events

A script that says Hello World in the logs isn’t exactly useful or interactive. To make a script interactive, we need it to respond to something happening: an event. An event can be many things, including a collision, someone entering the scene or leaving it, a chat message, a keyboard press, a message from another script, and more.

SimpleScript has some methods you can override to handle common events. It is also possible to set up your own subscription explicitly through a Subscribe method. Here we will be using SimpleScripts overrides, which do all the setup work for us.

Let's update the hello world script to instead greet users as they arrive in the scene. We don't need the SimpleInit any more, so we will replace it with an override of OnAddUser(AgentPrivate agent):

Now whenever a new visitor arrives in the scene, the method OnAddUser runs and writes "Hello!" to the logs.

For our greeter we want to send a chat message to the user that just logged in, welcoming them to our scene. An agent is a server-side representation of a logged in user, and it has a reference to a Client API we can use to sent them a chat message.

Once we add this script to an object in the scene, all visitors arriving in the scene are greeted by a message in their chat window welcoming them.

Editable properties

While our greeter is at least a little interactive, it isn’t very easy to customize or reuse. If we want to say a different message, we would need to modify the source code and re-upload the script. Sansar scripts show their public fields in the object properties for any object the script is on. We can use this to show a Message field that lets us change the message without recompiling or re-uploading the script.

Now after adding this script to an object in the scene and opening that object’s properties window, we will see this:

Note the Message attribute and blank text field.

If we put this script on the Sansar Store, anyone could adjust the message as they needed, and we could use the same script in different scenes with different messages without recompiling. We can make it even better with a couple of attributes. Attributes in C# are ways of attaching extra information to pieces of code. In this case we will use the [DisplayName("")] and [DefaultValue("")] attributes to give us a more meaningful name and an example message which will help make it clearer how to use the script. To apply these to our Message field we simply place them above the field.

Now looking at the properties for an object with this script will show this, which is more user friendly:

Message has become Greeter Message, and the text field is populated with a default message.

SimpleScript event overrides

SimpleScript offers several overrides to handle common event types.

OnAddUser(AgentPrivate agent) : When a user joins the scene.

OnRemoveUser(AgentInfo agentInfo): When a user leaves the scene.

OnTimer(): Automatically called 10 times a second.

OnCollision(CollisionData data): Called when the object the script is on collides with another object.

OnChat(ChatData data): Called whenever there is chat on channel 0.

OnScriptEvent(ScriptId sender, Object data): Called when scripts send script events to this script.

Since the default values on these overrides may not always be the best, SimpleScript also includes some Attributes to adjust them: change the rate of OnTimer, set what types of objects cause OnCollision, which channel to listen to for OnChat and more. It is also possible to use these attributes to create extra event handlers, by applying them to other methods that match the function list of the overrides.

Lastly there are a couple of helper methods and properties in SimpleScript worth pointing out:

RigidBodyComponent: This property will be the RigidBodyComponent of the object the script is on.

GetSubscription(string methodName): Gets an IEventSubscription for the named method, if it is subscribed to events either as an override or with an attribute. This IEventSubscription can be used to cancel the events.