Game Development Community

dev|Pro Game Development Curriculum

send command to visible players

by Fyodor -bank- Osokin · 04/03/2007 (11:24 am) · 2 comments

-------------------- Intro --------------------

If you want to modify the chat, so only players who in visible range will get the messages what do you do?
Of course you can go similar to this (starter.fps/common/server/commands.cs):
function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
   if ( ( %msgString $= "" ) || spamAlert( %sender ) || !isObject( %sender.getControlObject() ) )
      return;

   [b]%senderPosition = %sender.getControlObject().getPosition();[/b]

   %count = ClientGroup.getCount();

   for ( %i = 0; %i < %count; %i++ )
   {
      %obj = ClientGroup.getObject( %i );

      [b]if ( !isObject(%obj.getControlObjecT()) ) continue;
      %targetPosition = %obj.getControlObjecT().getPosition();

      %distance = VectorDist(%senderPosition, %targetPosition);
      if (%distance > $chatDistance) continue;[/b]

      if(%sender.team != 0)
         chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
      else
      {
         // message sender is an observer -- only send message to other observers
         if(%obj.team == %sender.team)
            chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
      }
   }
}
But code lacks on:
1. it's scripted, and if you have lots of clients it can hit on performance.
2. if the target player is in interior and not visible but is in range - he will get the message

-------------------- The "code" --------------------

My proposal is to use benefits of C++ (speed). So, here what I have done:

in file engine/game/gameConnection.h, and after
U32  getMissionCRC()           { return(mMissionCRC); }
add:
// Post RemoteCommandEvent only to "visible" clients
   bool commandToVisibleClients(S32 argc, const char **argv, bool selfExcept = false);

in file engine/game/net/net.cc, BEFORE
ConsoleFunctionGroupEnd( Net );
add:
// Script commandToVisibleClients implementation
ConsoleFunction( commandToVisibleClients, bool, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(GameConnection sourceClient, tagstring func, ...)")
{
   GameConnection *conn;
   if(!Sim::findObject(argv[1], conn)) {
      Con::errorf(ConsoleLogEntry::Script, "Remote Client Command Error - Can't find client connection. No command were issued.");
      return false;
   }
   return conn->commandToVisibleClients(argc - 2, argv + 2);
}

ConsoleFunction( commandToVisibleClientsSE, bool, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(GameConnection sourceClient, tagstring func, ...) Self-except")
{
   GameConnection *conn;
   if(!Sim::findObject(argv[1], conn)) {
      Con::errorf(ConsoleLogEntry::Script, "Remote CClient Command Error - Can't find client connection. No command were issued.");
      return false;
   }
   return conn->commandToVisibleClients(argc - 2, argv + 2, true);
}

at the end of the same file paste the following:
//Engine commandToVisibleClients implementation
bool GameConnection::commandToVisibleClients(S32 argc, const char **argv, bool selfExcept)
{
   if (!this->getControlObject())
   {
      Con::errorf(ConsoleLogEntry::Script, "Remote Client Command Error - Can't find controlObject of client. No command were issued.");
      return false;
   }
   //re-implementation of sendRemoteCommand
   if(U8(argv[0][0]) != StringTagPrefixByte)
   {
      Con::errorf(ConsoleLogEntry::Script, "Remote Client Command Error - command must be a tag.");
      return false;
   }
   S32 i;
   for(i = argc - 1; i >= 0; i--)
   {
      if(argv[i][0] != 0)
         break;
      argc = i;
   }

   SimGroup *g = Sim::getClientGroup();
   SimGroup::iterator t;
   for (t = g->begin(); t != g->end(); t++)
   {
      GameConnection *pClient = static_cast<GameConnection*>(*t);

      // if we need to except ourselfs - next one please!
      if (selfExcept && pClient == this)
         continue;

      // check if the source client have control object and get the position
      ShapeBase *pShape = NULL;
      if (! pClient->getControlObject())
         continue;
      VectorF v1(0,0,0), v2(0,0,0);
      v1 = this->getControlObject()->getPosition();
      pShape = dynamic_cast<ShapeBase*>(pClient->getControlObject());   
      if (!pShape)
         continue;

      // checking if we scoped found object
      if (this->getGhostIndex(pShape) == -1)
         continue;

      // Checking the distance
      v2 = pShape->getTransform().getPosition();
      VectorF v = v2 - v1;
      F32 dist = mSqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));

      // using custom variable setting
      F32 visDist = Con::getFloatVariable("$Game::visibleDistance",150.0);
      /*
      // Using sky
      F32 visDist;
      Sky * sky = gServerSceneGraph->getCurrentSky();
      if(sky)
         visDist = sky->getVisibleDistance();
      else
         visDist = 150.0f;
      */

      if ( dist < visDist ) 
      {
         for(i = 0; i < argc; i++)
            pClient->validateSendString(argv[i]);
         RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, pClient);
         pClient->postNetEvent(cevt);
      }
   }
   return true;
}
Using this technique the command (including chat messages if you do needed changes in script) will be issued only on currently scoped client AND in needed range. You can play with - remove check for scoped existence or the range check - to suit your needs.
I've also put there how you can get the distance from the current SKY object if needed.

The example usage is simple:
function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
   if ( ( %msgString $= "" ) || spamAlert( %sender ) )
      return;
      
   if (%sender.player) {
      commandToVisibleClients(%sender, 'ChatMessage', %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
      return;
   }
Of course you can play with it how you want.
You can perform any commands like this.

For example if you do something that only players in range should see/hear/whatever, just execute:
commandToVisibleClients( %sourceClient, 'commandTag', %argument1[, %argumentN ....]);
If you need to issue commands without checking the source object (from where the location is taken), you can change/add the function so it will take position as a first parameter, e.g.:
commandToVisibleClients( "x y z", 'tag', %args[, ...] );
You have sources and have this example - what you will do - is up to you!

Have fun and good luck with Torquing!

#1
04/03/2007 (7:41 pm)
i usually use the first one, and i send message to client that's on range of the player's eye.

wew, cool i'll try this tonight...

thx for the resource.
#2
01/12/2010 (8:58 pm)
Will this work in T3D?