Skip to content
JasXSL edited this page Nov 29, 2017 · 12 revisions

Run methods on another module

For this tutorial, you will need to have installed the jas Dialog module. Let's create our first module! A module is basically just a script, but follows a few rules:

  1. A module needs a header file that can be included by other scripts to access your module's methods.
  2. A module can be used as an object package for pseudo object oriented coding.
  3. A module has methods that can be called on it.
  4. A module can raise events.
  5. A module can set up and maintain DB3 tables.

In this example we'll create a dialog that will output "hello world" when you select the Yes option.

You should have a "#ROOT" script and the "jas Dialog" module installed. See Installing XOBJ & Setting Up a New Project

  1. Add a new script in your project linkset, name it "mfp Main". mfp stands for MyFirstProject, and for proper projects you should use your own prefix.
  2. Navigate to your "myFirstProject" or whatever you named your project folder.
  3. Create a new subfolder and name it "classes", so you'll have something like C:\LSL\myFirstProject\classes
  4. Inside that folder, create a new file and name it mfp Main.lsl
  5. Open the file in a text editor of choice (I recommend notepad++).
  6. Save the blank script for now, we'll use it later.
  7. Open up your _core.lsl file and add #include "myFirstProject/classes/mfp Main.lsl"
  8. Also add #include "xobj_core/classes/jas Dialog.lsl". Don't forget to end the file with a newline
  9. To make it more secure, add at the top of the script above any includes: #define PC_SALT <int> Obviously replace with a random integer of your choosing.
  10. Then add #define TOKEN_SALT "myRandomSalt" at the top (definitions always on top, remember?). And replace myRandomSalt with a random passphrase for your project. Then save.
  11. Open up your "mfp Main" script in SL.
  12. On the first row (above default) enter #include "myFirstProject/_core.lsl"

Next we'll need to start listening to events:

  1. At the very top of the script (above the _core.lsl include) add #define USE_EVENTS
  2. Between the #includes and default, add:
	onEvt(string module, integer evt, list data){
		if(module == "#ROOT" && evt == evt$TOUCH_START){
			integer prim = llList2Integer(data, 0);
			key clicker = llList2Key(data, 1);
			if(clicker == llGetOwner())
				Dialog$spawn(llGetOwner(), "Would you like to output hello world?", (["Yes", "No"]), 0, "");
		}
	}

Now you're listening to events. In particular you are checking if the sender module was named "#ROOT" and the event was the standard event evt$TOUCH_START (defined in xobj_core/_ROOT.lsl)

If it was, it reads the data of that event, [clicked_prim, clicker_key] and tells the dialog manager to create a new dialog for the user, using menu ID 0. If you need more menu IDs, you likely want to define constants for them, but since there are no submenus in this script we can disregard it in this example.

Next we'll add the link message event. Replace all content of the default{} state of your script with:

	#include "xobj_core/_LM.lsl" 
	/* 
	    Included in all these calls:
	    METHOD - (int)method
	    PARAMS - (var)parameters
	    SENDER_SCRIPT - (str)sender
	    CB - The callback you specified when you sent a task
	    CB_DATA - Array of params to return in a callback
	    id - (key)method_raiser_uuid
	*/
	// Handle callbacks here
	if(method$isCallback){
		return;
	}
	// Public here
	
	#define LM_BOTTOM  
	#include "xobj_core/_LM.lsl"

That will set up the linkmessage handler. The code in if(method$isCallback) will be executed upon receiving a callback. The code after that is executed when receiving a method call.

The dialog manager will send a callback, so we'll set up something to happen on receiving a dialog callback of "Yes"

Replace if(method$isCallback){ return;} with the following, and compile:

    if(method$isCallback){
        if(method$byOwner){
            if(SENDER_SCRIPT == "jas Dialog" && METHOD == DialogMethod$spawn){
                // jas Dialog is an old module and passes an object instead of arrays. Always use arrays for your user defined methods.
                string message = method_arg(0);
                integer menu = (int)method_arg(1);
                key user = method_arg(2);
                if(message == "Yes" && menu == 0){
                    llSay(0, llGetDisplayName(user)+" said Hello World!");
                }
            }
        }
        return;
    }
  • First off, this checks if the method was raised by owner. You can also check if(method$internal) to limit a method to being run only from the linkset the script is in.
  • It then checks if the callback was sent from the "jas Dialog" module, and if the method run on that script was DialogMethod$spawn
  • In that case it fetches the ID of the menu you opened, and the message received.
  • If the message was Yes and the menu was 0 (as we specified when we sent the call) we output the hello world message.

Let other objects communicate with your dialog object

Now we have an object that will handle a dialog response. Let's make a second object to interact with it using the built in listener.

  1. Start off by opening your mfp Main.lsl file.
  2. On the first line add: #define mfpMainMethod$helloWorld 1 // (key)sender - Sends a hello world message with sender's display name
  3. You have defined your first method identifier (1)! And it accepts 1 argument (key)sender
  4. Always add a comment after your method definition explaining the arguments it accepts, and what the method does. And as usual, make sure the file ends with a newline.
  5. Save the file and go back to your mfp Main SL script.
  6. After the if(method$isCallback){} if statement (right above #define LM_BOTTOM) add the following code:
	if(METHOD == mfpMainMethod$helloWorld){
		llSay(0, llGetDisplayName(method_arg(0))+" said Hello World!");
		CB_DATA = [method_arg(0)];
	}
  • The if statement checks if the method to run was your defined mfpMainMethod$helloWorld
  • method_arg(0) gets the first method argument as a string, in this case it's supposed to be the key of the sender.
  • CB_DATA = [method_arg(0)]; lets you return the key of the sender in a callback.

Let's create an in-world box to run your method.

  1. Create a box in world.
  2. Create a new script in your box and name it #ROOT.
  3. Copy+paste the root script you made earlier into the box #ROOT script.
  4. Replace the #include "xobj_core/_LM.lsl" block with the following, and compile:
	#include "xobj_core/_LM.lsl" 
	/* 
	    Included in all these calls:
	    METHOD - (int)method
	    PARAMS - (var)parameters
	    SENDER_SCRIPT - (str)script that called this
	    CB - The callback you specified when you sent a task
	    CB_DATA - List of params to return in a callback
	    id - (key)method_raiser_uuid
	*/
	    
	if(method$isCallback){
	    if(SENDER_SCRIPT == "mfp Main" && METHOD == mfpMainMethod$helloWorld){
		llSay(0, llGetDisplayName(method_arg(0))+"'s call was successful! Sender callback: "+CB);
	    }
	    return;
	}
	   
	#define LM_BOTTOM  
	#include "xobj_core/_LM.lsl"

This receives a callback from mfp Main and outputs that it was a success.

  1. Replace the new #ROOT script's touch_start event with the following:
	touch_start(integer total){
		runOmniMethod("mfp Main", mfpMainMethod$helloWorld, [llDetectedKey(0)], "This is a callback message");
	}
  1. Touch the box. If it's set up correctly it should output the hello world message, as well as the callback you specified in touch_start.

runOmniMethod sends the command to any listeners within the region, except the linkset that sent the command. It's recommended that you use it sparingly, and instead use runMethod() with the key of the target.

Method Macros

Now we can run the method with runMethod(id, "mfp Main", mfpMainMethod$helloWorld, [llDetectedKey(0)], "This is a callback message"), but nobody's gonna want to remember all that. That's why we define method macros.

  1. Go back to edit C:\MyFirstProject\classes\mfp Main.lsl
  2. After the first definition, add a newline and enter: #define mfpMain$helloWorld( clicker, callback ) runOmniMethod("mfp Main", mfpMainMethod$helloWorld, [clicker], callback)

This defines a macro which can be run by mfpMain$helloWorld( key clicker, string callback )

Let's test it!

  1. Edit the #ROOT script inside the second box we created.
  2. Replace the touch start event with:
    touch_start(integer total){
        mfpMain$helloWorld(llDetectedKey(0), "This is a callback message");
    }
  1. Compile and click the box again. The result should be the same, but now with a much easier macro to remember.

For more info about how to properly format a header file, see Header File