Keeping Data Together (MultiPlayer)
by Jake Callery · in Torque Game Engine · 09/10/2007 (4:29 pm) · 9 replies
Hey guys,
If I want to keep track of some data per player, say a score or health or something and I am working in a multiplayer environment, what is the best way to do that?
For instance, if I have a 4 player game, is it best to keep a global list some place where each element in the list represents the score for a particular client? Or can I keep persistent data attached to each client?
It seems that I need to keep track of the scores or whatever server-side so as to help with the cheating issue. So what is the best way to represent that? Can I add persistent member data to objects in the game through script alone, or do I have to modify the actual class in the engine to keep different data?
So for the sake of conversation, lets say we have 2-4 players, and we want to keep track of the score for each.
Any thoughts on what the best way to do this is?
Thanks so much!
If I want to keep track of some data per player, say a score or health or something and I am working in a multiplayer environment, what is the best way to do that?
For instance, if I have a 4 player game, is it best to keep a global list some place where each element in the list represents the score for a particular client? Or can I keep persistent data attached to each client?
It seems that I need to keep track of the scores or whatever server-side so as to help with the cheating issue. So what is the best way to represent that? Can I add persistent member data to objects in the game through script alone, or do I have to modify the actual class in the engine to keep different data?
So for the sake of conversation, lets say we have 2-4 players, and we want to keep track of the score for each.
Any thoughts on what the best way to do this is?
Thanks so much!
#2
Each client is assigned a unique client ID when they connect to the server. This is the %client value. Though %client appears to be a local client variable, it is actually a server pointer, meaning more or less that it acts as a global variable on the server side.
Thus, if the client was given the number 4187 when they connect, "%client.score" would be 4187.score on the server side. If you were to open up the client (using a dedicated server / client setup as opposed to single client/server setup), you would find that 4187.score is actually not set on the client side.
In general, you never want the client to be able to really manipulate data such as the score in an authoritative way -- have the server keep track of the score and everything else that you think a person might want to hack in order to cheat. If you are creating a single-person game, this probably doesn't apply, but it is still good practice.
09/11/2007 (12:40 pm)
I'm fairly new to Torque, so take this with a grain of salt. Each client is assigned a unique client ID when they connect to the server. This is the %client value. Though %client appears to be a local client variable, it is actually a server pointer, meaning more or less that it acts as a global variable on the server side.
Thus, if the client was given the number 4187 when they connect, "%client.score" would be 4187.score on the server side. If you were to open up the client (using a dedicated server / client setup as opposed to single client/server setup), you would find that 4187.score is actually not set on the client side.
In general, you never want the client to be able to really manipulate data such as the score in an authoritative way -- have the server keep track of the score and everything else that you think a person might want to hack in order to cheat. If you are creating a single-person game, this probably doesn't apply, but it is still good practice.
#3
a first question you should ask about data of this type is: do all the clients need to know this data, or just one? if the answer is "just the one", then commandToClient() is what you're after. if it's the former however, for example if player B needs to be able to see player A's score, then the data should be "ghosted" via the packUpdate()/unpackUpdate() mechanism. There are many good posts in the forums about the theory and practice of ghosting data, and probably some in TDN. If you have trouble finding them, let me know and i'll try to look one up.
09/11/2007 (1:38 pm)
Jake,a first question you should ask about data of this type is: do all the clients need to know this data, or just one? if the answer is "just the one", then commandToClient() is what you're after. if it's the former however, for example if player B needs to be able to see player A's score, then the data should be "ghosted" via the packUpdate()/unpackUpdate() mechanism. There are many good posts in the forums about the theory and practice of ghosting data, and probably some in TDN. If you have trouble finding them, let me know and i'll try to look one up.
#4
Orion, in this case, yes they all need to see the data. While I'm looking up Ghosting data, here is just a thought. Would it be bad practice then to use commandToClient() in a loop that sends the new data to all of the clients individually? So, for each client, send the update score commandToClient...
Is that maybe too slow to be accurate, and therefore not a good option?
Thanks again!
09/11/2007 (4:52 pm)
Thanks for the tips guys, really appreciate it!Orion, in this case, yes they all need to see the data. While I'm looking up Ghosting data, here is just a thought. Would it be bad practice then to use commandToClient() in a loop that sends the new data to all of the clients individually? So, for each client, send the update score commandToClient...
Is that maybe too slow to be accurate, and therefore not a good option?
Thanks again!
#5
That's certainly an option if the number of clients is fairly low and the event (score changing) happens infrequently. If either of those are high, then clients may experience a glitch whenever anyone's score changes.
Note that if you want to display the score of all players, whether or not their avatar is actually nearby (eg, a realtime Score Table), then ghosting won't quite work and the commandToClient() route might be better.
However if you're only displaying the score of players when their avatar is in view or nearby,
ghosting is pretty close to ideal.
09/11/2007 (5:08 pm)
Quote:Would it be bad practice then to use commandToClient() in a loop that sends the new data to all of the clients individually?
That's certainly an option if the number of clients is fairly low and the event (score changing) happens infrequently. If either of those are high, then clients may experience a glitch whenever anyone's score changes.
Note that if you want to display the score of all players, whether or not their avatar is actually nearby (eg, a realtime Score Table), then ghosting won't quite work and the commandToClient() route might be better.
However if you're only displaying the score of players when their avatar is in view or nearby,
ghosting is pretty close to ideal.
#6
I take it ghosting is bound by scoping then correct?
The score changing is going to happen very frequently, however I think I will give it a shot
and see when it breaks.
As for ghosting, I think I'm going to work with this, this week:
tdn.garagegames.com/wiki/Torque/Networking/Making_a_Ghostable_Object
I think that should get me going on ghosting. However, I don't think I will be able to use ghosting if they get scoped.
Thanks for the tips!
*** Edit ***
Just noticed the scopeAlways flag... that might work...
09/11/2007 (5:16 pm)
Hmm...I take it ghosting is bound by scoping then correct?
The score changing is going to happen very frequently, however I think I will give it a shot
and see when it breaks.
As for ghosting, I think I'm going to work with this, this week:
tdn.garagegames.com/wiki/Torque/Networking/Making_a_Ghostable_Object
I think that should get me going on ghosting. However, I don't think I will be able to use ghosting if they get scoped.
Thanks for the tips!
*** Edit ***
Just noticed the scopeAlways flag... that might work...
#7
- when an object goes out of network scope for a given client, that client actually deletes its local instance of the object.
scopeAlways would keep the object in scope, but there's a reason player objects aren't scopeAlways.
.. actually, i have a hunch that with modern bandwidth, if your number of players is fairly low, having them be scopeAlways probably isn't that big a deal. "low" being like under 20 or something. just a hunch.
edit -
"The score changing is going to happen very frequently, however I think I will give it a shot
and see when it breaks."
- sounds good !
09/11/2007 (8:30 pm)
Yep, ghosting only happens for object which are in network scope.- when an object goes out of network scope for a given client, that client actually deletes its local instance of the object.
scopeAlways would keep the object in scope, but there's a reason player objects aren't scopeAlways.
.. actually, i have a hunch that with modern bandwidth, if your number of players is fairly low, having them be scopeAlways probably isn't that big a deal. "low" being like under 20 or something. just a hunch.
edit -
"The score changing is going to happen very frequently, however I think I will give it a shot
and see when it breaks."
- sounds good !
#8
Its not great, or elegant, but it seems to work for now. It works fine with 3 players at the same time, we'll see what happens when there is more like 8, but for now this will work:
Thanks for everyone's help!
09/16/2007 (6:29 pm)
Just to wrap this up, this is what I came up with (after about an hour no realizing I can't pass in arrays :)):Its not great, or elegant, but it seems to work for now. It works fine with 3 players at the same time, we'll see what happens when there is more like 8, but for now this will work:
Thanks for everyone's help!
//Get which client
%colClient = %thisPlayer.client;
echo("Client: " @ %colClient);
//Update the score for the client
%colClient.score++;
echo("Score:" @ %colClient.score);
//Get the number of connected clients
%numClients = ClientGroup.getCount();
//Loop through each client and set everyone's score
for(%i = 0; %i < %numClients; %i++)
{//update score on each client GUI
%currClient = ClientGroup.getObject(%i);
//Set each players score on the client
for(%j = 0; %j < %numClients; %j++)
{//send each GUI update to each client
%thisClient = ClientGroup.getObject(%j);
%thisScore = %thisClient.score;
commandToClient(%currClient, 'SetScoreCounter', %thisClient, %thisScore, %j);
}//send each GUI update to each client
}//update score on each client GUI
#9
09/16/2007 (8:31 pm)
Having a separate GUI where you send score updates is the smart way to do it - especially since score and physical state are totally separate things.
Torque 3D Owner Jake Callery
now, when they do %client.score, is that data actually stored on the client side (on the connected machine) or is it stored on the server side?
If it is stored on the server side, then I guess that might be the best way to do it?
Thanks again!