Updating scores during multiplayer and showing on GUI
by Andy Hawkins · in Technical Issues · 08/10/2008 (9:19 am) · 11 replies
I have the following code to shoot a missile, tag it with the client who shot it then a bunch of methods to eventually draw the score for shooting a target on the GUI. Problem is when it gets down to %obj.client.incTargetScore(1) down in onEnterTrigger sometimes %obj.client doesn't exist.
Also the client always displays the score in the GUI as ""Colours: Kills:" with no scores drawn and on the server it's always "Colours: 2 Kills: 0" (assuming you scored 2)
The end game scores are correct however. So the score are being tracked but I keep getting these errors in ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj) when it happens. It's like the projectile object gets dropped sometimes.
So how do I make sure scores are passed from the server to client UI's reliably everytime please?
GUI (client/ui/playGui.gui)
Projectile code on server/scripts ** sets client for itself
Trigger code on server/scripts (to detect a projectile hitting the trigger)
Score code in common/clientScripts
Also the client always displays the score in the GUI as ""Colours: Kills:" with no scores drawn and on the server it's always "Colours: 2 Kills: 0" (assuming you scored 2)
The end game scores are correct however. So the score are being tracked but I keep getting these errors in ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj) when it happens. It's like the projectile object gets dropped sometimes.
So how do I make sure scores are passed from the server to client UI's reliably everytime please?
Quote:
scriptsAndAssets/server/scripts/ProjectileTrigger.cs (18): Unable to find object: '' attempting to call function 'incTargetScore'
scriptsAndAssets/server/scripts/ProjectileTrigger.cs (29): Unknown command getClassName.
GUI (client/ui/playGui.gui)
new GuiTextCtrl(Score) {
canSaveDynamicFields = "1";
Enabled = "1";
isContainer = "0";
Profile = "_GAME_Gui18TextProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "542 54";
Extent = "215 25";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Variable = "Score";
ToolTip = "Your score so far.";
hovertime = "1000";
Margin = "0 0 0 0";
Padding = "0 0 0 0";
AnchorTop = "1";
AnchorBottom = "0";
AnchorLeft = "1";
AnchorRight = "0";
maxLength = "1024";
};Projectile code on server/scripts ** sets client for itself
// Create the projectile object
%p = new (%this.projectileType)() {
dataBlock = %projectile;
initialVelocity = %muzzleVelocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client; //<--------------------
on = "true";
};
MissionCleanup.add(%p);
return %p;Trigger code on server/scripts (to detect a projectile hitting the trigger)
function ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj)
{
Parent::onEnterTrigger(%this,%trigger,%obj);
if(%obj.getClassName() $= "Projectile" ) //<--------------------
{
error("ProjectileTrigger::onEnterTrigger");
messageClient(%obj.client, 'MsgClient', 'Trigger Hit With Projectile');
%obj.client.incTargetScore(1); //<--------------------
}
commandToClient( %obj.client, 'checkMission', %obj.client);
}Score code in common/clientScripts
function clientCmdcheckMission(%client)
{
Score.text = "Colours: " @ %client.targetScore @ " Kills: " @ %client.score;
}
#2
But also the point is when it gets into the ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj) - %obj is sometimes nothing.
I really need a rock-solid system of scoring when triggers or objects get hit which is broadcasted to all clients.
08/10/2008 (5:37 pm)
Yes it appears to remember the score for each client because on the endGameGui it has stored each kill and score properly. It's just not showing on the clients gui in game. In fact it also seems to work on the in-built playListGui when you press F2 - just not the on-screen score GUI I'm showing.But also the point is when it gets into the ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj) - %obj is sometimes nothing.
I really need a rock-solid system of scoring when triggers or objects get hit which is broadcasted to all clients.
#3
08/10/2008 (5:38 pm)
If it helps I'll upload a build for you to check. It's for a game jam due this Friday, so I'm trying to keep it simple.
#5
08/10/2008 (6:43 pm)
I will take a look at it. You should really really take down that link though. This is a public forum btw.
#6
2. I would remove Variable = "Score" in your Score-Gui because it could possibly confuse things.
3. It looks like you are calling %client.incTargetScore, and it is incrementing it and the number after "color" IS getting changed on the client side gui. The score it looks like is setup to change with a call to %client.incScore, which you aren't calling anywhere. Is that the problem?
4. You are calling incTargetScore on both trigger-enter and exit, not sure why, but this is incrimenting it twice.
5. Also, it looks like the trigger is sometimes not actually getting hit when I shoot at it. I didn't see any code for removing or disabling triggers after one shot so I assume its just because the trigger needs to be made a little bit larger.
08/10/2008 (7:44 pm)
1. %client is a serverside object and your code will not work in a multiplayer situation the way it is now. 2. I would remove Variable = "Score" in your Score-Gui because it could possibly confuse things.
3. It looks like you are calling %client.incTargetScore, and it is incrementing it and the number after "color" IS getting changed on the client side gui. The score it looks like is setup to change with a call to %client.incScore, which you aren't calling anywhere. Is that the problem?
4. You are calling incTargetScore on both trigger-enter and exit, not sure why, but this is incrimenting it twice.
5. Also, it looks like the trigger is sometimes not actually getting hit when I shoot at it. I didn't see any code for removing or disabling triggers after one shot so I assume its just because the trigger needs to be made a little bit larger.
#7
I believe the colour changes you mention may be a clue as to part of the problem - because yes the colours keep changing in the font - like it's corrupted. Also I see the disconnect icon when onscreen scores are updated.
The incScore is only used to count kills.
I knew that system worked so I copied it for the targetScore.
What do you mean by
...how should it be?
08/10/2008 (8:27 pm)
Yeah the triggers are only just in there right now with no disable or anything so that is expected behaviour - scoring twice etc. I will eventually replace those with collidable objects that disappear.I believe the colour changes you mention may be a clue as to part of the problem - because yes the colours keep changing in the font - like it's corrupted. Also I see the disconnect icon when onscreen scores are updated.
The incScore is only used to count kills.
I knew that system worked so I copied it for the targetScore.
What do you mean by
Quote:
1. %client is a serverside object and your code will not work in a multiplayer situation the way it is now.
...how should it be?
#8
The server has a gameConnection object for each client that connects to it. One of those is the %client object you are setting "score" on as a dynamic field. The client has one gameConnection object called "ServerConnection" which represents the connection to the server.
Since in a singleplayer situation the client is also the server, clientside scripts can access serverside objects and things work out. In a multiplayer game your client will get the object id of the server-side gameconnection object in function clientCmdcheckMission(%client) but that object will not exist there and therefore the "id" is just a meaningless string with four numbers.
It looks to me like you can just pass the score itself to clientCmdcheckMission rather than the client id.
08/10/2008 (11:33 pm)
Hm, i didn't notice the color changing (maybe it was) but I did notice the number after color changing. The reason the number after score is not changing is because you never actually call incScore, only incTargetScore.The server has a gameConnection object for each client that connects to it. One of those is the %client object you are setting "score" on as a dynamic field. The client has one gameConnection object called "ServerConnection" which represents the connection to the server.
Since in a singleplayer situation the client is also the server, clientside scripts can access serverside objects and things work out. In a multiplayer game your client will get the object id of the server-side gameconnection object in function clientCmdcheckMission(%client) but that object will not exist there and therefore the "id" is just a meaningless string with four numbers.
It looks to me like you can just pass the score itself to clientCmdcheckMission rather than the client id.
#9
Looking at this code still confuses me. Is it only the host who is dolling out the points? I assume so. I'm assuming if you are a client you never run server/scripts/games.cs or server/scripts/ProjectileTrigger.cs - only the host does?????
server/scripts/ProjectileTrigger.cs
server/scripts/game.cs
08/11/2008 (4:03 pm)
I did these code changes and it seems to work - but I'm not sure if I obeyed the gameConnection paradigm you have described - I think I need to buy that new Torque Multiplayer book.Looking at this code still confuses me. Is it only the host who is dolling out the points? I assume so. I'm assuming if you are a client you never run server/scripts/games.cs or server/scripts/ProjectileTrigger.cs - only the host does?????
server/scripts/ProjectileTrigger.cs
function ProjectileTrigger::onEnterTrigger(%this,%trigger,%obj)
{
Parent::onEnterTrigger(%this,%trigger,%obj);
if(%obj.getClassName() $= "Projectile" )
{
error("ProjectileTrigger::onEnterTrigger");
messageClient(%obj.client, 'MsgClient', 'Trigger Hit With Projectile');
%obj.client.incTargetScore(1);
commandToClient( %obj.client, 'checkMission', %obj.client.targetScore, %obj.client.score); // <---------
}
}server/scripts/game.cs
// Doll out points and display an appropriate message
if (%damageType $= "Suicide" || %sourceClient == %this) {
%this.incScore(-1);
messageAll('MsgClientKilled','%1 takes his own life!',%this.name);
commandToClient( %this, 'checkMission', %this.targetScore, %this.score);
}
else {
%sourceClient.incScore(1);
messageAll('MsgClientKilled','%1 gets nailed by %2!',%this.name,%sourceClient.name);
commandToClient( %sourceClient, 'checkMission', %sourceClient.targetScore, %sourceClient.score);
if (%sourceClient.score >= $Game::EndGameScore)
{
endGame();
}
}
#10
It would make sense for the server to be the arbitrator of points, and that is what you are doing since the trigger onEnter/Leave callbacks occur on the server side (thats just the way they work). There are some functions like initClient and initServer or initBaseServer which exec all the scripts of the respective "sides". That is how scripts in the server folders get executed on the server and those in the client folder get executed for the client. A localhost (singleplayer) will get both executed but when the server callbacks are hit the object ids passed in will be the server-side object and the opposite for the client callbacks. Even though the client IS the server object creation / ghosting to the client / etc still works the same way as in a multiplayer situation.
Glad you got it working.
08/11/2008 (4:12 pm)
Quote:
Looking at this code still confuses me. Is it only the host who is dolling out the points? I assume so. I'm assuming if you are a client you never run server/scripts/games.cs or server/scripts/ProjectileTrigger.cs - only the host does?????
It would make sense for the server to be the arbitrator of points, and that is what you are doing since the trigger onEnter/Leave callbacks occur on the server side (thats just the way they work). There are some functions like initClient and initServer or initBaseServer which exec all the scripts of the respective "sides". That is how scripts in the server folders get executed on the server and those in the client folder get executed for the client. A localhost (singleplayer) will get both executed but when the server callbacks are hit the object ids passed in will be the server-side object and the opposite for the client callbacks. Even though the client IS the server object creation / ghosting to the client / etc still works the same way as in a multiplayer situation.
Glad you got it working.
#11
08/11/2008 (4:29 pm)
Thanks for your help amd yes that does explain things a lot better for me. I'm just glad I started small on multiplayer logic and got stuck early, rather that doing a full game and having to retro fit all the code later.
Associate James Ford
Sickhead Games