Game Development Community

Terrain deform: Work on client & server. But not dedicated!

by Davis Ray Sickmon, Jr · in Torque Game Engine · 04/11/2004 (1:43 am) · 12 replies

So, I've had terrain deformation for both client and server working for quite a while. Or, at least it appears to work - but there's a snag. I finally got around to setting up a dedicated server for this beta (before, I was using a non-dedicated server so I could keep a watch to see when people popped into the server to play, and quickly join 'em.) Now that I've got all my dedicated server goodies set up on the script side, I start testing - at first, everything looks ok, until I dig a little deeper.

For some reason, this works fine on a non-dedicated server. On a dedicated server, the collision mesh for the terrain gets all sorts of wonky. If I raise an area of terrain up, and a player lands on it later (only happens with randomized terrain generation - another good use for this code) they just fall right on through to the original terrain height. (But, displays just fine on the client.)

(Go ahead - make fun of my TorqueFu here ;-) I had an issue with detecting the server -vs- the client version of the terrain, so the same code got wrote twice, once for the client and once for the server. Among other hacky things about this code. Sleep depravation and coding are a scary mix :-)

ConsoleFunction(setTerrainHeight, void, 4, 6, "(Point2F pos, F32 height, F32 width, F32 deformType)")
{
	Point2F pos;
	float height;
	float width;
	float deformType;
	Point2I gPos;
	Point2I minPos;
	Point2I maxPos;
	float currHeight;
	float currentHeight;
	float newHeight;
	dSscanf(argv[1], "%f %f", &pos.x, &pos.y);
	dSscanf(argv[2], "%f", &height);
        dSscanf(argv[3], "%f", &width);
	dSscanf(argv[4], "%f", &deformType);
	TerrainBlock * terr = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));
	Con::printf("In terrain deform.");
	if(terr)
	{
	    Con::printf("Ok, there's terrain.");
		if(terr->isServerObject())
		{
		    Con::printf("Yeah, it's a server object.");
		    if(deformType==1)
			{
				const MatrixF & mat = terr->getTransform();
				Point3F origin;
				mat.getColumn(3, &origin);
				F32 squareSize = (F32) terr->getSquareSize();
				F32 halfSquareSize = squareSize / 2;

				float x = (pos.x - origin.x + halfSquareSize) / squareSize;
				float y = (pos.y - origin.y + halfSquareSize) / squareSize;
   
				gPos.x = (S32)mFloor(x);
				gPos.y = (S32)mFloor(y);
                
				terr->setHeight(gPos, height);
				minPos.x = gPos.x - 2;
				minPos.y = gPos.y - 2;
				maxPos.x = gPos.x + 2;
				maxPos.y = gPos.y + 2;
				terr->updateGrid(minPos, maxPos);
                                // This updates the collision mesh
                                // Works fine on a non-dedicated server.
			}
                }
	}
	else
	{
	    Con::printf("Umm... it's not on the server.");
		TerrainBlock *terr = dynamic_cast<TerrainBlock*>(gClientSceneGraph->getCurrentTerrain());
            //.... same thing happens here as above ....
        }
}

You can see the stuff that's supposed to spew to the console for debuging purposes. On a non-dedicated server you'll see:
Quote:
In terrain deform.
Ok, there's terrain.
Yeah, it's a server object.
On a dedicated server you see... exactly the same bloody thing. Nothing crashes, nothing looks like it goes wrong. It just doesn't do it's thing.

This is the only thing holding up Beta 3 of Trajectory Zone now. I'm loosing hair by the hour tryin' to puzzle this one out. Help? :-)

#1
04/11/2004 (3:58 am)
Something hit me later - a big ol' duh.
Sim::findObject("Terrain")
Would do the same thing as:
TerrainBlock *terr = dynamic_cast<TerrainBlock*>(gServerSceneGraph->getCurrentT
errain());
Which is return the ghosted version of the terrain. Ok, how does one get thier hands on the 'real' terrain the server works with to adjust it using (hopefully) the same operations I was using before?
#2
04/11/2004 (4:08 am)
I know this is not what you wanted to hear but I have ran into the "fall trough terrain" eventho the ground obviously was there.

This only happened on my dedicated and not on my non-d.
This was when spawning a AI player.
#3
04/11/2004 (4:52 am)
What initially comes to mind is that you're likely not synchronizing the client and server properly. You may be getting results when you host a mission since you're in "short circuit" mode where you're acting as both server and client, hosting the dedicated server and joining it will give you good proof of whether things are synchronizing correctly or not.

Here's how I'd do it (likely in terrData.h\.cc):
Update network maskbits to some TerrainHeightChangeMask whenever the height of the server terrain is modified (you could do this directly in the function you posted).
Pack an update with the exact info which was used to modify the terrain.
In the unpack method, when you receive the TerrainHeightChangeMask, update the terrain (which will be the ghosted one).

If you just want this working in single player you can get the server object from the ghost or the ghost from the server with just a few function calls, but that doesn't sound like it's what you want.
#4
04/11/2004 (8:41 am)
Davis, just as a hint : I seem to recall colliding into "invisible" terrain when playing on your listen server by myself. Not often, mind you :)
IIRC, it happened after blowing up the terrain not to far from my cannon, and then ammo jumping myself to another spot : got stuck in the air for a few secs, and then finally fell to the ground.
So, it might be you have something in your code, a corner case, or something, that somehow the dedicated server triggers a lot more often ?
#5
04/11/2004 (9:09 am)
The problem is probably that updateGrid has only been developed and tested for use in the editor, which is never used on a dedicated box. I would check to see that updateGrid is doing what it ought to.
#6
04/11/2004 (2:36 pm)
Luigi: That will lead to some consistency problems. A list of all deformations must be kept on the server, and transmitted to the client. Unless you block connections after the game is started, a client can connect at any time. So you have to store all the deformations, and send them to newly connected clients. However, you have to hold sending all deformations until you've sent all previous deformations - and the packupdate method is really designed to handle current events.

And yeah - in single player, it's dead simple to do terrain deformation :-)

Ben: I think you're right. I'm looking along that direction now. I'll post any findings I get.
#7
04/12/2004 (8:51 am)
Any luck? The invisible walls were just too weird. :)
#8
04/12/2004 (11:11 am)
The invisible walls problem was one o' the bugs I whacked - it was a consistency issue between the server and client. I had an 'oops' where a particular type of deformation under specific circumstances wasn't being written to the client and the server exactly the same. (The client got what it should have, but, the server didn't) Using two machines and the new bots, I finally tracked it down last week.

However, I'm still lost on this issue at the moment :-P
#9
04/12/2004 (11:54 am)
Here's what I'm not 'getting' at the moment. To update the collision mesh, I call updateGrid(min point, max point) on gClientSceneGraph and gServerSceneGraph. Where is the original terrain collision mesh stored at?
#10
04/12/2004 (3:28 pm)
In a TerrainFile instance...
#11
04/12/2004 (3:29 pm)
Could it be a scoping issue? On a listen server you always have a client who is aware of the terrain (who it is in the scope of) but on a dedicated server you wouldn't have that...not sure if that has *anything* to do with anything but it was the first thing that popped into my mind. If it is dumb feel free to tell me to move along =)
#12
04/12/2004 (8:02 pm)
Hey you're absolutely right, I had not thought that through properly.

Good luck, I'll post if I think of anything relevant :)