Game Development Community

Ideas on ranged chat

by Wolfgang Kurz · in Torque Game Engine · 12/06/2006 (10:12 am) · 17 replies

Hello everyone,

i am about to implement a chatconsole into my game.

the plan is to have the following chat options:

-say
-whisper
-shout
-static chat channels
-usercreated chat channels

my main question is about the range based channels like say and shout

how would you guys go about to implement this

of course the easiest way would be to check all players if they are in range and then send the message to them

is it worth to check in a kind of binary tree for who is in range?

does torque have anything already implemented about the range based chats?

well just looking for ideas and hints if you have any:-)

thx a lot in advance

#1
12/06/2006 (6:07 pm)
Most of the AI resources have a 'get closest player' function. Modifying this to return all players in a given radius rather than just the closest is I think where I would start. It's about the only method of finding the closest player I've come across.

That said I this function has always seemed very inefficient to me. Trawling through the entire list of connected players just to see which is closest just doesn't seem the best way to do it, especially if you've got a fair few players connected and a fair few bots/chat messages calling the function all the time. If anyone has a better method that they know of I think that both Wolfgang and myself would love to hear it.

Also, Wolfgang, when you do implement this chat system I'd love to hear how you did it so keep me posted. I'm thinking of looking into a similar style of system for my project.

Cheers,
Davidovich
#2
12/06/2006 (6:22 pm)
I'm about to implement server-side ranged chat as well,
and i'm planning on just doing a radial select or whatever it is.

- this may prove inefficient for us, as we regularly have 70+ users online at once,
and plan on quite a few more than that, and the select approaches O(n^2) when lots of people are talking at once.

for relatively fewer number of players, i would just go ahead and implement it w/ radial select and worry about performance only if it turns out to be required.

if performance *does* turn out to be a problem,
you can trade-off in bandwidth and simply broadcast the messages to everyone and do client-side filtering for range. - of course, that's subject to client-side cheats, but again, maybe you don't care.
#3
12/07/2006 (1:36 pm)
Quote:You can trade-off in bandwidth and simply broadcast the messages to everyone and do client-side filtering for range

Is this possible and if so can you point us to how would it be done? I thought all the raycasting functions (which you'd need for checking the distances) were server side. This would be useful for a bunch of things I can think of if you can check distances/do raycasts clients side.
#4
12/07/2006 (1:57 pm)
First off, a raycast is total overkill for determining distance.
raycasts are used for determining if there's an object in between two points,
not measuring the distance between two points.

that said,
there's a few ways you could implement the client-side range tests.

how are you transmitting the messages to the clients ?
via ghosting or commandToClient ?

the easiest thing might be to just include the position of the speaker along with the message,
and test for range against that.
#5
12/07/2006 (2:31 pm)
OK, firstly:
Quote:a raycast is total overkill for determining distance
True, my stupid error. What you want is the vector distance, however are the vector functions (like VectorLen)available client-side? And for that matter what about the client possition? Does the client side have access to that info through the getTransform function or not?

Sorry if this is a retarded thing to ask but I'm really lost when it comes to what info/functions the client side has access to.

Secondly:
Quote:the easiest thing might be to just include the position of the speaker along with the message
I think this is a good way to do it. It might bump up the message size slightly, but it cuts down on the work the server has to do.
#6
12/07/2006 (2:46 pm)
> are the vector functions (like VectorLen)available client-side?
yes.

> And for that matter what about the client possition?
well, the client has to render all the players, so it certainly has some notion of their position! ;)
so, yes, the client can definitely call getTransform() on any object it's rendering.
getting the "my player" object can be a bit tricky, my codebase has drifted pretty far from stock so i'm not sure exactly how to do it in stock TGE. in most cases it's
serverconnection.getControlObject()

.. actually, it's probably safe to use that always, since if the control object isn't the player, it's probably pretty close to the player.

so: serverconnection.getControlObject().getTransform().
#7
12/07/2006 (3:47 pm)
By default, a clients connection to a remote server will not return key values like position and tranform. You would have to open them up to the client, but that will open a whole new can of worms.

Server-side raycasting in this instance would not be such a bad idea, as it will search for and find every player object in the radius. Otherwise you would have to check every player on the server to get their positions relative to yours, and that would be more costly than a simple raycast.
#8
12/07/2006 (5:57 pm)
Paul - i'm not sure if you're referring to "serverconnection" there,
but in case you are, i can maybe clarify:

serverConnection is a global object on each client representing the client's connection to the server.

getControlObject() returns the client-side instance of the object which has "control" of the connection for things like player movement. this is typically the player object. getControlObject() is an entirely client-side function. it doesn't contact the server.

getTransform() i think we know.

and once again, if you just want to find everyone in a given range, a raycast is not the right tool to use. raycast is a comparatively expensive operation to determine if there are any objects in the line-of-sight between two points, so it's good to avoid it unless it's really needed. You're probably thinking of InitContainerRadiusSearch().
#9
12/07/2006 (11:44 pm)
Aha, just tried it on the TGE demo and it does work. Guess removing that was something I did myself, along with removing the ability to read anything from the clients gameconnection.
You're also correct about InitContainerRadiusSearch and containerRayCast, I was confusing the two.
#10
12/08/2006 (12:04 am)
Thank you all for posting your ideas!

I decided to use the InitContainerRadiusSearch from rangeDamage.cs to do the ranged chat.

@Davidovich

It will be a bit before i really get to implement the whole chat i still have some issues with persitence on the serverside. But once i solved that i will get to the chat module and if you want i can keep you updated on my progress.

My icq: 10068941
msn: zzz_kurz_zzz@hotmail.com


thanks to everyone again
#11
12/08/2006 (12:38 am)
I just wanted to say I have found this an interesting topic and something I also planned on attempting at somepoint down the line.
I'm no expert, but doing this client side would seem the best way to go, there is no point clogging up the server every time someone sends a message and on the client side its only going to run the code when sending your own message. The calcs will be done in C++ so there isn't going to be much loss there. Much better to add 10 client object id's to a message sent to the server than have the server check 100 players to see if there within range each time anyone sends a message.
I would assume messages are sent as a low priority anyway arn't they?, or at least they should be. Would be interesting to get that clarified by someone.
#12
12/08/2006 (1:18 am)
Here's how it's done in the MMOKit guys, I was going to submit this as a resource anyways but this is the gist of it.
function chatMessageRadius( %sender, %radius, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ){
//This function sends chats out for a radius of %radius
        if ( ( %msgString $= "" ) || spamAlert( %sender ) )
           return;
        
        %player = %sender.actor;
        %msgString = %player.getShapeName()@":" SPC %msgString;
        %searchMasks = $TypeMasks::PlayerObjectType;
            InitContainerRadiusSearch(%player.getTransform(), %radius, %searchMasks);
            while ((%NewObject = containerSearchNext()) != 0){
                %obj = %NewObject.getId();
                if(!%obj.isAi == 1){
                        chatMessageClient( %obj.client, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
                }
        }
}

function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
        if ( ( %msgString $= "" ) || spamAlert( %sender ) )
           return;
           
        %count = ClientGroup.getCount();
        
        for ( %i = 0; %i < %count; %i++ )
        {
           %obj = ClientGroup.getObject( %i );
           
           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 );
           }
        }
}

function Whisper(%client,%text){
//Used to handle PM or whispers
        %playername = getWord(%text,0);
        %msg = %client.name@" whispers to you: "@getRestWords(%text);
        %player = FindPlayerByName(%playerName);
        if(%player !=-1){
                %player.sendMessage(%msg);
                %player.lastTell = %client;
                %client.lastTell = %player.client;
        }else{
                %client.actor.sendMessage(%playerName SPC "does not appear to be here");
        }
}

function serverCmdMessageSent(%client, %text)
{
trace(1);
//This will be where we start looking for /commands
//TODO add in slash commands for the Guild Leader to use

        if(strlen(%text) >= $Pref::Server::MaxChatLen){
                %text = getSubStr(%text, 0, $Pref::Server::MaxChatLen);
        }        

        if(getWordCount(%text) > 1){
                %command = trim(firstword(%text));
                %text = trim(restWords(%text));
        }else{
                %command = trim(%text);
        }
        //echo("Command is :"@%command);
        switch$(%command){

                case "/shout":
                        chatMessageAll(%client, '\c4%1: %2', %client.name, %text);
                case "/tell":
                        //{player} {message} - (alias '/t') Send a player a private message. You can scroll through all the people that whispered you recently by typing on the tab key.
                        Whisper(%client,%text);
                default:
                        //Default is to send message to those very nearby
                         if(%command !$= %text){
                                %text = %command SPC %text;
                        }
                        chatMessageRadius( %client, 100, %text, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
                
        }
trace(0);
}

There's a little more to it than just that, especially the Whisper function and the %actor.sendmessage command, stuff but this should get you on the right track.

Enjoy!
Regards,
Dreamer

p.s. The MMOKit has about 50 different slash commands I trimmed this stuff for berevity.
#13
12/08/2006 (1:37 am)
Awsome Dreamer:-)

thx a lot
#14
12/08/2006 (1:43 am)
You're welcome.
#15
12/08/2006 (3:33 am)
This has been a very informative post. I have a question and I hope you'll excuse me if I come across totally ignorant but:

Could you use this InitContainerRadiusSearch to check for AI proximity? Specifically, to simulate how an animal might "smell" or "see" a player or player unit within a specific range? Or is there a better method for this?

Just curious as this seems a very useful function.
#16
12/08/2006 (6:09 am)
Yes James thats what you would use it for too:-)


Edit: Well for some people you wouldnt need this cause you can smell them from anywhere:-)
#17
12/08/2006 (8:53 am)
Are you saying I smell?

I'll get you for that :)

...bastard... ;)