Game Development Community

Monitoring Client Position Changes on Server

by Welias D. Willie II · in Torque Game Engine · 08/30/2005 (6:57 pm) · 34 replies

Okay I posted this in the public forum because I thought I could do it all in script...

http://www.garagegames.com/mg/forums/result.thread.php?qt=34003

Stephen Zepp suggested I do this in source code, I am best at scripting but have made many small changes to the source.

I will re-post here to see what source suggestions could help with my problem.
Page «Previous 1 2
#1
08/30/2005 (7:50 pm)
What is the best way to trigger an event on the server when a clients position changes? (Not just when a client moves)

currently I am trying something like this:

function ClientPositionMonitor()
{

    %count = ClientGroup.getCount();
    if (%count > 0)
    {
	for ( %ci = 0; %ci < %count; %ci++ )
	{
		%client = ClientGroup.getObject( %ci );
		if (%client > 0)
		{	
		    if(isObject(%client.player.getTransform()))  // returning error
		    {
			%pos = %client.player.getTransform();
			%newx = mFloor((getWord(%pos,0)/100));
			%newy = mFloor((getWord(%pos,1)/100));

			%currx = mFloor(getWord(%client.player.location,0));
			%curry = mFloor(getWord(%client.player.location,1));
		
			if (%newx != %currx || %newy != %curry)
			{
				%currx = %newx;
				%curry = %newy;
				
				%client.player.location = %currx SPC %curry;
				
				echo("*** POSITION MONITOR: CLIENT:" SPC %client SPC "MOVED TO: X" SPC %currx SPC "Y" SPC %curry);
                                // execute some function based on client movements
			}
		   }
		}
	}
    }
schedule(1000,0,"ClientPositionMonitor");
}

I execute this code as soon as the server launchs (in my common/server/game.cs). It seems like there would be a more optimized solution where events are kicked off based on movement or maybe something I haven't thought of, rather than executing a function every second to check client's positions on the server.

Also, if the code snippet above is acceptable, I am getting an error I would like to trap (Which is why I am checking every variable in the function above). The error happens when a client is issued a client id on the server side but has not fully entered the game therefore does not have a position for getTransform to report on. So in between "Mapping string: MissionStartPhase1Ack to index : 0" and "Mapping string: MissionStartPhase2Ack to index : 1" I get this error:

Unable to find object: ' ' attempting to call function 'getTransform'

Like I mentioned if the code I am using above is worth keeping, then I simply want some way to trap the error I am getting above, however, I feel there is probably a more efficient way to monitor and react to client position changes.

Suggestions?
#2
08/30/2005 (7:56 pm)
As I mentioned in the other thread, the exact purpose of processTick()/updateMove() in player.cc is to catch moves generated by the client for that object, and, well, process them. I would suggest looking at the two methods and see what they do regarding the player's position, and come up with a way to hook into that.

If it is a script function/method that you want to execute based on a move happening, then you'll probably want to use the Con::executef(...) method to perform a callback to the script. Otherwise, you can simply put your code into the appropriate area in the method you select, and then recompile and launch the new executable.
#3
08/30/2005 (7:57 pm)
I looked at player.cc and I like the updateMove() function, it seem to make sense to place my functionality in there. I am not the best at C++ so I hope I can get a helping hand here.

Would it be more efficient to move almost all the functionality above into the updateMove() function? Say, after a client has moved +/-100 in either x or y it would trigger an event in script.

okay... looking at Con::executef(...)
#4
08/30/2005 (7:59 pm)
If you can define your problem statement a bit more, it would help to give you guidance...tell us exactly what type of functionality you want to have, etc., and we can give better advice!
#5
08/30/2005 (8:09 pm)
I wish to monitor the position of each client connected to the game and when a player has moved more than 100 coordinates x or y (not concerned with z at the moment) I wish to execute a script function that can be used to execute various effects in the game.

In script I was doing this with the script above by storing the last registered cell (a cell is defined as x position/100 & y position/100) in %client.player.location, then once a second I would cycle through all connected clients, compare the current x & y location of each client (divided by 100) to the stored cell definitions in %client.player.location.

If this is done in the engine, how would I track or reference the stored location and compare it to the current location?
#6
08/30/2005 (8:12 pm)
Give me a bit and I'll give you a pretty finished implementation, but here's the guts:

Add two fields to the player class:, call them lastX and lastY if you want.

--Every tick, after movement is finished for that tick, compare the current x and y positions to these values. Could use the standard distance check algorithm (square each, subtract, see if difference is greater than 100 squared).
If the check succeeds they have moved your required distance. set the lastX and lastY to current X/Y, and do a Con::executef(...) on your script method.
#7
08/30/2005 (8:19 pm)
Great! I would need the Con::executef(...) to pass the new x/y as well.
#8
08/30/2005 (8:26 pm)
You could do that, or you could simply get the transform of the player in script using getPosition() (script version). That would probably be "cleaner", but just a very tiny bit less optimized.

Tell you what, this actually is a great place for you to start getting used to coding. Why don't you give it a shot, see what you can come up with and then if you run into trouble, you can post what you have here and we'll give help!

It's always easier to learn by doing (anf failing once or twice) than cut/paste :)
#9
08/30/2005 (8:34 pm)
I agree, it is time I get my hands dirty.

What you mention with using getPosition() with scripts is basically what I was doing above and it works but I am not sure I am satisfied with all that it will need to accomplish being completely written in script.

The more I look at player.cc, it doesn't seem to be that intimidating. It may actually be best if I implement not only the position tracking but the resulting actions of each position change. I ultimately want to load dynamic objects into the game based on a 9 cell grid around each player from a database.

Anyway, is player.cc the proper location for server side functions of each client? I am thinking that checkMissionArea(); may be my best bet at a function to somewhat clone in hopes of creating what I need.
#10
08/30/2005 (9:13 pm)
In player.cc I added:

PlayerData::PlayerData()
{
.....
   lastX = 0;
   lastY = 0;
.....
}

also added in player.cc:

void PlayerData::initPersistFields()
{
   Parent::initPersistFields();

   addField("lastX", TypeF32, Offset(lastX, PlayerData));
   addField("lastY", TypeF32, Offset(lastY, PlayerData));

.....
}

and

void PlayerData::packData(BitStream* stream)
{
   Parent::packData(stream);

   stream->write(lastX);
   stream->write(lastY);

.....
}

and

bool Player::updatePos(const F32 travelTime)
{
....// way down below checkMissionArea(); I added:

     checkCellArea();
...
}

and finally I added this function:

void Player::checkCellArea()
{
   // Checks to see if the player has moved to a new Cell Area...
   Point3F pos;
   getTransform().getColumn(3, &pos);

   if (int(pos.x/100) != mDataBlock->lastX || int(pos.y/100) != mDataBlock->lastY) 
   {
        // send the notification to the script
        Con::executef(mDataBlock,3,"onChangeCellArea",scriptThis(), mDataBlock->lastX, mDataBlock->lastY);

        // modify the datablock with the new X/Y position for future comparisons
	mDataBlock->lastX = int(pos.x/100);
	mDataBlock->lastY = int(pos.y/100);
   }    
   
}

Since this is my first attempt at real source modifications not to mention my first attempt at C++, please be gentle (but rag me out if it means teaching me).

So what did I do wrong?

You don't even want to see the compile errors. My guess is anyone with basic C++ can look at my code and find many things wrong before I start going over the compile errors. I also added:

F32 lastX;                      ///< LAST X
   F32 lastY;			           ///< LAST Y


just above F32 runforce in the player.h file, this reduced my compile errors from 17 to 13.

My guess is that I am doing way to much to accomplish what is probably a simple task.
#11
08/30/2005 (9:20 pm)
You don't want to be doing this in playerdata, but in player. You also need to add the fields in player.h in the appropriate areas. If you do it in playerdata, each and every player in your game will each be referencing the -same- lastX and lastY, which obviously isn't what you want.

Finally, since this will be server side only, you don't need to do anything in pack/unpack...that is used for sending the fields to the client(s), which isn't needed for what you want.

Other than those changes, I don't see anything major that's wrong...I myself probably wouldn't be using a cell system (although there isn't anything wrong with it per se), but if it accomplishes what you want then that part is fine.

A quick aside for you by the way, since you (once you put them in Player:: instead of PlayerData) are setting your lastX and lastY to persistent fields, you can actually reference them from script by simply doing a %client.player.lastX or %client.player.lastY.
#12
08/30/2005 (9:41 pm)
Ok...

removed the stream->write(lastX); and stream->write(lastY);
removed the stream->read(&lastX); and stream->read(&lastY);
removed lastx and lasty from PlayerData::PlayerData()

Added:

Player::Player()
{
....
   lastX = 0;
   lastY = 0;
....
}

I left the addField definitions in the PlayerData::PersistFields. (is this correct or is there a Player::PersistFields that I should be looking for?)

well I am still getting 15 or so compile errors and it is late, I will work on this one again tomorrow. Thanks Stephen.
#13
08/30/2005 (9:47 pm)
Hey, cool... I tweaked it and added a line here and there and this is all I have left:

--------------------Configuration: Torque Demo - Win32 Release--------------------
Compiling...
player.cc
C:\Torque\SDK - Phase 5 - AUG 2005\engine\game\player.cc(756) : error C2065: 'lastX' : undeclared identifier
C:\Torque\SDK - Phase 5 - AUG 2005\engine\game\player.cc(757) : error C2065: 'lastY' : undeclared identifier
Error executing cl.exe.

torqueDemo.exe - 2 error(s), 0 warning(s)

------

Okay I just declared them in player.h and all is compiled fine. Off to test.

Thanks for pushing me into this Stephen!


------

Back from testing... not so good. It seems that nothing is happening. I query %client.player.lastx from the server console and it returns empty, plus the checkCellArea() function in script is not being executed.

Oh well, back to the drawing board.
#14
08/30/2005 (9:52 pm)
If you want to load objects into the game based on a 9 cell grid, you may be able to do it useing 9 100*100 triggers. ? May not be what you need but it would make it nice'n ezy:)
#15
08/30/2005 (9:56 pm)
K. you need to move -everything- to the appropriate Player functions. You should not be dealing with playerdata at all.

You also still need to actually define the fields in player.h, in the player class definition.

player.h, roughly line 358, after the line Point3F mLastPos; add:
F32 mLastX;
F32 mLastY;

and below line 464 or so, Point3F getVelocity(), add
void checkCellArea()

In player.cc, you don't really need the persist fields for this, so let's leave that out.

Your call in updateMove() is fine.

Your checkCellArea() needs to replace all of the mDataBlock-> stuff with simply mLastX and mLastY (note: the m in front is simply a standardization that means this is a "member variable" of the class Player).

Finally, your Con::executef(...) call should look like:
Con::executef(this,3,"onChangeCellArea",scriptThis(), mlastX, mlastY);

That should get you real close :)
#16
08/30/2005 (9:59 pm)
Good to hear that you're getting close! you are going to find a bit of funkiness until you take a look at the changes I suggested (specifically, need to be getting away from the mDataBlock stuff for this particular functionality, including the con::executef call--keep in mind that the script function should be of the name Player::onChangeCellArea() ).
#17
08/30/2005 (10:04 pm)
Great! I had placed the definitions in player.h just about as you suggested above. I had also modified my checkCellArea() function to look like this:

void Player::checkCellArea()
{
   // Checks to see if the player has moved to a new Cell Area...
   Point3F pos;

   getTransform().getColumn(3, &pos);

   if (int(pos.x/100) != lastX || int(pos.y/100) != lastY) 
   {
        Con::executef(mDataBlock,3,"onChangeCellArea",scriptThis(), lastX, lastY);
	lastX = int(pos.x/100);
	lastY = int(pos.y/100);
   }    
   
}

notice I removed all the mDataBlock references. Going to check everything and compile again.

Brian, thanks for the suggestion but unfortunately triggers will not work as this must be an infinite solution.


oops just noticed I had another mDataBlock in the Con::executef(...)
#18
08/30/2005 (10:11 pm)
Do I still add this:

void PlayerData::initPersistFields()
{
   Parent::initPersistFields();

   addField("lastX", TypeF32, Offset(lastX, PlayerData));
   addField("lastY", TypeF32, Offset(lastY, PlayerData));
??

I could not find a "initPersistFields()" for Player::

Just noticed you said to leave the above out.
#19
08/30/2005 (10:27 pm)
Ok I give up for tonight. Compiles fine. Nothing in PlayerData, all lastx/lasty have been change to mlastX/mlastY for standardization, removed all the mDataBlock references, placed proper definitions in player.h, checked Con::executeF(), all appears to be correct but still nothing is being passed to my script function. I can call my script function by %client.onChangeCellArea(); from the console and the function works but the engine is never calling it.
#20
08/30/2005 (11:06 pm)
Seeing as I am looking for something alot like you are doing with the player location tracking I will follow along your work and post anything I might come up with that could help you out.

While you are useing this for dynamic loading of objects (if I unserstand right). I plan to use it as a dynamic location saving system so when someone moves a set distance there location is saved within a database. That way when if the player logs out or the server was to crash we could put the player back at a general rough location they where at when the server went down or they logged out.

I could also see other uses for this feature so if everything works out and you do not post it as a resource I would like to do so. So other users could benefit from the work and learn / maybe even expand the system into new areas.
Page «Previous 1 2