Starting from scratch , I tried the built in sync of Acknex and I failed. The engine sends updates and it isnt that inaccurate, but I couldnt make the Ghost entity follow the actual player entity smoothly, because the Server is just a little out of sync.

However , I started blank again and tested a simple function to predict position.
If I use 120 c_move calls, including gravity(to simulate a second passed on 60fps) the function ends up exactly where my player will be in 60 frames. The problem is that, I think, I cannot use that many c_move calls just for one client in one frame. If I have 1000 clients, this becomes 120 000 in one frame. If I use it at different frames(best scenario) it ends up at 2000, wich is still a lot. Not counting the 2 actual c_move calls per client to move the client every frame.

So I instead use one c_move call with 16 times the speed per tick to get the movement per second.

I send move instruction from the client and after one second I send stop instruction. I also send Pan with the move instruction, and the result is exact XYZ position on both client and server.
But the prediction is off by 0.1 to 2.0 quants.
I guess its all right for such small numbers, but I still have to script it to test and see what the result is.
I guess the 18 quants difference in my previous attemp was comming from the conversion from ticks to ms and using quants per ms to calculate the distance, but I dont intend to spend time figuring out why it didnt work.
I'm using ticks now (16/sec.)

Here are the codes so far:

Superku's gravity
Code:
function apply_gravity()
{
	var move_direction[3];
	var temp_trace[3];

	vec_set( temp_trace , vector( my.x , my.y , my.z - 150 + my.foot_height ) );
	c_trace( my.x , temp_trace , IGNORE_PASSABLE | IGNORE_ME | USE_BOX );
	if( !trace_hit )
	{
		vec_set( target , temp_trace );
	}
	my.soil_height = target.z - my.foot_height;
		
	if(my.z > my.soil_height + ( 5 + 20 * my.soil_contact ) * time_step )
	{
		my.soil_contact = 0;
		move_direction[2] = maxv( move_direction[2] - 9 * time_step , -90 );
	}
	else
	{
		my.soil_contact = 1;
		move_direction[2] = 0;
		my.z = my.soil_height;
	}

	if( move_direction[2] ) { c_move( me , nullvector , vector( 0 , 0 , move_direction[2] * time_step ) , IGNORE_PASSABLE ); }
	my.z = maxv( my.soil_height , my.z );
}



Client trigger and movement:
Code:
if(key_space == 1)
		{
			while(key_space == 1) { wait(1); }
			
			my.movement_input = 1;
			send_skill( my.movement_input , 0 );
			send_skill( my.pan , 0 );
// Prediction
////////////////
			var tmpvar1[3];
			vec_set( tmpvar1 , my.x );

			c_move( me , vector( 160 , 0 , 0 ) , NULLVECTOR , GLIDE );

			apply_gravity();

			vec_set( my.x , tmpvar1 );
// Actual movement
/////////////////////
			counter1 = 0;
			while(counter1 < 16)
			{
				counter1 += time_step;
				c_move( me , vector( 10 * time_step , 0 , 0 ) , NULLVECTOR , GLIDE );
				apply_gravity();
				wait(1);
			}

			my.movement_input = 0;
			send_skill( my.movement_input , 0 );
		}



Server movement:
Code:
if(my.movement_input == 1) { move_direction[0] = 10 * time_step; move_direction[1] = 0; }
		if(my.movement_input == 2) { move_direction[0] = (10 * 0.7142857142857143) * time_step; move_direction[1] = (10 * 0.7142857142857143) * time_step; }
		if(my.movement_input == 3) { move_direction[0] = (10 * 0.7142857142857143) * time_step; move_direction[1] = -(10 * 0.7142857142857143) * time_step; }
		if(my.movement_input == 4) { move_direction[0] = -10 * time_step; move_direction[1] = 0; }
		if(my.movement_input == 5) { move_direction[0] = -(10 * 0.7142857142857143) * time_step; move_direction[1] = (10 * 0.7142857142857143) * time_step; }
		if(my.movement_input == 6) { move_direction[0] = -(10 * 0.7142857142857143) * time_step; move_direction[1] = -(10 * 0.7142857142857143) * time_step; }
		if(my.movement_input == 7) { move_direction[0] = 0; move_direction[1] = 10 * time_step; }
		if(my.movement_input == 8) { move_direction[0] = 0; move_direction[1] = -10 * time_step; }
		if(my.movement_input == 0) { move_direction[0] = 0; move_direction[1] = 0; }
		c_move( me , move_direction , NULLVECTOR , GLIDE );
		
		apply_gravity();


I'll try next to add the prediction to the server code and use it just to eliminate Latency, then move the client every frame to get correct results from collision and correct frame-rate movement. This will limit the error to just the quant that I described above.
Next, I'll try to include a code to correct the server movement if the server receives a change in Pan between 2 updates (wich would desync the client-server ents) by moving the server back in time at a previous position, correcting Pan angle, predicting how far the client went during the latency and continuing with the normal frame-rate movement to keep both entities in sync. The server will send the updated position once each second with a floating start point depending on client behaviour. This deals with the problem of a global update clock sending 1000's of packets at once, by making the update clock local for each entity.
The back-in-time part will also be used to check if a client hit another client with a projectile X ms ago...


If I shape this into a working script, everyone wins laugh so , Please , drop in any opinions and ideas , because this works not only for MMOs, but for FPS and other styles.

So , any Ideas?



PS.: My main concern is the prediction, I need to figure out a way to make it as perfect as normal movement without using 1000's of c_move calls every frame (Oh, forgot the 1000's of c_trace calls for gravity.)


Extensive Multiplayer tutorial:
http://mesetts.com/index.php?page=201