Game Development Community

dev|Pro Game Development Curriculum

How to Identify Objects from Client to Server or Server to Client

by Nathan Davies · 12/08/2003 (8:53 am) · 12 comments

The purpose of this resource is to show you how to identify objects across client/server boundaries. It is not intended to show you how to select objects, as there are resources for that already.

Objects in Torque Script are represented by IDs. For example, if you select a tree object and echo( %TreeObject ), it will output a number. This works great within the confines of the client or server code you're working in, but if you tried to send this ID to the 'other side' to work on it, you would either get an error, or access the wrong object. This is because each connected client has it's own list of IDs for objects (Though when hosting, you can access both the client and server version of an object directly).

All hope is not lost however, because Torque obviously has a way to identify objects across these boundaries, and it is done with the mighty Ghost Index. Each game connection (one on the clients, and one for each client connected to the server) has a list of objects and their corresponding IDs. These IDs are unique to the game world and do match across client/server boundaries. Accessing and using these IDs is really what this resource is about.

The changes I put in place are simple. Add the following lines to netConnection.cc:

ConsoleMethod( NetConnection, GetGhostIndex, S32, 3, 3, "" )
{
	ShapeBase *pObject;
	if( Sim::findObject( argv[2], pObject ))
		return object->getGhostIndex( pObject );

	return 0;
}

ConsoleMethod( NetConnection, ResolveGhost, S32, 3, 3, "" )
{
	S32 nIndex = dAtoi( argv[2] );
	if( nIndex != -1 )
	{
		NetObject* pObject = NULL;
		if( object->isGhostingTo())
			pObject = object->resolveGhost( nIndex );
		else if( object->isGhostingFrom())
			pObject = object->resolveObjectFromGhostIndex( nIndex );

		return pObject->getId();
	}

	return 0;
}

For some usage examples, i'm going to assume you have a method in place for selecting objects (whether it be with the mouse, or any other way).

Lets say you now have a %TreeObject selected on the client, and you want this %TreeObject to do something on the server (like mount an image, or grow a few feet).

CommandToServer( 'GrowATree', ServerConnection.GetGhostIndex( %TreeObject ));

This will send along the Ghost Index, which can be turned into the local (in this case server) ID with this:

function ServerCmdGrowATree( %Client, %TreeObjectGhostIndex )
{
	%TreeObject = %Client.ResolveGhost( %TreeObjectGhostIndex );

	// Do something with %TreeObject
}

Ok, next we'll assume you have a %TreeObject on the server and you need the client to do something with the object (such as mount an image or make it shake for a few seconds). I'm assuming you have a %Client variable at this point.

CommandToClient( %Client, 'ShakeALeaf', %Client.GetGhostIndex( %TreeObject ));

This will again send along the Ghost Index, which can be turned into the local (in this case client) ID with this:

function ClientCmdShakeALeaf( %TreeObjectGhostIndex )
{
	%TreeObject = ServerConnection.ResolveGhost( %TreeObjectGhostIndex );

	// Do something with %TreeObject
}

Please Note: Ghost Indexes are only available after the object is actually ghosted. If GetGhostIndex returns -1, this could be the reason. For example, I have my project set up so that when I click on a piece of pavement, it builds something on top of it. I now have the newly built object on the server, but the ghost index isn't available yet, so I schedule a timer to wait until it is available to inform the client about it.

About the author

Recent Blogs


#1
12/08/2003 (11:27 am)
Wow, I just implemented this for a project of mine yesterday... Crazy.
#2
12/28/2003 (5:43 am)
Just put this in, worked perfectly, thankyou.
If anyone's getting an "undeclared identifier" on "ShapeBase" when trying to use this resource, add the following to the top of netconnection.cc:

#include "game/ShapeBase.h"
#3
04/30/2004 (9:40 am)
I ve implemented this ressource for my project and it works perfectly !

Thanks
#4
04/17/2005 (12:52 pm)
Great resource! Interesting this wasn't part of the engine already. I think it might be more proper to use a NetObject instead of a ShapeBase in GetGhostIndex, since ShapeBase inherits all it's network functionality from NetObject anyway - this also fixes the "undeclared identifier - ShapeBase" error. I don't know the network code very well yet, so I don't know if there's any reason you wouldn't want this ability for a GameBase, or anything else that inherits from NetObject
#5
02/13/2006 (3:06 pm)
This resource worked perfect and was a great help. Awesomeness
#6
06/24/2006 (11:48 am)
yes!! this is great resource!!
#7
09/05/2007 (10:20 pm)
Hi,

Can anyone tell me what exactly will be a %client variable. i want to use this snippet but i dont have a %client variable and moreover the console gives an error - Unable to find object: '' attempting to call function 'GetGhostIndex'. can anyone help me out with this.

Thanks,
Raghav.
#8
09/12/2007 (6:08 pm)
Thank you for this. One suggestion, change the last part of ResolveGhost to
return pObject ? pObject->getId() : 0;
just in case you get a bad ghost index like I did when I was playing with this.
#9
09/21/2007 (5:39 am)
Awsome resource, thanks!
#10
10/22/2007 (5:49 am)
great stuff.
small correction:
else if( object->isGhostingFrom())
			pObject = object->resolveObjectFromGhostIndex( nIndex );

		return pObject->getId();
should be
else if( object->isGhostingFrom())
			pObject = object->resolveObjectFromGhostIndex( nIndex );
		if (pObject)
			return pObject->getId();
		else
			return 0;
This is to prevent (really rare) crashes, if the object is stopped scoping while the command arrives to the server.
#11
02/29/2008 (4:18 pm)
I have been having this problem:

I created an object queue called $SaveData in my client side.
$SavedData = new ScriptObject(Queue) {};
MissionCleanup.add($SavedData);
$SavedData.init();
and I want to pass it to the server
commandToServer('DataSync', ServerConnection.GetGhostIndex($SavedData));
On the server side I am trying to resolve my object and use one of it's methods like this
function serverCmdDataSync(%client, %GhostObj)
{
   ... some code here ...
   
   %obj = %client.ResolveGhost(%GhostObj);

   ... some more code ...
   %aVariable = %obj.getCurrentSize() ;
   ...
}
When I call

%aVariable = %obj.getCurrentSize() ;
I get the following console message when I try to



starter.rpg/server/scripts/DataSync.cs (121): Unknown command getCurrentSize.
Object (2621) Camera -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject
I have checked and I do not get any error messages when I call ServerConnection.GetGhostIndex OR %client.ResolveGhost
I am asuming that it has somethign to do with the location of my file queue.cs which is in commmon and both server and client execute it ...

Any help would be appreciated since it is important for me to send that data structure to the server and eventually I will be passing another data structure back to the client.

Thank you
#12
10/13/2008 (6:12 am)
Steve//

Maybe you already figured it out by now, but if you want the object to be networked you need to derive it from NetObject. In your case the NetConnection won't be able to find it in its ghost table and pass you 0, which looks like a camera.