How can I get the client-side (ghost) id of an TSStatic object?
by Edward Rotberg · in Torque 3D Professional · 03/09/2010 (1:39 pm) · 11 replies
Hi,
I'm sure that from the title alone this will seem like a simple thing to do, but please read on...
Because of our game design we have to do some client-side rayCasting to collide with certain objects (let's call them Target Objects, or TOs). This NEEDS to be done on the client side for a number of reasons, and don't worry, there will be no modifications made to these TO objects server-side. I've already created a version of containerRayCast() that will do the collision on the client-side objects, so when I get a hit, I get the client-side (ghost) object ID returned. Fantastic! This is exactly what I wanted.
Now, I also have another class of objects, each of which "owns" up to 6 of these TOs. Let's call this object the Master Object or MO. So what I need to be able to do - all on the client - side, is when I get a hit on my rayCast, be able to iterate through the array of TOs owned by each MO to see if there is a match. This way I can find the owning MO.
The association is set up between the MOs and the TOs in the World Editor by having 6 TypeSimObjectPtr persist fields in the MO. This gives me the server-side object pointer to each of the TOs whose name I enter into the World Editor. I had thought that I could simply obtain the client-side (ghost) Id of these TOs at packUpdate() time by calling getGhostIndex() and passing the ID to the client for his unpackUpdate, but getGhostIndex() returns 0xFFFFFFFF. Previously I had hoped to put the MO and its TOs in the same SimGroup, but SimGroups do not exist client-side (grrrrr). Neither do object names (grrrrrr).
I have tried all sorts of things to resolve this. Nothing I have tried has led to any feasible solution, and I have spent a number of days trying both reasonable and insane ideas. I have one last thing to try that I'd really rather avoid, and that would be to pass the name of the TO object to the ghost object in the pack/unpack process so that the ghost object can actually have its name and I could do compares against that instead of ghost IDs. Sadly, this means overriding TSStatic, which will present a problem to our artists as they try to add these TOs from collada files. For this reason, I am extremely hesitant to go down this path.
To re-iterate, there are a number of reasons that the rayCast MUST NOT be done on the server-side. Just to make everything plain, this will need to run in a separated client in a multi-player environment.
While I love many things about Torque 3D, the whole client-server implementation that exists seems geared to a very limited class of games (read FPS and the like). It has been extremely cumbersome for much of what we are trying to do. Perhaps I just don't fully understand the gestalt of the paradigm chosen - this is likely the case.
In any event, if anyone has any ideas on how to address this problem, I'm all ears (makes it hard to look in the mirror though).
Thanks in advance,
= Ed =
I'm sure that from the title alone this will seem like a simple thing to do, but please read on...
Because of our game design we have to do some client-side rayCasting to collide with certain objects (let's call them Target Objects, or TOs). This NEEDS to be done on the client side for a number of reasons, and don't worry, there will be no modifications made to these TO objects server-side. I've already created a version of containerRayCast() that will do the collision on the client-side objects, so when I get a hit, I get the client-side (ghost) object ID returned. Fantastic! This is exactly what I wanted.
Now, I also have another class of objects, each of which "owns" up to 6 of these TOs. Let's call this object the Master Object or MO. So what I need to be able to do - all on the client - side, is when I get a hit on my rayCast, be able to iterate through the array of TOs owned by each MO to see if there is a match. This way I can find the owning MO.
The association is set up between the MOs and the TOs in the World Editor by having 6 TypeSimObjectPtr persist fields in the MO. This gives me the server-side object pointer to each of the TOs whose name I enter into the World Editor. I had thought that I could simply obtain the client-side (ghost) Id of these TOs at packUpdate() time by calling getGhostIndex() and passing the ID to the client for his unpackUpdate, but getGhostIndex() returns 0xFFFFFFFF. Previously I had hoped to put the MO and its TOs in the same SimGroup, but SimGroups do not exist client-side (grrrrr). Neither do object names (grrrrrr).
I have tried all sorts of things to resolve this. Nothing I have tried has led to any feasible solution, and I have spent a number of days trying both reasonable and insane ideas. I have one last thing to try that I'd really rather avoid, and that would be to pass the name of the TO object to the ghost object in the pack/unpack process so that the ghost object can actually have its name and I could do compares against that instead of ghost IDs. Sadly, this means overriding TSStatic, which will present a problem to our artists as they try to add these TOs from collada files. For this reason, I am extremely hesitant to go down this path.
To re-iterate, there are a number of reasons that the rayCast MUST NOT be done on the server-side. Just to make everything plain, this will need to run in a separated client in a multi-player environment.
While I love many things about Torque 3D, the whole client-server implementation that exists seems geared to a very limited class of games (read FPS and the like). It has been extremely cumbersome for much of what we are trying to do. Perhaps I just don't fully understand the gestalt of the paradigm chosen - this is likely the case.
In any event, if anyone has any ideas on how to address this problem, I'm all ears (makes it hard to look in the mirror though).
Thanks in advance,
= Ed =
About the author
#2
Rather, the problem is converting an array of server-side object pointers into a client-side array of ghost IDs. I should only need to do this once. Then at rayCast time I can find my "hit" ghost ID in some arrays I'd be maintaining of these ghost IDs. The problem is building the list of Ghost IDs to compare against, not in getting a ghost ID on rayCast.
Thanks though.
= Ed =
03/09/2010 (3:25 pm)
Thanks for the quick response Jesse. The problem is not getting the ghost ID for the object that gets collided with. As I explained in the original post, my modified, client-side rayCast already returns the ghost ID without having to make any server-side calls dynamically.Rather, the problem is converting an array of server-side object pointers into a client-side array of ghost IDs. I should only need to do this once. Then at rayCast time I can find my "hit" ghost ID in some arrays I'd be maintaining of these ghost IDs. The problem is building the list of Ghost IDs to compare against, not in getting a ghost ID on rayCast.
Thanks though.
= Ed =
#3
If you want to learn how the server resolves client side ghost id's then the function resolveObjectFromGhostIndex would be the place to look wouldn't it? I'm not really sure why you would need to do this so its pretty hard for me to figure out what your asking for.
03/09/2010 (3:35 pm)
I'm confused. There are two arrays. One array is on the server and one array is on the client app. The client app holds each id and when a new one is ghosted it populates an array field in the server ghost id array for that client object. So now the server has a reference to the real object the client's object of that real server object. So what are you doing exactly :/If you want to learn how the server resolves client side ghost id's then the function resolveObjectFromGhostIndex would be the place to look wouldn't it? I'm not really sure why you would need to do this so its pretty hard for me to figure out what your asking for.
#4
03/09/2010 (3:38 pm)
Wait a second. So your asking how to add the server side ID for the object to all ghosted versions of the object so that you can group them and they would be the same for everyone?
#5
It IS complicated. But if you read and understand my original post about having some Master Objects that each "own" up to 6 Target Objects the basic problem is this: Once I find the client-side ID of the object that has been "hit" by my rayCast, I need to find the MasterObject that owns it. As I stated in the original post I set up the "owning" relationship in the World Editor. Therefore, each Master Object needs to have an array of up to 6 Target Object "identifiers". These identifiers could be client-side IDs, object pointers, names, whatever I can use to store in the Master Objects array of owned objects so I can find the correct Master Object when the Target Object is hit by the rayCast.
I don't need server-side for anything during game run-time. However, when I do my association of the Target Objects to the Master Object in the World Editor, all I can get is server-side identification information. I can get either a object name or an object pointer - both of which are server-side. So I can't set my client-side Master Object with anything that I can use to identify the hit Target Object when I do my rayCast on the client side. Ideally I can find a way to add some information to the ghost Master Objects that can be used to tell whether the hit Target Object is owned by that Master object based upon the ID (all client-side).
I hope this explains things better.
= Ed =
03/09/2010 (4:02 pm)
Jese,It IS complicated. But if you read and understand my original post about having some Master Objects that each "own" up to 6 Target Objects the basic problem is this: Once I find the client-side ID of the object that has been "hit" by my rayCast, I need to find the MasterObject that owns it. As I stated in the original post I set up the "owning" relationship in the World Editor. Therefore, each Master Object needs to have an array of up to 6 Target Object "identifiers". These identifiers could be client-side IDs, object pointers, names, whatever I can use to store in the Master Objects array of owned objects so I can find the correct Master Object when the Target Object is hit by the rayCast.
I don't need server-side for anything during game run-time. However, when I do my association of the Target Objects to the Master Object in the World Editor, all I can get is server-side identification information. I can get either a object name or an object pointer - both of which are server-side. So I can't set my client-side Master Object with anything that I can use to identify the hit Target Object when I do my rayCast on the client side. Ideally I can find a way to add some information to the ghost Master Objects that can be used to tell whether the hit Target Object is owned by that Master object based upon the ID (all client-side).
I hope this explains things better.
= Ed =
#6
World Editor is a Server Side only object. If you make something in the World Editor you made it for the server. Clients are participants in server side changes through ghosting. Also, the World Editor only knows of the Server Side ID's for things. If you told it to ghost over to the clients then you have the object there with the client side stuff you just need to pick it out.
OK, so add a field when the TO is created to desginate which MO is owned by it and I add its ID to the MO placeholder field. Then the MO will know each TO and each TO will know its MO.
03/09/2010 (4:29 pm)
{EDIT}World Editor is a Server Side only object. If you make something in the World Editor you made it for the server. Clients are participants in server side changes through ghosting. Also, the World Editor only knows of the Server Side ID's for things. If you told it to ghost over to the clients then you have the object there with the client side stuff you just need to pick it out.
OK, so add a field when the TO is created to desginate which MO is owned by it and I add its ID to the MO placeholder field. Then the MO will know each TO and each TO will know its MO.
#7
This is exactly what I have done, but you cannot get ANY client-side ID information into the ghosted objects - not without extending the engine. I have already done a lot of engine extension, and I'll probably be doing more, but I just don't think you have comprehended what I'm trying to do. The World Editor is used to build the level. Objects are ghosted when the "game" is started and it is at that point that I need to find out the associations that I have set up between MOs and TOs for the client-side ghosts of these objects. The only information that I can add to the MO (not the TO) in what you describe as "add a field when the TO is created to desginate which MO is owned by it and I add its ID to the MO placeholder field" is server-side ID information which does me no good on the client-side when I am processing my rayCast hits.
I appreciate your trying to help me, but I have been programming games for a long time, and it just appears to me that you are not understanding the issue that I have presented here. I am sorry that I am unable to clarify it any further for you.
Thanks again for trying to help.
= Ed =
03/09/2010 (4:56 pm)
Jesse,This is exactly what I have done, but you cannot get ANY client-side ID information into the ghosted objects - not without extending the engine. I have already done a lot of engine extension, and I'll probably be doing more, but I just don't think you have comprehended what I'm trying to do. The World Editor is used to build the level. Objects are ghosted when the "game" is started and it is at that point that I need to find out the associations that I have set up between MOs and TOs for the client-side ghosts of these objects. The only information that I can add to the MO (not the TO) in what you describe as "add a field when the TO is created to desginate which MO is owned by it and I add its ID to the MO placeholder field" is server-side ID information which does me no good on the client-side when I am processing my rayCast hits.
I appreciate your trying to help me, but I have been programming games for a long time, and it just appears to me that you are not understanding the issue that I have presented here. I am sorry that I am unable to clarify it any further for you.
Thanks again for trying to help.
= Ed =
#8
Then open up item.cpp
Then I wanted the object to pull it out of the string so
03/09/2010 (5:28 pm)
So you just wanted to know the methods used for Instantiating the objects?//In item.h
class Item: public ShapeBase
{
public:
String mDB; //This is your unique field thats holding whatever you want the client side object to know.
}Then open up item.cpp
//I wanted to get the datablock name of the original server object and pass it to all client objects of that type so I can do a client side raycast and read its name.
//So we need to add our field transfer here in packUpdate to the client
U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
{
//I did mine in the InitialUpdateMask field
char buffer[256]; //reserve space for name
if ( mDataBlock->getName() ) //make sure this object has a name
{
mDB = StringTable->insert( mDataBlock->getName() );
dStrncpy( buffer, mDB, 256 );
}
else
{
buffer[0] = 0;
}
stream->writeString( buffer ); //send the name to the ghosting services to go to the client.Then I wanted the object to pull it out of the string so
void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
{
char buffer[256];
stream->readString( buffer );
mDB = buffer; //now the client's ghosted object knows its datablock name
}
#9
This is what I meant when I said that I had "one last thing to try: in my original post:
This is basically the same as what you are proposing except that it requires no change to the Engine source. With the method I was referring to above, I would just subclass TSStatic to create my own new object type. At that point I can include the name at initial pack/unpack as you suggest. However there are reasons I would prefer not to create a sub-class for this if I don't have to. Then the artists won't forget to create the new "special" object.
Modifying the engine the way you suggest would seem to only effect Item derived objects (objects of SimObjectType = ItemObjectType). I don't believe that TSStatics fall into that category, though of course I could modify TSStatics to do the same thing. In that case it has the advantage that the artists could continue to use TSStatics as they are accustomed to. It has the disadvantage that every TSStatic in the game gets affected. A minor problem.
All of that said, I'm now pursuing a different tact now than either of these, that has some other nice advantages. If this attempt doesn't work, I'll have to try the sub-class method.
= Ed =
03/09/2010 (6:23 pm)
Jesse,This is what I meant when I said that I had "one last thing to try: in my original post:
Quote:I have one last thing to try that I'd really rather avoid, and that would be to pass the name of the TO object to the ghost object in the pack/unpack process so that the ghost object can actually have its name and I could do compares against that instead of ghost IDs. Sadly, this means overriding TSStatic, which will present a problem to our artists as they try to add these TOs from collada files. For this reason, I am extremely hesitant to go down this path.
This is basically the same as what you are proposing except that it requires no change to the Engine source. With the method I was referring to above, I would just subclass TSStatic to create my own new object type. At that point I can include the name at initial pack/unpack as you suggest. However there are reasons I would prefer not to create a sub-class for this if I don't have to. Then the artists won't forget to create the new "special" object.
Modifying the engine the way you suggest would seem to only effect Item derived objects (objects of SimObjectType = ItemObjectType). I don't believe that TSStatics fall into that category, though of course I could modify TSStatics to do the same thing. In that case it has the advantage that the artists could continue to use TSStatics as they are accustomed to. It has the disadvantage that every TSStatic in the game gets affected. A minor problem.
All of that said, I'm now pursuing a different tact now than either of these, that has some other nice advantages. If this attempt doesn't work, I'll have to try the sub-class method.
= Ed =
#10
03/09/2010 (10:50 pm)
Well lol, I give. If you figure it out I would like to know the solution! Thanks Ed!
#11
http://www.torquepowered.com/community/forums/viewthread/111665
= Ed =
03/10/2010 (9:01 am)
Here ya go Jesse. Ryan Mounts figured this out. There was another approached being discussed in my original post on this general problem in a different thread. I just got it working on my system. Here is a link to that thread:http://www.torquepowered.com/community/forums/viewthread/111665
= Ed =
Torque Owner JesseL
Pyrotronics-Games
Example script from the game I'm working on.
I modified the Gui Object Selection Hud resource to give me the ID of the object I'm looking at in the field selected. I then called this script on client side when I had my player click the mouse button.
%obj = GuiOSH.selected; %GID = %obj.getGhostID(); commandToServer('UpgradeObject', %GID);Then my server script does this:
function serverCmdUpgradeObject(%client, %GID) { if( %GID >= 0 ) %obj = %client.resolveObjectFromGhostIndex(%GID); //now that you have a %obj on the server that represents the Ghost Object on the client you can do something with it. }