Associating gameconnection with missioncleanup
by Keith "The Dip" Wagner · in Torque Game Engine · 02/10/2005 (6:21 am) · 7 replies
In my GameConnection::createPlayer I add some attributes to the newly-added player, like this:
I added swim functionality as shown here (and I agree with the author, after trying every other swim function on this site, this one is the best). The trick is each player can have a different speed (set in %player.speed). This works fine when the swim function (called by the updatemove in player.cc) operates on the missioncleanup group. It also operates on the gameconnection group of player objects, which do not have the speed variable, and so far I have not been able to associate the two in any way. The end result is that all players 'shake' back and forth when they swim, because the objects (missioncleanup vs gameconnection) are moving at different speeds.
The swim function, in player.cs:
MissionCleanup.add(%player); %player.speed = (different number depending on case);These attributes are applied as tagged fields to the player object in the missioncleanup group. How do I apply these same fields to the player object in the gameconnection group?
I added swim functionality as shown here (and I agree with the author, after trying every other swim function on this site, this one is the best). The trick is each player can have a different speed (set in %player.speed). This works fine when the swim function (called by the updatemove in player.cc) operates on the missioncleanup group. It also operates on the gameconnection group of player objects, which do not have the speed variable, and so far I have not been able to associate the two in any way. The end result is that all players 'shake' back and forth when they swim, because the objects (missioncleanup vs gameconnection) are moving at different speeds.
The swim function, in player.cs:
function Armor::swim(%this, %obj)
{
%position = %obj.getTransform();
%pos = getWords(%position, 0, 2);
%eye = %obj.getEyeVector();
%swimspeed = 2 * %obj.speed;
%xx = getWord(%eye, 0) * %swimspeed;
%yy = getWord(%eye, 1) * %swimspeed;
%zz = getWord(%eye, 2) * %swimspeed;
%vec = (%xx SPC %yy SPC %zz);
%obj.applyImpulse(%pos, VectorScale(%vec, %obj.getDataBlock().mass));
}About the author
#2
game.cs:
player.cs:
Now, the net result of this is the following from the console log:
DEBUG: getswimspeed: obj:1508, obj.speed:, obj.client:, obj.client.speed:
DEBUG: getswimspeed: obj:1504, obj.speed:1, obj.client:1491, obj.client.speed:1
1508 is the gameconnection (edit: serverconnection, sorry) player, and 1504 is the missioncleanup player. One gets a client, and even gets that speed set. The other has no effect whatsoever. Both call the swim function every time the player moves in water.
02/10/2005 (3:02 pm)
The problem is, I don't always have the player object. After your suggestion, I tried covering all my bases:game.cs:
function GameConnection::createPlayer(%this, %spawnPoint)
{
....
MissionCleanup.add(%player);
%player.speed = %speed;
%player.client.speed = %speed;
// Give the client control of the player
%this.player = %player;
%this.speed = %player.speed;
%this.setControlObject(%player);
}player.cs:
function Armor::swim(%this, %obj)
{
echo("DEBUG: getswimspeed: obj:" @ %obj @ ", obj.speed:" @ %obj.speed @ ", obj.client:" @ %obj.client @ ", obj.client.speed:" @ %obj.client.speed);
(rest is same as above)
}Now, the net result of this is the following from the console log:
DEBUG: getswimspeed: obj:1508, obj.speed:, obj.client:, obj.client.speed:
DEBUG: getswimspeed: obj:1504, obj.speed:1, obj.client:1491, obj.client.speed:1
1508 is the gameconnection (edit: serverconnection, sorry) player, and 1504 is the missioncleanup player. One gets a client, and even gets that speed set. The other has no effect whatsoever. Both call the swim function every time the player moves in water.
#3
the MissionCleanup() simGroup has one purpose: store references to objects that you want deleted when the mission ends. It does nothing else. It shouldn't be even thought about when you are working on client objects, player objects, or any other object--you simply park a reference to any object that you want deleted at the end of the mission and forget about it.
The object is still an "object", and you never need to worry about the fact that it is "in" the MissionGroup, and the object is not any different from what you are calling the "gameconnection player".
Now, on to your issue:
Please keep in mind that I haven't had the time to trace through the code completely, but one possibility here is that in the first function (GameConnection::createPlayer() ), you are running the script on the "server" side of the executable, but the Armor::swim() is being called from a client side script. Object ID's do not transfer directly from server to client. For every server object, each client (even if you are running a "single player" mode) is told by the server to make a "Ghost Object" of the server's object, and the object id -will be different-. Not only that, but the object itself is different, and will only have values that the server side object has if you pass that data via pack()/unPack() in the player object source code. (You also have to expose the variables to script).
It's hard to picture the separation between server and client, especially when you are doing a single player execution, but understanding how ghost objects work is pretty critical to successful coding/scripting in TGE.
There are other possibilities as well, if the assumption I made above is incorrect--but those are related to how the functions are called, and by whom. You'll need to track object numbers down through each call to really get a strong feel for what is happening, but please keep in mind my opening statement: MissionCleanup() means nothing except for the fact that it is a simGroup of references to your objects that you want the scripts to automatically delete for you at the end of the mission.
02/10/2005 (3:43 pm)
I've brought this up in a different thread, but there is something very fundamental that I think you are missing:the MissionCleanup() simGroup has one purpose: store references to objects that you want deleted when the mission ends. It does nothing else. It shouldn't be even thought about when you are working on client objects, player objects, or any other object--you simply park a reference to any object that you want deleted at the end of the mission and forget about it.
The object is still an "object", and you never need to worry about the fact that it is "in" the MissionGroup, and the object is not any different from what you are calling the "gameconnection player".
Now, on to your issue:
Please keep in mind that I haven't had the time to trace through the code completely, but one possibility here is that in the first function (GameConnection::createPlayer() ), you are running the script on the "server" side of the executable, but the Armor::swim() is being called from a client side script. Object ID's do not transfer directly from server to client. For every server object, each client (even if you are running a "single player" mode) is told by the server to make a "Ghost Object" of the server's object, and the object id -will be different-. Not only that, but the object itself is different, and will only have values that the server side object has if you pass that data via pack()/unPack() in the player object source code. (You also have to expose the variables to script).
It's hard to picture the separation between server and client, especially when you are doing a single player execution, but understanding how ghost objects work is pretty critical to successful coding/scripting in TGE.
There are other possibilities as well, if the assumption I made above is incorrect--but those are related to how the functions are called, and by whom. You'll need to track object numbers down through each call to really get a strong feel for what is happening, but please keep in mind my opening statement: MissionCleanup() means nothing except for the fact that it is a simGroup of references to your objects that you want the scripts to automatically delete for you at the end of the mission.
#4
The armor::swim is in the player.cs and as far as I can tell, it runs solely on the server. I have echo()'s in there for debugging, and none of them display for the client. The host of the game, however, shows 2 lines per player. Armor::swim gets called from updateMove in player.cc in the source.
Looking at the tree, the id's correspond to a player object in the missioncleanup - simgroup, and a playerobject in serverconnection - gameconnection (if I am the host, or single player. If I am a client, I only see the second one).
Maybe I should rephrase my question. How do you allow players variable speeds, which can change during the game (a 'speed boost' powerup, for instance)? If I set a variable on the client's object, how do I get the engine to use it? There appears to be no relation back from the ghosted object to the client that it is ghosting.
02/10/2005 (6:37 pm)
Thanks for the info. I must have missed that kind of distinction from the docs I read. I was just going by what 'looked right' with limited knowledge. The armor::swim is in the player.cs and as far as I can tell, it runs solely on the server. I have echo()'s in there for debugging, and none of them display for the client. The host of the game, however, shows 2 lines per player. Armor::swim gets called from updateMove in player.cc in the source.
void Player::updateMove(const Move* move)
{
...
if (!isMounted())
if (mWaterCoverage >= 0.8f)
Con::executef(mDataBlock,3,"swim",scriptThis());
}Looking at the tree, the id's correspond to a player object in the missioncleanup - simgroup, and a playerobject in serverconnection - gameconnection (if I am the host, or single player. If I am a client, I only see the second one).
Maybe I should rephrase my question. How do you allow players variable speeds, which can change during the game (a 'speed boost' powerup, for instance)? If I set a variable on the client's object, how do I get the engine to use it? There appears to be no relation back from the ghosted object to the client that it is ghosting.
#5
Using pack/unpack will allow the server to automatically update the player's info to the appropriate clients (whomever is in scope, determined by onCameraScopeQuery()--keep in mind this is for the viewing client's camera,, not the controlling client.).
There is another thread that is currently active regarding RPG stats, and should provide an excellent model for what you want to do.
02/10/2005 (6:58 pm)
You need to declare it in source code, as part of the player (not the playerData), and then provide access methods to modify it. You also want to update it to the client via pack/unpack, as well as provide console methods so that your scripts can read/modify the variable.Using pack/unpack will allow the server to automatically update the player's info to the appropriate clients (whomever is in scope, determined by onCameraScopeQuery()--keep in mind this is for the viewing client's camera,, not the controlling client.).
There is another thread that is currently active regarding RPG stats, and should provide an excellent model for what you want to do.
#6
02/10/2005 (7:01 pm)
Thanks again for the info. Yes, first thing i tried was adding a variable and changing it in the player data block, and I quickly realized it applied globally to everyone.
#7
02/10/2005 (7:40 pm)
Btw, this is a public forum, so we need to stop talking actual code here. You probably want to take this to the private SDK forums!
Zod
%player.client.speed = (different number depending on case);
I wouldn't even bother attaching it to the player object. You can just read it from the client.. %speed = %player.client.speed; if you hve the player object.
Client = gameconnection
Player = object under the clients control