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

From Second Life Wiki
Jump to navigation Jump to search
m (The link to the Incutio XML-RPC library does not exist any longer; gave a few alternatives)
(27 intermediate revisions by 13 users not shown)
Line 1: Line 1:
{{LSL Header|ml=*}}{{LSLC|}}{{LSLC|Communications}}
{{KBwarning|1='''THESE FUNCTIONS SHOULD BE CONSIDERED TO BE DEPRECIATED''' - Linden Lab intends to remove XML-RPC "at some point". (See [https://community.secondlife.com/forums/topic/488396-http-body-length-questions/?do=findComment&comment=2473694 this forum post], and [http://web.archive.org/web/20150103182352/http://community.secondlife.com/t5/Technology-General/HTTP-In-and-LSL-Communications/ba-p/646366 this blog entry].) }}
[http://en.wikipedia.org/wiki/XMLRPC XML-RPC] is a standard for sending Procedure Calls (e.g. function calls) to Remote systems.  It sends [http://en.wikipedia.org/wiki/XML XML] data over HTTP that remote system then handles.
[http://en.wikipedia.org/wiki/XMLRPC XML-RPC] is a standard for sending Procedure Calls (e.g. function calls) to Remote systems.  It sends [http://en.wikipedia.org/wiki/XML 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 {{LSLGC|HTTP}} Requests).
LSL receives XML-RPC requests and passes them to the prim specified.  It may not establish this connection, but it may reply and keep two-way communication with that server.  These responses seem to be able to transport the largest amount of data out of Second Life (vs. [[Email]] and {{LSLGC|HTTP}} Requests).


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


[http://rpgstats.com/wiki/index.php?title=XMLRPC http://rpgstats.com/wiki/index.php?title=XMLRPC]
{{KBcaution|width=100%|XML-RPC requests often time-out due to the front-end server being overloaded.  LL has continued to upgrade the server hardware periodically, but it has remained unreliable.  LL developers have advised that the XML-RPC design isn't scalable (due to the single server bottle-neck) and that the service is "deprecated".  They suggest using [[:Category:LSL HTTP|LSL HTTP]] as an alternative. If an XML-RPC request does time-out the script's remote_data event may or may not be triggered (and any script response is lost). See [http://web.archive.org/web/20150103182352/http://community.secondlife.com/t5/Technology-General/HTTP-In-and-LSL-Communications/ba-p/646366 this blog entry] for more about the future of XML-RPC.}}


=== IMPORTANT IMPLEMENTATION NOTE: ===
=== IMPORTANT IMPLEMENTATION NOTE: ===
Line 15: Line 17:
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.
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*.
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 which uses multiple scripts with [[llMessageLinked]] to concurrently deal with XML-RPC replies from the same source, because XML-RPC channels are *script-specific*, NOT *object-specific*.


For more information, see these forum threads [http://forums.secondlife.com/showthread.php?t=158437 here] and [http://forums.secondlife.com/showthread.php?p=1379244 here].
For more information, see these forum threads [http://forums.secondlife.com/showthread.php?t=158437 here] and [http://forums-archive.secondlife.com/139/c2/158585/1.html#post1379244 here].


==Other Resources==
==Other Resources==
* [http://www.xmlrpc.com/ XMLRPC.com]
* [http://www.xmlrpc.com/ XMLRPC.com]
* [http://scripts.incutio.com/xmlrpc/ Incutio's XML-RPC PHP library]
* [http://scripts.incutio.com/xmlrpc/ Incutio's XML-RPC PHP library] (broken link; try [https://github.com/hobbez/IXR_Library PHP7+ compatible replacement for IXR_Library.php] or any other port/replacement available on GitHub; another reliable source for a well-maintained port is [https://developer.wordpress.org/reference/classes/ixr_server/ WordPress itself])
* [http://perisic.com/sl/ A simple form to launch an XMLRPC request into Second Life]


==Example==
==Examples==


=php=
=php=
Line 30: Line 33:
One language to create a webapplication in is PHP.
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:
Here is an example of how to send a xmlrpc message to your SL-script from a webserver using PHP:
<code>&lt;?php
<syntaxhighlight lang="php"><?php
echo '&lt;pre&gt;';
echo '<pre>';
$channel = ""; //Fill in the channel you are using (key)
$channel = ""; //Fill in the channel you are using (key)
$xmldata = "&lt;?xml version=\"1.0\"?&gt;&lt;methodCall&gt;&lt;methodName&gt;llRemoteData&lt;/methodName&gt;&lt;params&gt;&lt;param&gt;&lt;value&gt;&lt;struct&gt;&lt;member&gt;&lt;name&gt;Channel&lt;/name&gt;&lt;value&gt;&lt;string&gt;".$channel."&lt;/string&gt;&lt;/value&gt;&lt;/member&gt;&lt;member&gt;&lt;name&gt;IntValue&lt;/name&gt;&lt;value&gt;&lt;int&gt;11261979&lt;/int&gt;&lt;/value&gt;&lt;/member&gt;&lt;member&gt;&lt;name&gt;StringValue&lt;/name&gt;&lt;value&gt;&lt;string&gt;happy birthday&lt;/string&gt;&lt;/value&gt;&lt;/member&gt;&lt;/struct&gt;&lt;/value&gt;&lt;/param&gt;&lt;/params&gt;&lt;/methodCall&gt;";
$intvalue = ""; //Fill in the intvalue you are using (integer)
echo sendToHost("xmlrpc.secondlife.com", "POST", "/cgi-bin/xmlrpc.cgi", $xmldata);
$strvalue = ""; //Fill in the strvalue you are using (string)
echo '&lt;/pre&gt;';
$xmldata = "<?xml version=\"1.0\"?><methodCall><methodName>llRemoteData</methodName>
<params><param><value><struct>
function sendToHost($host,$method,$path,$data,$useragent=0)
<member><name>Channel</name><value><string>".$channel."</string></value></member>
{  
<member><name>IntValue</name><value><int>".$intvalue."</int></value></member>
$buf="";
<member><name>StringValue</name><value><string>".$strvalue."</string></value></member>
// Supply a default method of GET if the one passed was empty  
</struct></value></param></params></methodCall>";
if (empty($method))  
echo sendToHost("xmlrpc.secondlife.com", "POST", "/cgi-bin/xmlrpc.cgi", $xmldata);
$method = 'GET';  
echo '</pre>';
$method = strtoupper($method);  
function sendToHost($host,$method,$path,$data,$useragent=0)
$fp = fsockopen($host, 80, $errno, $errstr, 30);
{  
$buf="";
if( !$fp )
// Supply a default method of GET if the one passed was empty  
{
if (empty($method))  
$buf = "$errstr ($errno)&lt;br /&gt;\n";
$method = 'GET';  
}else
$method = strtoupper($method);  
{
if ($method == 'GET')  
$fp = fsockopen($host, 80, $errno, $errstr, 30);
$path .= '?' . $data;  
fputs($fp, "$method $path HTTP/1.1\r\n");  
if( !$fp )
fputs($fp, "Host: $host\r\n");  
{
fputs($fp, "Content-type: text/xml\r\n");  
$buf = "$errstr ($errno)<br />\n";
fputs($fp, "Content-length: " . strlen($data) . "\r\n");  
}else
if ($useragent)  
{
fputs($fp, "User-Agent: MSIE\r\n");  
if ($method == 'GET')  
fputs($fp, "Connection: close\r\n\r\n");  
$path .= '?' . $data;  
if ($method == 'POST')  
fputs($fp, "$method $path HTTP/1.1\r\n");  
fputs($fp, $data);  
fputs($fp, "Host: $host\r\n");  
while (!feof($fp))  
fputs($fp, "Content-type: text/xml\r\n");  
$buf .= fgets($fp,128);  
fputs($fp, "Content-length: " . strlen($data) . "\r\n");  
fclose($fp);  
if ($useragent)  
}
fputs($fp, "User-Agent: MSIE\r\n");  
return $buf;  
fputs($fp, "Connection: close\r\n\r\n");  
}  
if ($method == 'POST')  
?&gt;</code>
fputs($fp, $data);  
while (!feof($fp))  
$buf .= fgets($fp,128);  
fclose($fp);  
}
return $buf;  
}  
?></syntaxhighlight>


=perl=
=perl=
Line 75: Line 85:
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 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."
The perl code sends the message that contains the string "Message to pass" and the number "2007." The LSL code responds by sending 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)
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
#  
<code>
&#35;!/usr/bin/perl
&#35;


use RPC::XML;
use RPC::XML;
Line 93: Line 100:
my $req = RPC::XML::request->new(
my $req = RPC::XML::request->new(
         "llRemoteData",
         "llRemoteData",
         &#123;'Channel' => RPC::XML::string->new("OBJECTS UUID GOES HERE!"),
         {'Channel' => RPC::XML::string->new("UUID for the open channel from the object GOES HERE!"),
         'IntValue' => RPC::XML::int->new(2007),
         'IntValue' => RPC::XML::int->new(2007),
         'StringValue' => RPC::XML::string->new("message to pass")&#125;);
         'StringValue' => RPC::XML::string->new("message to pass")});


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


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




&#35; Print out the response
#  Print out the response
print "\n\n\nResponse\n";
print "\n\n\nResponse\n";
print "ref(resp): ",ref($resp),"\n";
print "ref(resp): ",ref($resp),"\n";
Line 123: Line 130:
$res = $P->parse($xml);
$res = $P->parse($xml);
print ref($res),"\n";
print ref($res),"\n";
if (ref($res)) &#123;
if (ref($res)) {
     %h = %{$res->value};
     %h = %{$res->value};
     foreach $lupe (keys %h) &#123;
     foreach $lupe (keys %h) {
       $val =  $h{$lupe};
       $val =  $h{$lupe};
       print "$lupe: $val\n";
       print "$lupe: $val\n";
     &#125;
     }
&#125;
}</syntaxhighlight>
 
 
=Java=
 
This Java code uses the org.apache.xmlrpc library, a jar file for this can be found at http://www.perisic.com/xmlrpc/cis69mc.jar (the code has been tested with this). See http://perisic.com/xmlrpc for a simple introduction into Java & XMLRPC if necessary.
* Save the code in a file named SLClient.java and download the jar file cis69mc.jar from the location above.
* Add the channel id where it says '<add channel id here!!!>', so this line may eventually look similar to <code>theData.put("Channel", "24a2c834-c984-9209-78df-20608d4c8ade");</code>
* Compile the code with the command: javac -classpath "cis69mc.jar;." SLClient.jar (you may get a note about unchecked or unsafe operations, ignore that). 
* Run the code with the command: java -classpath "cis69mc.jar;." SLClient
 
<syntaxhighlight lang="java">import java.util.*;
import org.apache.xmlrpc.*;
 
public class SLClient {
public static void main (String [] args) {
  try {
  Hashtable theData = new Hashtable();
  XmlRpcClient server = new XmlRpcClient("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi"); //
  theData.put("Channel", "<add channel id here!!!>");
  theData.put("IntValue", 2483);
  theData.put("StringValue", "The date is: "+ (new Date()).toString() );
  Vector params = new Vector();  
  params.add(theData);
 
  Object result = server.execute("llRemoteData", params );
 
  } catch (Exception exception) {
  System.err.println("SL_Client: " + exception);
  exception.printStackTrace();  
  }
  }
}</syntaxhighlight>


=Visual Basic 6=


</code>
This piece of Visual Basic 6 code makes use of the Microsoft WinSock 6.0 control. Make a new project, and place a Winsock control on the form.


Corresponding LSL code:
<syntaxhighlight lang="vb">
Dim Channel As String
Dim IntValue As Integer
Dim StrValue As String
Dim XMLData As String


{{LSL_Function
Private Sub Form_Load()
key remoteChannel;
    Channel = "<add channel id here!!!>"
init() &#123;
    IntValue = 2007
    StrValue = "This is a test string."
    XMLData = "<?xml version=""1.0""?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct>"
    XMLData = XMLData & "<member><name>Channel</name><value><string>" & Channel & "</string></value></member>"
    XMLData = XMLData & "<member><name>IntValue</name><value><int>" & CStr(IntValue) & "</int></value></member>"
    XMLData = XMLData & "<member><name>StringValue</name><value><string>" & StrValue & "</string></value></member>"
    XMLData = XMLData & "</struct></value></param></params></methodCall>"
 
    Winsock1.Connect "xmlrpc.secondlife.com", 80
End Sub
 
Private Sub Winsock1_Connect()
    WinsockSend "POST /cgi-bin/xmlrpc.cgi HTTP/1.1" & vbCrLf
    WinsockSend "Host: xmlrpc.secondlife.com" & vbCrLf
    WinsockSend "Content-type: text/xml" & vbCrLf
    WinsockSend "Content-length: " & CStr(Len(XMLData)) & vbCrLf
    WinsockSend "Connection: close" & vbCrLf & vbCrLf
    WinsockSend XMLData & vbCrLf
End Sub
 
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    Dim TempStr As String
    Winsock1.GetData TempStr
    MsgBox TempStr
End Sub
 
Sub WinsockSend(str As String)
    Winsock1.SendData str
End Sub
</syntaxhighlight>
 
=Command line curl=
If you want to debug XML-RPC from the command line you can use "curl" to do so.  Put this in a file called <code>rpc.xml</code>, replacing the UUID for Channel with your script's channel id:
 
<syntaxhighlight lang="xml">
<?xml version="1.0"?>
<methodCall>
    <methodName>llRemoteData</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>Channel</name>
                        <value><string>1ee246fc-e60d-2fd3-6242-f5fc5d19850f</string></value>
                    </member>
                    <member>
                        <name>IntValue</name>
                        <value><int>42</int></value>
                    </member>
                    <member>
                        <name>StringValue</name>
                        <value><string>Hello, world!</string></value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>
</syntaxhighlight>
 
Then call curl to make a POST to the XMLRPC server:
 
<syntaxhighlight lang="bash">
curl --verbose --data "@rpc.xml" http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi
</syntaxhighlight>
 
=LSL Server Code=
 
Corresponding LSL code (copy and paste this code as a script in an object, save and rezz it, a message with the channel id (and other stuff) will be printed):
 
<syntaxhighlight lang="lsl2">key remoteChannel;
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;
default {
     state_entry() &#123;
     state_entry() {
         init();
         init();
     &#125;
     }
      
      
     state_exit() &#123;
     state_exit() {
         return;
         return;
     &#125;
     }
                                  
                                  
     on_rez(integer param) &#123;
     on_rez(integer param) {
         llResetScript();         
         llResetScript();         
     &#125;
     }
                                  
                                  
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) &#123;
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
         if (type == REMOTE_DATA_CHANNEL) &#123; // channel created
         if (type == REMOTE_DATA_CHANNEL) { // 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 164: Line 280:
             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
         &#125; else &#123;
         } else {
             llSay(DEBUG_CHANNEL,"Unexpected event type");  
             llSay(DEBUG_CHANNEL,"Unexpected event type");  
         &#125;                      
         }                      
     &#125;                  
     }                  
&#125;                      
}                      
                                  
                                  


state receiving &#123;
state receiving {


     state_entry() &#123;
     state_entry() {
         llOwnerSay("Ready to receive information from outside SL");
         llOwnerSay("Ready to receive information from outside SL");
     &#125;  
     }  
      
      
     state_exit() &#123;
     state_exit() {
         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) &#123;
     on_rez(integer param) {
         llResetScript();
         llResetScript();
     &#125;
     }
      
      
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) &#123;
     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
         if (type == REMOTE_DATA_REQUEST) &#123; // handle requests sent to us
         if (type == REMOTE_DATA_REQUEST) { // 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);
             llRemoteDataReply(channel,NULL_KEY,"I got it",2008);
             llRemoteDataReply(channel,NULL_KEY,"I got it",2008);
             llOwnerSay("I just recieved data in "+ llGetRegionName() +  
             llOwnerSay("I just received data in "+ llGetRegionName() +  
                         " 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;
     }
      
}</syntaxhighlight>
&#125;
 
}}

Revision as of 09:07, 13 July 2022

KBwarning.png Warning: THESE FUNCTIONS SHOULD BE CONSIDERED TO BE DEPRECIATED - Linden Lab intends to remove XML-RPC "at some point". (See this forum post, and this blog entry.)

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 connection, but it may reply and keep two-way communication with that server. These responses seem to be able to transport the largest amount of data out of Second Life (vs. Email and HTTP Requests).

NOTE:

KBcaution.png Important: XML-RPC requests often time-out due to the front-end server being overloaded. LL has continued to upgrade the server hardware periodically, but it has remained unreliable. LL developers have advised that the XML-RPC design isn't scalable (due to the single server bottle-neck) and that the service is "deprecated". They suggest using LSL HTTP as an alternative. If an XML-RPC request does time-out the script's remote_data event may or may not be triggered (and any script response is lost). See this blog entry for more about the future of XML-RPC.

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 which uses multiple scripts with llMessageLinked to concurrently deal with XML-RPC replies from the same source, because XML-RPC channels are *script-specific*, NOT *object-specific*.

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

Other Resources

Examples

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)
	$intvalue = ""; //Fill in the intvalue you are using (integer)
	$strvalue = ""; //Fill in the strvalue you are using (string)
	$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>".$intvalue."</int></value></member>
<member><name>StringValue</name><value><string>".$strvalue."</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 responds by sending the string "I got it" and the number "2008."

#!/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("UUID for the open channel from the object 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";
    }
}


Java

This Java code uses the org.apache.xmlrpc library, a jar file for this can be found at http://www.perisic.com/xmlrpc/cis69mc.jar (the code has been tested with this). See http://perisic.com/xmlrpc for a simple introduction into Java & XMLRPC if necessary.

  • Save the code in a file named SLClient.java and download the jar file cis69mc.jar from the location above.
  • Add the channel id where it says '<add channel id here!!!>', so this line may eventually look similar to theData.put("Channel", "24a2c834-c984-9209-78df-20608d4c8ade");
  • Compile the code with the command: javac -classpath "cis69mc.jar;." SLClient.jar (you may get a note about unchecked or unsafe operations, ignore that).
  • Run the code with the command: java -classpath "cis69mc.jar;." SLClient
import java.util.*;
import org.apache.xmlrpc.*;

public class SLClient {
 public static void main (String [] args) {
  try {
   Hashtable theData = new Hashtable(); 
   XmlRpcClient server = new XmlRpcClient("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi"); //
   theData.put("Channel", "<add channel id here!!!>"); 
   theData.put("IntValue", 2483); 
   theData.put("StringValue", "The date is: "+ (new Date()).toString() ); 
   Vector params = new Vector(); 
   params.add(theData); 
   
   Object result = server.execute("llRemoteData", params ); 
  
  } catch (Exception exception) {
   System.err.println("SL_Client: " + exception);
   exception.printStackTrace(); 
   }
  }
}

Visual Basic 6

This piece of Visual Basic 6 code makes use of the Microsoft WinSock 6.0 control. Make a new project, and place a Winsock control on the form.

Dim Channel As String
Dim IntValue As Integer
Dim StrValue As String
Dim XMLData As String

Private Sub Form_Load()
    Channel = "<add channel id here!!!>"
    IntValue = 2007
    StrValue = "This is a test string."
    XMLData = "<?xml version=""1.0""?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct>"
    XMLData = XMLData & "<member><name>Channel</name><value><string>" & Channel & "</string></value></member>"
    XMLData = XMLData & "<member><name>IntValue</name><value><int>" & CStr(IntValue) & "</int></value></member>"
    XMLData = XMLData & "<member><name>StringValue</name><value><string>" & StrValue & "</string></value></member>"
    XMLData = XMLData & "</struct></value></param></params></methodCall>"

    Winsock1.Connect "xmlrpc.secondlife.com", 80
End Sub

Private Sub Winsock1_Connect()
    WinsockSend "POST /cgi-bin/xmlrpc.cgi HTTP/1.1" & vbCrLf
    WinsockSend "Host: xmlrpc.secondlife.com" & vbCrLf
    WinsockSend "Content-type: text/xml" & vbCrLf
    WinsockSend "Content-length: " & CStr(Len(XMLData)) & vbCrLf
    WinsockSend "Connection: close" & vbCrLf & vbCrLf
    WinsockSend XMLData & vbCrLf
End Sub

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    Dim TempStr As String
    Winsock1.GetData TempStr
    MsgBox TempStr
End Sub

Sub WinsockSend(str As String)
    Winsock1.SendData str
End Sub

Command line curl

If you want to debug XML-RPC from the command line you can use "curl" to do so. Put this in a file called rpc.xml, replacing the UUID for Channel with your script's channel id:

<?xml version="1.0"?>
<methodCall>
    <methodName>llRemoteData</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>Channel</name>
                        <value><string>1ee246fc-e60d-2fd3-6242-f5fc5d19850f</string></value>
                    </member>
                    <member>
                        <name>IntValue</name>
                        <value><int>42</int></value>
                    </member>
                    <member>
                        <name>StringValue</name>
                        <value><string>Hello, world!</string></value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>

Then call curl to make a POST to the XMLRPC server:

curl --verbose --data "@rpc.xml" http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi

LSL Server Code

Corresponding LSL code (copy and paste this code as a script in an object, save and rezz it, a message with the channel id (and other stuff) will be printed):

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