Game Development Community

Client/Server Variable issues, please help

by Michael Perry · in Torque Game Engine · 03/01/2007 (1:02 pm) · 18 replies

Hey all. I know discovering the answer to this is gonna make me feel like a complete n00b, but I'm stuck on a problem.

Goal:
When the player gets in range or collides with a usable object (vehicle or door), I want to display a GUI with a list of commands the player can perform on the object. The mouse wheel will scroll the list, highlighting the selected command. The middle mouse button will execute the command.

Problem:
The host (server)'s action menu works correctly, but joining players (clients) are not getting an updated list.

Script Code:
[b]// server\scripts\Player.cs[/b]

function Armor::onCollision(%this,%obj,%col)
{
   if (%obj.getState() $= "Dead")
      return;

   // Try and pickup all items
   if (%col.getClassName() $= "Item")
      %obj.pickup(%col);

   %this = %col.getDataBlock();
   if ((%this.className $= WheeledVehicleData) && %obj.mountVehicle &&
         %obj.getState() $= "Move" && %col.mountable) 
   {
      
      [b]%client = %obj.getControllingClient();
      echo("Armor::onCollision: " @ %client);
      commandToClient(%client, 'VehicleActionMenuCheck', %client);      [/b]
      
      
   }
}


client\scripts\ActionMenu.cs
function clientCmdVehicleActionMenuCheck(%client)
{
    //determine how far should the picking ray extend into the world
   %selectRange = 3;
   
   //only search for vehicles
   %searchMasks = $TypeMasks::VehicleObjectType;
   
   echo("VehicleActionMenuCheck: " @ %client);
   
   %pos = %client.player.getEyePoint();    [b]//<===== Error #1 produced here[/b]
   
   //start with the shape's eye vector...
   %eye = %client.player.getEyeVector();     [b]//<====Error #2 produced here[/b]
   %eye = VectorNormalize(%eye);
   %vec = VectorScale(%eye, %selectRange);
   
   %end = VectorAdd(%vec, %pos);
   
   %scanTarg = containerRayCast(%pos, %end, %searchMasks);
   
   //a target in range was found so select it
   if(%scanTarg)
   {
      /*
      %client.currentActionIndex = 0;
   %client.actionMenuActive = 0;
   %client.actionTimerThread
   */
     %client.actionMenuActive = 1;
     if(!ActionMenu.isVisible())
         ActionMenu.setVisible(true);
           
      if(isEventPending(%client.actionTimerThread))
      {
         cancel(%client.actionTimerThread);
         
         %client.actionTimerThread = schedule(1000, 0, hideActionMenu, %scanTarg, %client);  
      }
      else
         %client.actionTimerThread = schedule(1000, 0, hideActionMenu, %scanTarg, %client);
      clientCmdSetAction(%client, 0, "Enter Vehicle as Driver", "enterVehicle", 0, "", "");
      clientCmdSetAction(%client, 1, "Enter Vehicle as Gunner", "enterVehicle", 1, "", "");
      clientCmdSetAction(%client, 2, "Enter Vehicle as Passenger", "enterVehicle", 2, "", "");     
   }
}

Error Print Outs
(59): Unable to find object: '' attempting to call function 'getEyePoint'
(62): Unable to find object: '' attempting to call function 'getEyeVector'

So, apparently, the playerID or clientID are messed up by the time we jump into clientCmdVehicleActionMenuCheck().

I can post more script code if needed, but I believe this is the best place to look to find the problems.

Any help is appreciated, and as always, thanks in advance...

#1
03/01/2007 (2:00 pm)
You are trying to access dynamic fields that are created on the server, but not created on the client. you will need another method of getting a pointer to the client.
#2
03/01/2007 (2:06 pm)
Off the top of my head I *believe* you could replace %client.player with $ServerConnection.getControlObject().
#3
03/01/2007 (3:38 pm)
Matt is correct, except it's just ServerConnection, not $ServerConnection.

you shouldn't be passing %client to the clientCmd. the value you get will be meaningless.

your later uses of %client should be rewritten.
just use global variables. like $actionTimerThread instead %client.actionTimerThread.

also you'll need to refactor your calls to clientCmdSetAction() so that they don't take a %client param.
- i think just not including %client may work.
#4
03/01/2007 (3:48 pm)
Whoops! Been a while since I've coded in that particular section of the script (not much call for it in Constructor =P).
#5
03/01/2007 (4:06 pm)
You need to move most of that code to a serverside function. Having the client id in this case won't help you in any way. Also you are overriding the Armor datablock in that collision code. This is very bad practice. Basically what you would want to do in ::onCollision is send the client the vehicles datablock name or some parameter in the datablock and let the client push a gui based on that information. Then have the gui send a commandToServer telling the server the selected action to take.
#6
03/01/2007 (4:07 pm)
One more thing I noted: you should not call ClientCmd's from the client itself...they should be reserved for receiving server netEvents (via commandToClient).
#7
03/01/2007 (4:23 pm)
Michael-

I think the reason it doesnt work the way you have it is because youre passing in a variable which represents an ID, rather than just a string or number.

on the client side, use either "ServerConnection" or "localclientconnection" to obtain a handle to the local client object.
#8
03/01/2007 (4:32 pm)
And just to pile on the criticism a bit more =)

you'll probably want to add another condition to make sure the vehicles not moving as well.
#9
03/01/2007 (4:40 pm)
It's also possible that your mama dresses you funny. ;D

don't get discouraged Michael - the learning curve for getting a handle on Torque client/server stuff is famous and steep. i know i certainly flailed around these sorts of issues for a long time.
#10
03/01/2007 (5:32 pm)
Wow, I didn't expect such an overwhelming response. I don't take any of this as criticism, but as helpful information that I desperately needed. After over 3 years of using Torque, I still have my weak points, and anything remotely related to networking kicks my ass.

Thanks everyone, I'll give these all a shot tomorrow. =)
#11
03/05/2007 (9:55 am)
One step forward, two steps backwards =).

I'm getting closer to proper client/server variable handling, but I'm still not getting my desired results. The client actionMenu is blank, and I'm still not getting the client ID or setting up properly

INIT CODE

common\server\clientConnection.cs
function GameConnection::startMission(%this)
{
   // Inform the client the mission starting
   commandToClient(%this, 'MissionStart', $missionSequence);
   
   commandToClient(%this, 'InitActionMenu');
}

client\scripts\ActionMenu.cs
function clientCmdInitActionMenu()
{
   %myClientPlayer = LocalClientConnection.player;
   %myClientPlayer.currentActionIndex = 0;
   %myClientPlayer.actionMenuActive = 0;
   %myClientPlayer.actionTimerThread = -1;
   
   %AMText[0] = ActionChoiceDescripOne;
   %AMText[1] = ActionChoiceDescripTwo;
   %AMText[2] = ActionChoiceDescripThree;
   %AMText[3] = ActionChoiceDescripFour;
   %AMText[4] = ActionChoiceDescripFive;
   
   // CLIENT BASED ACTION MENU SETUP
   for(%i = 0; %i < 5; %i++)
   {
         %myClientPlayer.actionMenu[%i] = new CustomAction("") {
                                          Description = "No action Available";
                                          Command = "choseActionOne();";
					  datablock = StandardAction;
                                          };
         %text = %myClientPlayer.actionMenu[%i].Description;
      
         %AMText[%i].settext(%myClientPlayer.actionMenu[%i].Description);
         %AMText[%i].setProfile("ArmaliteBlack16");
   }
   
   %AMText[%myClientPlayer.currentActionIndex].setProfile("ArmaliteYellow18");
}

Console Errors for Init
Mapping string: InitActionMenu to index: 13
Object 'StandardAction' is not a member of the 'GameBaseData' data block class
game/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.
Object 'StandardAction' is not a member of the 'GameBaseData' data block class
game/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.
Object 'StandardAction' is not a member of the 'GameBaseData' data block class
game/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.
Object 'StandardAction' is not a member of the 'GameBaseData' data block class
game/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.
Object 'StandardAction' is not a member of the 'GameBaseData' data block class
game/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.
game/client/scripts/ActionMenu.cs (28): Unable to find object: '' attempting to call function 'setProfile'



SCROLLING and SELECTION
client\scripts\default.bind.cs
function PickAction(%val)
{
   %zaxis = getMouseAdjustAmount(%val);
   
   if(%zaxis > 0)
      commandToServer('ActionMenuUp');
   else
      commandToServer('ActionMenuDown');
}
moveMap.bind(mouse, zaxis, PickAction);

server\scripts\commands.cs
function serverCmdActionMenuUp(%client)
{
   %client.player.currentActionIndex--;
   
   if(%client.player.currentActionIndex < 0)
      %client.player.currentActionIndex = 0;
   
   commandToClient(%client, 'UpdateActionMenu');
}


client\scripts\ActionMenu.cs
function clientCmdUpdateActionMenu()
{
   
   %controlObject = LocalClientConnection.getControlObject();
   
   %AMText[0] = ActionChoiceDescripOne;
   %AMText[1] = ActionChoiceDescripTwo;
   %AMText[2] = ActionChoiceDescripThree;
   %AMText[3] = ActionChoiceDescripFour;
   %AMText[4] = ActionChoiceDescripFive;
   
   for(%i = 0; %i < 5; %i++)
   {
      %text = %controlObject.actionMenu[%i].Description;
      
      %AMText[%i].settext(%controlObject.actionMenu[%i].Description);
      %AMText[%i].setProfile("ArmaliteBlack16");
   }
   
   %AMText[%controlObject.currentActionIndex].setProfile("ArmaliteYellow18");
}

Console errors for actionMenu scrolling
game/client/scripts/ActionMenu.cs (33): Unable to find object: 'LocalClientConnection' attempting to call function 'getControlObject'
game/client/scripts/ActionMenu.cs (49): Unable to find object: '' attempting to call function 'setProfile'


I might have missed the point on how to use LocalClientConnection, as well as global variable usage. If $ClientID is declared in client\scripts\ActionMenu.cs, does it exist on the server side? Should I use that to store each clients ID?

And yes, my mama did dress me funny...luckily those days are over and my girlfriend now dresses me funny... =)

Thanks again for the help. . .
#12
03/05/2007 (11:15 am)
Never heard of a customaction object. is that new or something you've added yourself?

I think you should be using "Serverconnection" rather than localclientconnection. I guess localclientconnection will only work if that client is serving the game. I never tried using that label in a networked game. just like all other objects in torque, game connection objects can have an ID and a name. "Serverconnection" is just the default name given to the connection when it's created. you could also just use a global to store the ID of the connection object as well. just look in the startmissiongui file to find where it's created.
#13
03/05/2007 (11:28 am)
CustomAction is my own class. . . I'll give ServerConnection a try and see what happens...

*EDIT* - Calling ServerConnection.getControlObject() returns '0' in ActionMenu.cs, so that's not gonna work for me.

Plus there is still the matter of the registered object failure problem.
#14
03/05/2007 (11:52 am)
Maybe a little re-organization is needed. The original problem I was having is that I was handling the GUIs in the wrong location, causing only the servers GUI to update and display (it was even being triggered by the client).

Honestly, the only problem I have is setting each GUI to the descriptions held in the array inside the player:

%AMText[0] = ActionChoiceDescripOne;
   %AMText[1] = ActionChoiceDescripTwo;
   %AMText[2] = ActionChoiceDescripThree;
   %AMText[3] = ActionChoiceDescripFour;
   %AMText[4] = ActionChoiceDescripFive;
   
   for(%i = 0; %i < 5; %i++)
   {
      %text = %controlObject.actionMenu[%i].Description;
      
      %AMText[%i].settext(%controlObject.actionMenu[%i].Description);
      %AMText[%i].setProfile("ArmaliteBlack16");
   }
   
   %AMText[%controlObject.currentActionIndex].setProfile("ArmaliteYellow18");

It would be messy to send 5 strings and a number each time I have to update, but I can move everything back to server commands if this problem can be resolved. UpdateActionMenu is the only thing I really need to handle in the client scripts, so that the correct connection's GUIs are updated.
#15
03/05/2007 (11:53 am)
Quote:
Object 'StandardAction' is not a member of the 'GameBaseData' data block classgame/client/scripts/ActionMenu.cs (21): Register object failed for object (null) of class CustomAction.

%myClientPlayer.actionMenu[%i] = new CustomAction("") {
                                          Description = "No action Available"; 
                                         Command = "choseActionOne();";
     		         datablock = StandardAction; 
                                         };

--Do you actually have a "StandardAction" datablock declared in script?
----is it a derivative class of GameBaseData (it needs to be)
#16
03/05/2007 (12:30 pm)
It is derived from GameBaseData, and it is declared in server\scripts\Player.cs:

datablock ActionData(StandardAction)
{
   type = 0;
};
#17
03/05/2007 (12:35 pm)
Make sure that script file is loaded before you wish to use it.
#18
03/05/2007 (12:44 pm)
ActionMenu.cs is executed before Player.cs, but the function clientCmdInitActionMenu is called after the datablock is loaded, shouldn't be a problem, right?

*EDIT* Yet somehow I think that is the problem. If I move the datablock into ActionMenu.cs, neither the client nor the server are initialized correctly. If I move the datablock into Player.cs, the server actionMenu works fine, but the client still isn't initialized properly. Looking into this further. . .