Difference between revisions of "Category:LSL XML-RPC"

From Second Life Wiki
Jump to navigation Jump to search
Line 76: Line 76:


The perl code sends the message that contains the string "Message to pass" and the number "2007." The LSL code sends the string "I got it" and the number "2008."
The perl code sends the message that contains the string "Message to pass" and the number "2007." The LSL code sends the string "I got it" and the number "2008."
(HELP!) The formatting in the code section is wrong, and I cannot figure out why. (Sorry... - Grandma Bates)


<code>
<code>
Line 84: Line 86:
use RPC::XML::Parser;
use RPC::XML::Parser;
use RPC::XML::Client;
use RPC::XML::Client;


my $llURL = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi";
my $llURL = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi";
Line 95: Line 93:
my $req = RPC::XML::request->new(
my $req = RPC::XML::request->new(
         "llRemoteData",
         "llRemoteData",
         {'Channel' => RPC::XML::string->new("OBJECTS UUID GOES HERE!"),
         &#123;'Channel' => RPC::XML::string->new("OBJECTS UUID GOES HERE!"),
         'IntValue' => RPC::XML::int->new(2007),
         'IntValue' => RPC::XML::int->new(2007),
         'StringValue' => RPC::XML::string->new("message to pass")});
         'StringValue' => RPC::XML::string->new("message to pass")&#125;);


&#35;  Print out the message to send
&#35;  Print out the message to send
Line 106: Line 104:
$res = $P->parse($xml);
$res = $P->parse($xml);
print ref($res),"\n";
print ref($res),"\n";
if (ref($res)) {
if (ref($res)) &#123;
     %h = %{$res->args->[0]->value};
     %h = %{$res->args->[0]->value};
     foreach $lupe (keys %h) {
     foreach $lupe (keys %h) &#123;
       print "$lupe: $h{$lupe}\n";
       print "$lupe: $h{$lupe}\n";
     }
     &#125;
}
&#125;


&#35;  Submit the request to send the information above.
&#35;  Submit the request to send the information above.
Line 125: Line 123:
$res = $P->parse($xml);
$res = $P->parse($xml);
print ref($res),"\n";
print ref($res),"\n";
if (ref($res)) {
if (ref($res)) &#123;
     %h = %{$res->value};
     %h = %{$res->value};
     foreach $lupe (keys %h) {
     foreach $lupe (keys %h) &#123;
       $val =  $h{$lupe};
       $val =  $h{$lupe};
       print "$lupe: $val\n";
       print "$lupe: $val\n";
     }
     &#125;
}
&#125;




Line 139: Line 137:


<code>
<code>
key remoteChannel;
key remoteChannel;
 
init() &#123;
 
init() {
     llOpenRemoteDataChannel(); // create an XML-RPC channel
     llOpenRemoteDataChannel(); // create an XML-RPC channel
     llOwnerSay("My key is " + (string)llGetKey());
     llOwnerSay("My key is " + (string)llGetKey());
}
&#125;


 
default &#123;
 
     state_entry() &#123;
default
{
     state_entry() {
         init();
         init();
     }
     &#125;
      
      
     state_exit() {
     state_exit() &#123;
         return;
         return;
     }
     &#125;
                                  
                                  
     on_rez(integer param) {   
     on_rez(integer param) &#123;
         llResetScript();         
         llResetScript();         
     }                         
     &#125;
                                  
                                  
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {           
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) &#123;
         if (type == REMOTE_DATA_CHANNEL) { // channel created
         if (type == REMOTE_DATA_CHANNEL) &#123; // channel created
             llSay(DEBUG_CHANNEL,"Channel opened for REMOTE_DATA_CHANNEL" +  
             llSay(DEBUG_CHANNEL,"Channel opened for REMOTE_DATA_CHANNEL" +  
                 (string)channel + " " + (string)message_id + " " + (string)sender + " " +                         
                 (string)channel + " " + (string)message_id + " " + (string)sender + " " +                         
Line 174: Line 164:
             llOwnerSay("Ready to receive requests on channel \"" + (string)channel + "\"");                         
             llOwnerSay("Ready to receive requests on channel \"" + (string)channel + "\"");                         
             state receiving; // start handling requests
             state receiving; // start handling requests
         } else {             
         &#125; else &#123;
             llSay(DEBUG_CHANNEL,"Unexpected event type");  
             llSay(DEBUG_CHANNEL,"Unexpected event type");  
         }                      
         &#125;                      
     }                         
     &#125;               
}                             
&#125;                   
                                  
                                  


state receiving {
state receiving &#123;


     state_entry() {
     state_entry() &#123;
         llOwnerSay("Ready to receive information from outside SL");
         llOwnerSay("Ready to receive information from outside SL");
     }   
     &#125; 
      
      
     state_exit() {
     state_exit() &#123;
         llOwnerSay("No longer receiving information from outside SL.");
         llOwnerSay("No longer receiving information from outside SL.");
         llCloseRemoteDataChannel(remoteChannel);
         llCloseRemoteDataChannel(remoteChannel);
     }
     &#125;
      
      
     on_rez(integer param) {
     on_rez(integer param) &#123;
         llResetScript();
         llResetScript();
     }
     &#125;
      
      
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) &#123;
         if (type == REMOTE_DATA_REQUEST) { // handle requests sent to us
         if (type == REMOTE_DATA_REQUEST) &#123; // handle requests sent to us
             llSay(DEBUG_CHANNEL,"Request received for REMOTE_DATA_REQUEST " + (string)channel + " " +
             llSay(DEBUG_CHANNEL,"Request received for REMOTE_DATA_REQUEST " + (string)channel + " " +
                 (string)message_id + " " + (string)sender + " " + (string)ival + " " + (string)sval);
                 (string)message_id + " " + (string)sender + " " + (string)ival + " " + (string)sval);
Line 204: Line 194:
                         " at position " + (string)llGetPos() + "\n" +
                         " at position " + (string)llGetPos() + "\n" +
                       "The string was " +  sval + "\nThe number was " + (string)ival + ".");
                       "The string was " +  sval + "\nThe number was " + (string)ival + ".");
         }
         &#125;
     }
     &#125;
      
      
}
&#125;
 
 


</code>
</code>

Revision as of 10:57, 2 July 2007

XML-RPC is a standard for sending Procedure Calls (e.g. function calls) to Remote systems. It sends XML data over HTTP that remote system then handles.

LSL receives XML-RPC requests and passes them to the prim specified. It may not establish this connect, but it may reply and keep two-way communication with that server. These responses seem to be able to transport a largest amount of data out of Second Life (vs. Email and HTTP Requests).

Due to potential copyright issues the following article will be linked rather than imitated, it should get you well on your way:

http://rpgstats.com/wiki/index.php?title=XMLRPC

IMPORTANT IMPLEMENTATION NOTE:

The current implementation of XML-RPC only allows ONE request to be queued on the front-end server (xmlrpc.secondlife.com) at a time. Any additional requests to the same data channel overwrite any pending one. This has serious ramifications for the design of XML-RPC communications where the in-world object could receive requests faster than it can respond to them. In addition, the 3-second delay in llRemoteDataReply exacerbates this problem even more.

The observed issue is this: if you send multiple quick requests to an in-world object via XML-RPC, one which is scripted to perform some processing and then return a response (via llRemoteDataReply), there is a potential for earlier requests to get lost on the front end server (they still should generate remote_data events, though), and have the response meant for an earlier request end up being passed back to a later one, while the earlier requests will time out back at your external application.

As a result, if you intend to do any serious work with XML-RPC, you will have to design your external client application to manually serialize all requests to each individual RPC channel. That means you have to wait for a response from the previous request before you attempt to send the next one. If you don't care about receiving responses, then this problem is not an issue, as all requests seem to get passed on to the script, regardless of the queueing issue.

Also note that there is NO way to get around the 3-second delay for llRemoteDataReply; you cannot use the multiple-slave-comm-script trick, because XML-RPC channels are *script-specific*, NOT *object-specific*.

For more information, see these forum threads here and here.

Other Resources

Example

php

To initialize a xmlrpc from an external server you'll need some kind of web-application. One language to create a webapplication in is PHP. Here is an example of how to send a xmlrpc message to your SL-script from a webserver using PHP:

<?php
	echo '<pre>';
	$channel = ""; //Fill in the channel you are using (key)
	$xmldata = "<?xml version=\"1.0\"?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>".$channel."</string></value></member><member><name>IntValue</name><value><int>11261979</int></value></member><member><name>StringValue</name><value><string>happy birthday</string></value></member></struct></value></param></params></methodCall>";
	echo sendToHost("xmlrpc.secondlife.com", "POST", "/cgi-bin/xmlrpc.cgi", $xmldata);
	echo '</pre>';
	
	function sendToHost($host,$method,$path,$data,$useragent=0)
	{ 
		$buf="";
		// Supply a default method of GET if the one passed was empty 
		if (empty($method)) 
			$method = 'GET'; 
		$method = strtoupper($method); 
	
		$fp = fsockopen($host, 80, $errno, $errstr, 30);
	
		if( !$fp )
		{
			$buf = "$errstr ($errno)<br />\n";
		}else
		{
			if ($method == 'GET') 
			$path .= '?' . $data; 
			fputs($fp, "$method $path HTTP/1.1\r\n"); 
			fputs($fp, "Host: $host\r\n"); 
			fputs($fp, "Content-type: text/xml\r\n"); 
			fputs($fp, "Content-length: " . strlen($data) . "\r\n"); 
			if ($useragent) 
				fputs($fp, "User-Agent: MSIE\r\n"); 
			fputs($fp, "Connection: close\r\n\r\n"); 
			if ($method == 'POST') 
				fputs($fp, $data); 
			while (!feof($fp)) 
				$buf .= fgets($fp,128); 
			fclose($fp); 
		}
		return $buf; 
	} 
?>

perl

The perl code and corresponding LSL code to contact an object in Second Life is given below. The perl code makes use of the RPC::XML module. You can run the perl code from the command line. It creates an rpc object and sends a message to an object that is rezzed on the grid. Note that you have to supply the UUID of the object within the perl code.

The perl code sends the message that contains the string "Message to pass" and the number "2007." The LSL code sends the string "I got it" and the number "2008."

(HELP!) The formatting in the code section is wrong, and I cannot figure out why. (Sorry... - Grandma Bates)

#!/usr/bin/perl #

use RPC::XML; use RPC::XML::Parser; use RPC::XML::Client;

my $llURL = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi"; $P = RPC::XML::Parser->new();

my $cli = RPC::XML::Client->new($llURL); my $req = RPC::XML::request->new(

       "llRemoteData",
       {'Channel' => RPC::XML::string->new("OBJECTS UUID GOES HERE!"),
       'IntValue' => RPC::XML::int->new(2007),
       'StringValue' => RPC::XML::string->new("message to pass")});

# Print out the message to send print "ref(req): ",ref($req),"\n"; $xml = $req->as_string(); print "Length: ",$req->length,"\n\n",$xml,"\n\n";

$res = $P->parse($xml); print ref($res),"\n"; if (ref($res)) {

   %h = %{$res->args->[0]->value};
   foreach $lupe (keys %h) {
      print "$lupe: $h{$lupe}\n";
   }

}

# Submit the request to send the information above. my $resp = $cli->send_request($req);


# Print out the response print "\n\n\nResponse\n"; print "ref(resp): ",ref($resp),"\n"; $xml = $resp->as_string(); print "Length: ",$resp->length,"\n\n",$xml,"\n\n";

$res = $P->parse($xml); print ref($res),"\n"; if (ref($res)) {

   %h = %{$res->value};
   foreach $lupe (keys %h) {
      $val =  $h{$lupe};
      print "$lupe: $val\n";
   }

}


Corresponding LSL code:

key remoteChannel; init() {

   llOpenRemoteDataChannel(); // create an XML-RPC channel
   llOwnerSay("My key is " + (string)llGetKey());

}

default {

   state_entry() {
       init();
   }
   
   state_exit() {
       return;
   }
                               
   on_rez(integer param) {
       llResetScript();        
   }
                               
   remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
        if (type == REMOTE_DATA_CHANNEL) { // channel created
            llSay(DEBUG_CHANNEL,"Channel opened for REMOTE_DATA_CHANNEL" + 
               (string)channel + " " + (string)message_id + " " + (string)sender + " " +                         
               (string)ival + " " + (string)sval);
            remoteChannel = channel;
            llOwnerSay("Ready to receive requests on channel \"" + (string)channel + "\"");                        
            state receiving; // start handling requests
        } else {
            llSay(DEBUG_CHANNEL,"Unexpected event type"); 
        }                      
    }                 

}


state receiving {

   state_entry() {
       llOwnerSay("Ready to receive information from outside SL");
   }  
   
   state_exit() {
       llOwnerSay("No longer receiving information from outside SL.");
        llCloseRemoteDataChannel(remoteChannel);
   }
   
   on_rez(integer param) {
       llResetScript();
   }
   
   remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
       if (type == REMOTE_DATA_REQUEST) { // handle requests sent to us
            llSay(DEBUG_CHANNEL,"Request received for REMOTE_DATA_REQUEST " + (string)channel + " " +
               (string)message_id + " " + (string)sender + " " + (string)ival + " " + (string)sval);
           llRemoteDataReply(channel,NULL_KEY,"I got it",2008);
           llOwnerSay("I just recieved data in "+ llGetRegionName() + 
                       " at position " + (string)llGetPos() + "\n" +
                      "The string was " +  sval + "\nThe number was " + (string)ival + ".");
       }
   }
   

}

Subcategories

This category has the following 2 subcategories, out of 2 total.