Scoping and ghosting
by Hugo Mardolcar · in Torque Game Engine · 04/15/2004 (1:18 pm) · 17 replies
Hi people,
I'm trying to implement an inventory that is managed in server side. In order to be possible a client seeing its items, I have done the following:
On Inventory::onCameraScopeQuery() I scope all the items.
The Items don't exist in the client, so they must be ghosted, right?
The problem is:
On Inventory::packUpdate() I want to send the ids of the items, but when I call con->getGhostIndex(item) the returned value is -1, so I believe that the ghosts weren't created yet.
In the documentation it's written:
"First, the server determines what objects are in-scope for the client. This is done by calling onCameraScopeQuery() on the object which is considered the "scope" object. This is usually the player object, but it can be something else. (For instance, the current vehicle, or a object we're remote controlling.)
Second, it ghosts them to the client; this is implemented in netGhost.cc.
Finally, it sends updates as needed, by checking the dirty list and packing updates."
What is wrong in this procedure? Can't I scope an object and try to send its ID on packUpdate immediately after I scoped it?
I'm trying to implement an inventory that is managed in server side. In order to be possible a client seeing its items, I have done the following:
On Inventory::onCameraScopeQuery() I scope all the items.
The Items don't exist in the client, so they must be ghosted, right?
The problem is:
On Inventory::packUpdate() I want to send the ids of the items, but when I call con->getGhostIndex(item) the returned value is -1, so I believe that the ghosts weren't created yet.
In the documentation it's written:
"First, the server determines what objects are in-scope for the client. This is done by calling onCameraScopeQuery() on the object which is considered the "scope" object. This is usually the player object, but it can be something else. (For instance, the current vehicle, or a object we're remote controlling.)
Second, it ghosts them to the client; this is implemented in netGhost.cc.
Finally, it sends updates as needed, by checking the dirty list and packing updates."
What is wrong in this procedure? Can't I scope an object and try to send its ID on packUpdate immediately after I scoped it?
#2
Also, you can use connection->objectLocalScopeAlways(object) to make an object always in scope for a connection.
06/11/2004 (11:21 am)
AFAIK the onCameraScopeQuery is only called in the control object, this means you should implement the inventory at ShapeBase or Player.Also, you can use connection->objectLocalScopeAlways(object) to make an object always in scope for a connection.
#3
I was thinking of implementing the inventory at player and item. Player for the management side of things and item for the attributes/responsive side of things.
(Put the inventory list in player but have onPickup etc in item etc..)
Hmm.. so then if the inventory was nothing but a linked list of objects in the Player then would onCameraScopeQuery automatically know which objects were in scope? Or would that have to be implemented?
If that has to be implemented then does the ghosting need implemented? Or is once an object scoped it automatically gets ghosted by the rest of the code according to updates and such?
I was under the impression that ghosting was only for 'visible' objects in the game world, not objects or even structures such as in the player class. Or can you cheat and use the scope/ghost code as a way to have ghost like objects on the client that arent visible/interactive objects?
Just trying to dig into this ghost/scope code. Could only see the bare basics in the docs. Gunna go attack the engine code now I think.
06/11/2004 (1:53 pm)
Thanks for the response!I was thinking of implementing the inventory at player and item. Player for the management side of things and item for the attributes/responsive side of things.
(Put the inventory list in player but have onPickup etc in item etc..)
Hmm.. so then if the inventory was nothing but a linked list of objects in the Player then would onCameraScopeQuery automatically know which objects were in scope? Or would that have to be implemented?
If that has to be implemented then does the ghosting need implemented? Or is once an object scoped it automatically gets ghosted by the rest of the code according to updates and such?
I was under the impression that ghosting was only for 'visible' objects in the game world, not objects or even structures such as in the player class. Or can you cheat and use the scope/ghost code as a way to have ghost like objects on the client that arent visible/interactive objects?
Just trying to dig into this ghost/scope code. Could only see the bare basics in the docs. Gunna go attack the engine code now I think.
#4
The 'ghost' code of an object is completely implemented per class of the object. This is because the states of the object have to be manually created in the code and the bit masks have to be designed to mean something on each end.
That is, we define the states then define the bitmasks to match the states.
Then we write the pack code to put the bitmasks to use.
Then we reverse the process with unpack to get it out at the other end.
Yes? I havnt found any other major ghost explanation code around so I figured if I can figure this out I would post it and pass it to someone else. :D
Still have to figure out how the ghostable is determined. This is all nice and dandy but if the inventory never gets ghosted cuz its not a visible object, im out of luck. :(
06/11/2004 (2:11 pm)
I think I am begining to understand this better:The 'ghost' code of an object is completely implemented per class of the object. This is because the states of the object have to be manually created in the code and the bit masks have to be designed to mean something on each end.
That is, we define the states then define the bitmasks to match the states.
Then we write the pack code to put the bitmasks to use.
Then we reverse the process with unpack to get it out at the other end.
Yes? I havnt found any other major ghost explanation code around so I figured if I can figure this out I would post it and pass it to someone else. :D
Still have to figure out how the ghostable is determined. This is all nice and dandy but if the inventory never gets ghosted cuz its not a visible object, im out of luck. :(
#5
So this is where the scope is determined for objects in relationship to player.
If I make the linked list in player I should be able to scope the items in the linked list here. Then they will get updated accordingly. I hope. (If ive build the pack/unpack stuff properly.)
Any glaring holes? Mistakes? Idiocies? :D
06/11/2004 (2:17 pm)
AHH.. Found this nuget:void Player::onCameraScopeQuery(NetConnection *connection, CameraScopeQuery *query)
{
// First, we are certainly in scope, and whatever we're riding is too...
if(mControlObject.isNull() || mControlObject == mMount.object)
Parent::onCameraScopeQuery(connection, query);
else
{
connection->objectInScope(this);
if (isMounted())
connection->objectInScope(mMount.object);
mControlObject->onCameraScopeQuery(connection, query);
}
}So this is where the scope is determined for objects in relationship to player.
If I make the linked list in player I should be able to scope the items in the linked list here. Then they will get updated accordingly. I hope. (If ive build the pack/unpack stuff properly.)
Any glaring holes? Mistakes? Idiocies? :D
#6
Fooey. :D
Might make more sense to create an inventory object and assign it to a variable in player and then always scope the inventory object individually for each player. Might be able to control the scope much more directly and not have accidental scopings. :D
(Be kind of nasty if the client could view other people's inventory. :D)
06/11/2004 (5:18 pm)
Wait a minute. If I put the ghost code in here then I think each person's inventory will be ghosted to the client when they come into view. I think this is intended to be used for recognizing other players as well.Fooey. :D
Might make more sense to create an inventory object and assign it to a variable in player and then always scope the inventory object individually for each player. Might be able to control the scope much more directly and not have accidental scopings. :D
(Be kind of nasty if the client could view other people's inventory. :D)
#7
Assumptions:
--The client (through your patch/distribution channels) will have all needed images, commands (gui scripts) shapes, etc. required for a player to view/manipulate their inventory.
--The server actually stores the inventory (in a db I would guess), and maintains "control" over what actually is in the inventory (for security/anti-hack purposes)
--The client can "ask" the server to perform item manipulation tasks (equip, remove, destroy, drop, etc.)
If the above assumptions are close, you could basically use a simple protocol for item manipulation across the (stock) commandToServer and commandToClient to pass (low bandwidth) information to the client so they can manipulate their inventory. Something along the lines of (use case based):
Player: View Inventory
commandToServer-->dumpInventoryContents
...server builds inventory list
commandToClient-->updateInventory(InventoryList)
:client now has a list of the "current" items in their inventory: mostly item_id's and counts.
Player:Wear Item
commandToServer-->requestWearItem(ItemId)
...server validates wear attempt (has item, can wear, etc.)
...server processes wearItem action on the player object (for ghosting purposes at the player object level)
commandToClient-->wearItem(ItemId)
:client "wears" item
This is basic, and (obviously) psuedo-code, but you possibly may be trying to go too "low level" with the network interface. Assuming that you have objects/items that the client can "know about" ahead of time, you really only need to send across updates to the particular item(s) being manipulated, and any game state changes are handled server side. AFAIK, the ghosting code is intended for objects that -must- be continuously updated across all clients that "observe" that object (as you mentioned), and in general, inventory items are probably a bit less "intense" when it comes to continuous low level update cycles.
Edit: The reason I point out the (probably small) difference between ghosting actual inventory contents, and what the player is wearing is because you probably don't need to tell every client what player_x has in their inventory when only player_x is the one really looking, but you do need to tell all clients (in scope, etc.) when player_x wears a new item. In all cases, the server is aware of what is going on, but what gets ghosted and what doesn't is more a matter of game flow in this case.
In other words, the commandToxxxx level is designed to pass info to one and only one client, while the ghosting level is designed to pass info to all clients in scope...
EDIT: removed misleading %this and replaced with ItemId.
06/11/2004 (5:34 pm)
I have to admit I haven't put a lot of design effort into inventories yet, but off the top of my head:Assumptions:
--The client (through your patch/distribution channels) will have all needed images, commands (gui scripts) shapes, etc. required for a player to view/manipulate their inventory.
--The server actually stores the inventory (in a db I would guess), and maintains "control" over what actually is in the inventory (for security/anti-hack purposes)
--The client can "ask" the server to perform item manipulation tasks (equip, remove, destroy, drop, etc.)
If the above assumptions are close, you could basically use a simple protocol for item manipulation across the (stock) commandToServer and commandToClient to pass (low bandwidth) information to the client so they can manipulate their inventory. Something along the lines of (use case based):
Player: View Inventory
commandToServer-->dumpInventoryContents
...server builds inventory list
commandToClient-->updateInventory(InventoryList)
:client now has a list of the "current" items in their inventory: mostly item_id's and counts.
Player:Wear Item
commandToServer-->requestWearItem(ItemId)
...server validates wear attempt (has item, can wear, etc.)
...server processes wearItem action on the player object (for ghosting purposes at the player object level)
commandToClient-->wearItem(ItemId)
:client "wears" item
This is basic, and (obviously) psuedo-code, but you possibly may be trying to go too "low level" with the network interface. Assuming that you have objects/items that the client can "know about" ahead of time, you really only need to send across updates to the particular item(s) being manipulated, and any game state changes are handled server side. AFAIK, the ghosting code is intended for objects that -must- be continuously updated across all clients that "observe" that object (as you mentioned), and in general, inventory items are probably a bit less "intense" when it comes to continuous low level update cycles.
Edit: The reason I point out the (probably small) difference between ghosting actual inventory contents, and what the player is wearing is because you probably don't need to tell every client what player_x has in their inventory when only player_x is the one really looking, but you do need to tell all clients (in scope, etc.) when player_x wears a new item. In all cases, the server is aware of what is going on, but what gets ghosted and what doesn't is more a matter of game flow in this case.
In other words, the commandToxxxx level is designed to pass info to one and only one client, while the ghosting level is designed to pass info to all clients in scope...
EDIT: removed misleading %this and replaced with ItemId.
#8
Key piece of information that helped me understand ghosting.
I was looking at the ghosting code trying to figure out when it gets updated and if it is as you say it is then your precisely right. Ghosting the inventory would be overkill. Even open up a hackability if one client can read another character's inventory through memory calls with an external program.
There is an inventory resource out there that uses the client/server commands but I thought it was a bit too ineffecient. I was thinking low leveling it might make it more effecient. But its looking like your saying that the client/server commands are 'effecient enough' for such an application. At this point I think I am going to take your word for it. :D
I still have to implement an actual object based inventory system since the basic one only deals with unit counts. (how many bullets how much health etc)
But I bet I can do almost all of that in script if I do it right. Maybe make an inventory class at the most so long as I rewrite the Item information correctly. Does mean we'll need subclasses for probably every type or atleast category of item but thats to be expected.
Again, thanks. DEFINITELY thanks for the detailed posting.
06/11/2004 (6:00 pm)
Quote:
In other words, the commandToxxxx level is designed to pass info to one and only one client, while the ghosting level is designed to pass info to all clients in scope...
Key piece of information that helped me understand ghosting.
I was looking at the ghosting code trying to figure out when it gets updated and if it is as you say it is then your precisely right. Ghosting the inventory would be overkill. Even open up a hackability if one client can read another character's inventory through memory calls with an external program.
There is an inventory resource out there that uses the client/server commands but I thought it was a bit too ineffecient. I was thinking low leveling it might make it more effecient. But its looking like your saying that the client/server commands are 'effecient enough' for such an application. At this point I think I am going to take your word for it. :D
I still have to implement an actual object based inventory system since the basic one only deals with unit counts. (how many bullets how much health etc)
But I bet I can do almost all of that in script if I do it right. Maybe make an inventory class at the most so long as I rewrite the Item information correctly. Does mean we'll need subclasses for probably every type or atleast category of item but thats to be expected.
Again, thanks. DEFINITELY thanks for the detailed posting.
#9
Originally my idea was to maintain a server and client inventory simultainously with the client being slave to the server. (no back updating for security reasons)
I still think that would be a little more effecient then having the client download the entire inventory each time it wants to display it. (Which is what I think that tutorial I saw was doing.)
Can you think of a good place to put the listed client inventory?
I figure I can put the server side inventory list just as a linked list in player but I was wondering if there is a copy of player on the client that I could use or if there is some other object that is a daddy object for the client.
I guess if I had to i could create it in the InventoryGui but that seems kind of sloppy to me code wise.
(This was the original problem I couldnt get my head around rewriting the original tutorial until I bumped into the ghosting stuff.)
Thanks if you can and even if you cant help.
06/11/2004 (6:19 pm)
One question if you dont mind.Originally my idea was to maintain a server and client inventory simultainously with the client being slave to the server. (no back updating for security reasons)
I still think that would be a little more effecient then having the client download the entire inventory each time it wants to display it. (Which is what I think that tutorial I saw was doing.)
Can you think of a good place to put the listed client inventory?
I figure I can put the server side inventory list just as a linked list in player but I was wondering if there is a copy of player on the client that I could use or if there is some other object that is a daddy object for the client.
I guess if I had to i could create it in the InventoryGui but that seems kind of sloppy to me code wise.
(This was the original problem I couldnt get my head around rewriting the original tutorial until I bumped into the ghosting stuff.)
Thanks if you can and even if you cant help.
#10
That being said, we are using this same general principle (a self-designed protocol used via commandToxxxx) in other aspects of our design, and haven't had any serious issues as of yet.
Based on my personal commercial game play (Everquest, Shadowbane, etc.), The concept of "dumpInventoryToClient" isn't rare. There are most probably ways to optimize it, but you need to allow for other ways of an inventory being changed that a specific client may not be directly "aware" of....for instance, a thief "pick-pockets" from your inventory. You -could- immediately update the target client's inventory, but that would give away the fact that the thief was stealing (to a hacker that was sniffing packets for example)...so why it may seem inefficient to dump an entire inventory to a client every time they ask, it does deserve design attention IMO.
06/11/2004 (6:42 pm)
Heheh...as I said, I haven't had enough need (yet) to put a lot of design time to it, and in addition, I'm still new to the Torque mechanics myself, so your follow-up questions are beyond my current expertise for sure, and my post may be flawed in any case, so don't treat it as gospel!That being said, we are using this same general principle (a self-designed protocol used via commandToxxxx) in other aspects of our design, and haven't had any serious issues as of yet.
Based on my personal commercial game play (Everquest, Shadowbane, etc.), The concept of "dumpInventoryToClient" isn't rare. There are most probably ways to optimize it, but you need to allow for other ways of an inventory being changed that a specific client may not be directly "aware" of....for instance, a thief "pick-pockets" from your inventory. You -could- immediately update the target client's inventory, but that would give away the fact that the thief was stealing (to a hacker that was sniffing packets for example)...so why it may seem inefficient to dump an entire inventory to a client every time they ask, it does deserve design attention IMO.
#11
I have a class named MyItem that extends NetObject. It's ghostable but the flag scopeAlways isn't marked.
The Player may have several instances of this class. On Player::onCameraScopeQuery(), I add to scope all items the Player has.
This works well, if I only want to transmit the Players items.
But now, I want to do another thing. I want to transmit not only the Player's items, but also the Players' items that are inside of my vision's field.
In other words, when I scope a Player, I want also to scope objects that are associated with it.
How can I do that?
ShapeBase::onCameraScopeQuery() selects all objects that should be scoped. Is there a way to get the objects that this method has selected?
06/22/2004 (7:03 am)
Imagine the following solution:I have a class named MyItem that extends NetObject. It's ghostable but the flag scopeAlways isn't marked.
The Player may have several instances of this class. On Player::onCameraScopeQuery(), I add to scope all items the Player has.
This works well, if I only want to transmit the Players items.
But now, I want to do another thing. I want to transmit not only the Player's items, but also the Players' items that are inside of my vision's field.
In other words, when I scope a Player, I want also to scope objects that are associated with it.
How can I do that?
ShapeBase::onCameraScopeQuery() selects all objects that should be scoped. Is there a way to get the objects that this method has selected?
#12
06/22/2004 (7:23 am)
Ghosting inventory items is probably not the best solution. It seems like it would be better to do an InventoryManager class and transmit state using NetEvents.
#13
i'm focusing primarily on scripting, in order to maintain a high degree of cross platform compatibility (although new classes in c++ seems to work alright too, as long as you don't atart folling around with the OS related stuff)...
and i've arrived at the same, or a similar place as you guys... communicating with the server objects and propogating the server objects across to the clients...
it took me three days to figure out how to instantiate an object on the server so that it would ghost down to the clients... but now i'm climbing the wall trying to get an id or handle... or some sort of one to one relationship of a server object on the client so that i can manipulate it from the client, and the results would be seen by all other clients...
this aspect of the engine is a key point in the understanding of how to work with Torque... and i feel that more emphasis should be made to 'publicize' this...
it should be more documented, not only with references, but some simple scripting and code examples...
without a good understanding of this key part of the engine's functionality, anyone attempting to use the engine is just spinning their wheels...
--Mike
06/20/2005 (7:31 am)
I'm at the point where i'm taking Torque seriously, and am making a concerted effort to come to grips with the basic concepts that drive the engine, and how to code em... i'm focusing primarily on scripting, in order to maintain a high degree of cross platform compatibility (although new classes in c++ seems to work alright too, as long as you don't atart folling around with the OS related stuff)...
and i've arrived at the same, or a similar place as you guys... communicating with the server objects and propogating the server objects across to the clients...
it took me three days to figure out how to instantiate an object on the server so that it would ghost down to the clients... but now i'm climbing the wall trying to get an id or handle... or some sort of one to one relationship of a server object on the client so that i can manipulate it from the client, and the results would be seen by all other clients...
this aspect of the engine is a key point in the understanding of how to work with Torque... and i feel that more emphasis should be made to 'publicize' this...
it should be more documented, not only with references, but some simple scripting and code examples...
without a good understanding of this key part of the engine's functionality, anyone attempting to use the engine is just spinning their wheels...
--Mike
#14
Maybe I'm missing something, but ghosting inventories on the server seems.... overly complex... for an inventory. The server SHOULD be the final word on all game-related transactions for simple security's sake. Why would the client need to know anything about whats in the player's inventory if not to simply display it? I'm not trying to be argumentative, I simply don't understand what in-game inventory requirement you could have that wouldn't be best handled by server-side scripting.
I've seen a number of inventory examples in the Resources section, and a few even require engine modifications. I've avoided them because I've always been from the school of thought that says that inventory code and control is always best handled server-side and reported to the client via (Stephen's suggestion) commandToClient and commandToServer.
You might be getting confused because you are trying to make a mountain out of a molehill. :)
06/20/2005 (4:33 pm)
@David: Maybe I'm missing something, but ghosting inventories on the server seems.... overly complex... for an inventory. The server SHOULD be the final word on all game-related transactions for simple security's sake. Why would the client need to know anything about whats in the player's inventory if not to simply display it? I'm not trying to be argumentative, I simply don't understand what in-game inventory requirement you could have that wouldn't be best handled by server-side scripting.
I've seen a number of inventory examples in the Resources section, and a few even require engine modifications. I've avoided them because I've always been from the school of thought that says that inventory code and control is always best handled server-side and reported to the client via (Stephen's suggestion) commandToClient and commandToServer.
You might be getting confused because you are trying to make a mountain out of a molehill. :)
#15
For quick reference since you didn't really describe what exactly you are trying to do, look for the console method "resolveObjectFromGhostIndex(%ghostID);--it can be used on the server side to map a specific client's ghost ID to an authoritative server ID.
06/27/2005 (6:32 am)
Have you looked at the documentation generated by dOxygen for networked objects? It's pretty clear, and there are dozens of examples within both script and code demonstrating the various mechanisms available to meet your needs.For quick reference since you didn't really describe what exactly you are trying to do, look for the console method "resolveObjectFromGhostIndex(%ghostID);--it can be used on the server side to map a specific client's ghost ID to an authoritative server ID.
#16
e.g.: NetConnection::objectInScope(NetObject *thisGuy, bool inScope);
I, too, have scoping needs where ideas of camera frustrum are irrelevant, and having access to a simple, general scoping mechanism would solve the issue in a split second.
tone
01/03/2006 (8:04 am)
Why are we given a NetConnection::objectInScope() but it doesn't take a boolean parameter so we can also take something OUT of scope?e.g.: NetConnection::objectInScope(NetObject *thisGuy, bool inScope);
I, too, have scoping needs where ideas of camera frustrum are irrelevant, and having access to a simple, general scoping mechanism would solve the issue in a split second.
tone
#17
%objectID.scopeToClient(%client);
and
%objectID.clearScopeToClient(%client);
objectInScope() is a boolean test, not a helper/accessor.
01/03/2006 (5:36 pm)
You are looking for the ConsoleMethods (used in script):%objectID.scopeToClient(%client);
and
%objectID.clearScopeToClient(%client);
objectInScope() is a boolean test, not a helper/accessor.
Torque Owner David Schwanke
I've got the same situation.