Game Development Community

dev|Pro Game Development Curriculum

WebServices, HTTPObject, XML, and T3D.

by Vincent BILLET · 08/09/2010 (12:44 am) · 2 comments

This ressource give some code example to handle a WebService for T3D 1.1 using XML. It explains and give example to handle XML using HTTPObject.
First, let's take a simple WebService written in PHP. This simple WebService have 3 functionnalities :
* Multiple parameters requests (Login)
* Single record request (AccountInfo)
* A Listing (Connected)
You can copy/paste this simple php code. This code is not the subject of this ressource. It is only here to illustrate the T3D code.
<?php
function usrlogin($u,$p)
{
	$result = "";
	if (($u=="User1") && ($p=="pass1")) {$result.="<loginresult>SUCCESS</loginresult><userID>bac4e7cf41ae15478</userID>";} else
	if (($u=="User2") && ($p=="pass2")) {$result.="<loginresult>AWAITING</loginresult>";} else
	{ $result.="<loginresult>FAILURE</loginresult>"; }
	return $result;
}

function accountinfo($uid)
{
	if ($uid=="bac4e7cf41ae15478")
	{
		//Account exists
		$result = "<exists>yes</exists>";
		$result.="<LastName>DUPONT</LastName>";
		$result.="<FirstName>Jhon Henri</FirstName>";
		$result.="<mail>jhon@example.com</mail>";
		$result.="<Score>18735</Score>";
	} else
	{
		$result = "<exists>$uid does not exist.</exists>";
	}
	return $result;
}

function connected()
{
	$result = "<ID>bac4e7cf41ae15478</ID><pseudo>Hey_Toto</pseudo><score>12547</score>";
	$result.= "<ID>12345678902124528</ID><pseudo>Andrew</pseudo><score>541</score>";
	$result.= "<ID>ba524c522ef524c45</ID><pseudo>Hello</pseudo><score>1257</score>";
	$result.= "<ID>bcde1254585458521</ID><pseudo>Elric</pseudo><score>9875</score>";
	$result.= "<ID>12542354856849447</ID><pseudo>Elminster</pseudo><score>1245</score>";
	$result.= "<ID>12258525454542154</ID><pseudo>Johanna</pseudo><score>0</score>";
	$result.= "<ID>11111111111111111</ID><pseudo>Vincent</pseudo><score>12584</score>";
	$result.= "<ID>22222222222222222</ID><pseudo>Great_guy</pseudo><score>957</score>";
	$result.= "<ID>33333333333333333</ID><pseudo>Funny</pseudo><score>5684</score>";
	return $result;
}

$xml="<?xml version='1.0' encoding='ISO-8859-1'?>";
$xml.="<root>";
if ($_REQUEST["cmd"]=="login") {$xml.=usrlogin($_REQUEST["u"],$_REQUEST["p"]);} else
if ($_REQUEST["cmd"]=="accountinfo") {$xml.=accountinfo($_REQUEST["uid"]);} else
if ($_REQUEST["cmd"]=="connected") {$xml.=connected();}
$xml.="</root>";
echo($xml);
?>
Notes : You can use multiple parameters on a query (see $_REQUEST). You need to create a "root" node to make your WebService server compliant with the following code (see HTTPObject::getXMLRoot). You also need to handle each command with "cmd" parameter (see HTTPObject::getXML).

Now, let's go to Torque :
1) You have to fix the engine.
2) You have to write WebService Client.

Fix for T3D 1.1 Beta 2
in httpObject.cpp , line 174 : Comment this line :
//      asciiEscapeTable[static_cast<U32>('&')] = true;
(&'s Ascii Escape should be done in scripts)

in httpObject.cpp , line 366 : Comment these lines to make the code looks like this :
//   if(dStrcmp(query,""))
//		object->get(Address, requirstURI, NULL);
//   else
		object->get(Address, requirstURI, query);
// End of Fix.

The WebService Client
The Following code is divided in 3 parts :

PART1 : Test functions, to allow you a quick test from console
PART2 : WebService - General purpose. That's the main object of this ressource. This part gives you useful functions to build your WebService client.
PART3 : Example WebService Methods. These methods show you how to handle XML requests and answers.

//*********************************************************************
// PART1
// Test functions
//*********************************************************************
$WebServiceDNS = "www.someurl.com";
$WebServicePort = 80;
$WebServiceURL= $WebServiceDNS@":"@$WebServicePort;
$WebServicePath = "/";
$WebServiceApplication = "WebService.php";

function example_Login()
{
	$WebService = new HTTPObject();
	$WebService.init_http($WebServiceURL,$WebServicePath,$WebServiceApplication);
	$WebService.XMLLogin("User1","pass1");
}

function example_Account()
{
	$WebService = new HTTPObject();
	$WebService.init_http($WebServiceURL,$WebServicePath,$WebServiceApplication);
	$WebService.XMLUserAccount("bac4e7cf41ae15478");
}

function example_Connected()
{
	$WebService = new HTTPObject();
	$WebService.init_http($WebServiceURL,$WebServicePath,$WebServiceApplication);
	$WebService.XMLConnected();
}

//*********************************************************************
// PART2
// WebService - General purpose
//*********************************************************************

//*********************************************************************
// Init
function HTTPObject::init_http(%this,%webserver,%webpath,%webapp)
{
	%this.WebServer = %webserver;
	%this.WebPath = %webpath;
	%this.WebApp = %webapp;
	%this.WebResp = "";
}
  
//*********************************************************************
// HTTPEvents
function HTTPObject::onLine(%this,%line)  
{  
    %this.WebResp = %this.WebResp @ %line;  
}  

function HTTPObject::onDisconnect(%this)  
{
	%this.getXMLRoot();
	eval(%this@"."@%this.callBack@"CallBack();");
	%this.WebResp="";
}

function HTTPObject::onDNSResolved(%this)
{
	echo("WebService : DNS Resolved");
}

function HTTPObject::onDNSFailed(%this)
{
	error("Check your DNS configuration - (Try to ping "@$WebServiceDNS@")");
}

function HTTPObject::onConnectFailed(%this)
{
	error("Unable to connect to "@$WebServiceURL@" - try to browse URL : http://"@$WebServiceURL@$WebServicePath@$WebServiceApplication);
}

//*********************************************************************
// Simple XML Handling
function HTTPObject::getXML(%this,%command,%callback)
{
   %query = "cmd="@%command;
   %script = %this.WebPath @ %this.WebApp;
   %this.WebResp="";
   if (%this.XMLData!$="") {%this.XMLData.delete(); }
   %this.XMLData = new ScriptObject();
   %this.callBack = %callback;
   %this.get(%this.WebServer,%script,%query);  
}

function HTTPObject::getXMLRoot(%this)
{
    %startTag = "<root>";  
    %endTag   = "</root>";  
	%startChar=0;
	%xml = %this.WebResp;
    %startTagOffset = strpos(%xml, %startTag, %startChar);  
	%startOffset = %startTagOffset + strlen(%startTag);  
    %endTagOffset = strpos(%xml, %endTag, %startOffset - 1);  
    if(%startTagOffset >= 0)
	{
	    %this.XMLRoot=getSubStr(%xml, %startOffset, %endTagOffset - %startOffset);
	} else {
		%this.XMLRoot = "";
		error("No XML Root : WebService answers must have a <root>...</root> node");
	}
}

function HTTPObject::getXMLData(%this,%node)
{  
	%xml = %this.XMLRoot;
    %startTag = "<" @ %node @ ">";  
    %endTag   = "</" @ %node @ ">";  
	%startChar=0;
	%ii = -1;
	while (true)
	{
	    %startTagOffset = strpos(%xml, %startTag, %startChar);  
		%startOffset = %startTagOffset + strlen(%startTag);  
	    %endTagOffset = strpos(%xml, %endTag, %startOffset-1);  
		%startChar = %endTagOffset+strlen(%endTag);
		%ii++;
	    if(%startTagOffset >= 0)  
		{
		    %this.XMLData.setFieldValue(%node@%ii,getSubStr(%xml, %startOffset, %endTagOffset - %startOffset));
		} else {
		    %this.XMLData.setFieldValue("count_"@%node,%ii);  
			return;
		}
	}
}
//*********************************************************************
// Main XML functions
function HTTPObject::getXMLNode(%this,%node)
{
	if (%this.XMLData.getFieldValue(%node@"0") $= "")
	{
		%this.getXMLData(%node);
	}
	return %this.XMLData.getFieldValue(%node@"0");
}

function HTTPObject::getXMLListNodeCount(%this,%node)
{
	if (%this.XMLData.getFieldValue("count_"@%node) $= "")
	{
		%this.getXMLData(%node);
	}
	return %this.XMLData.getFieldValue("count_"@%node);
}

function HTTPObject::getXMLListNode(%this,%node,%line)
{
	if (%this.XMLData.getFieldValue(%node@"0") $= "")
	{
		%this.getXMLData(%node);
	}
	return %this.XMLData.getFieldValue(%node@%line);
}

//*********************************************************************
// PART3
// Example WebService Methods
//*********************************************************************
// XML Login
function HTTPObject::XMLLogin(%this,%login,%pass)
{
	%this.getXML("login&u="@%login@"&p="@%pass,"XMLLogin");
}

function HTTPObject::XMLLoginCallBack(%this)
{
	%loginresult = %this.getXMLNode("loginresult");
	%LoginOK = (%loginresult $= "SUCCESS");
	%Awaiting = (%loginresult $= "AWAITING");
	if (%LoginOK)
	{
		%this.logged=true;
		%this.userID = %this.getXMLNode("userID");
		// Account GUI
		echo("Account GUI:"@%this.userID);
	} else
	if (%Awaiting)
	{
		%this.logged=false;
		%this.userID = 0;
		// Launch Awainting validation GUI
		echo("Awaiting account GUI - Check your mail");
	} else
	{
		%this.logged=false;
		%this.userID = 0;
		// Launch Login failure GUI
		echo("Login Failure");
	}
}

//*********************************************************************
// XML User Account
function HTTPObject::XMLUserAccount(%this,%userID)
{
	%this.getXML("accountinfo&uid="@%userID,"XMLUserAccount");
}

function HTTPObject::XMLUserAccountCallBack(%this)
{
	//Show User AccountGUI
	if (%this.getXMLNode("exists")$="yes")
	{
		echo(%this.getXMLNode("LastName"));
		echo(%this.getXMLNode("FirstName"));
		echo(%this.getXMLNode("mail"));
		echo(%this.getXMLNode("Score"));
	} else
	{
		echo("Account does not exist.");
	}
}

//*********************************************************************
// XML ConnectedUsers
function HTTPObject::XMLConnected(%this)
{
	%this.getXML("connected","XMLConnected");
}

function HTTPObject::XMLConnectedCallBack(%this)
{
	//Show Connected Users GUI 
	%num = %this.getXMLListNodeCount("ID");
	if (%num$="") { echo("Nobody connected"); return; }
	echo("Users Connected : "@%num);
	// Browse each lines.
	echo("user ID : pseudo : score");
	for (%ii=0;%ii<%num ;%ii++ )
	{
		%id = %this.getXMLListNode("ID",%ii); 
		%pseudo = %this.getXMLListNode("pseudo",%ii);
		%score = %this.getXMLListNode("score",%ii);
		echo(%id@" : "@%pseudo@" : "@%score);
	}
}

To Create your own WebService Methods, you need to create one "caller" method, and one "callback" method on HTTPObject.
In the "caller" method (e.g. XMLLogin), you need to use :
%this.getXML(%query,%MyMethod);
As defined in HTTPObject::onDisconnect you need to create a callback function called HTTPObject::MyMethodCallBack()
In this callBack method, you can use the following functions :
%value = %this.getXMLNode("MyXMLNode"); // Return a single node value
%value = %this.getXMLListNode("MyXMLNode",%lineNum); // Return the node of %lineNum (for grids)
%value = %this.getXMLListNodeCount("MyXMLNode"); // Return the Number of node in the XML response.

Enjoy! Hopping it will help you to build WebServices clients for T3D.

Edit : Added OnDNSFailed, onConnectFailed functions.

#1
08/10/2010 (3:15 pm)
I keep getting A DNS error does this related to the apache ?
#2
08/11/2010 (10:05 am)
I edited this resource to make simple error's handling. If you have a DNS error, check your $WebServiceDNS :
$WebServiceDNS = "www.someurl.com";     // With no http://
$WebServicePort = 80;                   // 80 in most cases
$WebServiceURL= $WebServiceDNS@":"@$WebServicePort; 
$WebServicePath = "/";                  // path of your application
$WebServiceApplication = "WebService.php"; // WebService PHP

I don't think DNS errors comes from Apache.