2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 #include "freespace.h"
14 #include "multimsgs.h"
15 #include "multiutil.h"
24 // ---------------------------------------------------------------------------------------------------
25 // OBJECT UPDATE DEFINES/VARS
28 // how much data we're willing to put into a given oo packet
29 #define OO_MAX_SIZE 480
31 // new improved - more compacted info type
32 #define OO_POS_NEW (1<<0) //
33 #define OO_ORIENT_NEW (1<<1) //
34 #define OO_HULL_NEW (1<<2) //
35 #define OO_SHIELD_NEW (1<<3) //
36 #define OO_AI_MODE_NEW (1<<4) //
37 #define OO_SUBSYSTEMS_NEW (1<<5) //
38 #define OO_EXTRA_PHYSICS (1<<6) // means pos update will have desired velocity and orient update will have desired rotvel as well
40 #define OO_VIEW_CONE_DOT (0.1f)
41 #define OO_VIEW_DIFF_TOL (0.15f) // if the dotproducts differ this far between frames, he's coming into view
45 #define OO_NEAR_DIST (200.0f)
47 #define OO_MIDRANGE_DIST (600.0f)
49 #define OO_FAR_DIST (1200.0f)
51 // how often we should send full hull/shield updates
52 #define OO_HULL_SHIELD_TIME 600
53 #define OO_SUBSYS_TIME 1000
55 // timestamp values for object update times based on client's update level.
56 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] =
58 150, // low update -- do every 1 second
59 125, // medium update -- do every 1/2 second
60 100, // high update -- every frame
65 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
74 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
83 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
92 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
95 1000, // medium update
101 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
104 1200, // medium update
110 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
113 1600, // medium update
118 // let's keep track of the breakdown of individual object update elements
119 int OO_pos_total = 0;
120 int OO_vel_total = 0;
121 int OO_desired_vel_total = 0;
122 int OO_orient_total = 0;
123 int OO_rotvel_total = 0;
124 int OO_desired_rotvel_total = 0;
125 int OO_hull_total = 0;
126 int OO_shield_total = 0;
127 int OO_single_subsys_total = 0;
128 int OO_subsys_total = 0;
129 int OO_forward_thrust_total = 0;
131 // keys for selectively disabling interpolation - these keys can be used in combination with each other
132 #define LAG_OFF_KEY SDLK_7
133 #define LEVEL_1_OFF_KEY SDLK_8
134 #define LEVEL_2_OFF_KEY SDLK_9
138 // ship index list for possibly sorting ships based upon distance, etc
139 short OO_ship_index[MAX_SHIPS];
141 int OO_debug_info = 0 ;
142 DCF(ood, "switch on and off targeted object update info")
144 OO_debug_info = !OO_debug_info;
147 // object update stats storing stuff
148 #define OO_POS_NEW (1<<0) //
149 #define OO_ORIENT_NEW (1<<1) //
150 #define OO_HULL_NEW (1<<2) //
151 #define OO_SHIELD_NEW (1<<3) //
152 #define OO_AI_MODE_NEW (1<<4) //
153 #define OO_SUBSYSTEMS_NEW (1<<5) //
154 #define OO_EXTRA_PHYSICS (1<<6) // means pos update will have desired velocity and orient update will have desired rotvel as well
156 #define NUM_UPDATE_RECORDS 5 // last five second average
157 typedef struct np_update_record {
158 int pos_bytes; // how many pos bytes we've sent
159 int orient_bytes; // how many orient bytes we've sent
160 int hull_bytes; // how many hull bytes we've sent
161 int shield_bytes; // how many shield bytes we've sent
162 int ai_mode_bytes; // how many ai_mode bytes we've sent
163 int extra_physics_bytes; // how many bytes of extra physics info we've sent
164 int fthrust_bytes; // how many bytes of forward thrust we've sent
165 int subsys_bytes; // how many bytes of subsys data we've sent
168 int pos_bytes_frame; // how many bytes we've sent in the last second
169 int pos_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
170 int pos_record_count;
174 int orient_bytes_frame; // how many bytes we've sent in the last second
175 int orient_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
176 int orient_record_count;
180 int hull_bytes_frame; // how many bytes we've sent in the last second
181 int hull_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
182 int hull_record_count;
186 int shield_bytes_frame; // how many bytes we've sent in the last second
187 int shield_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
188 int shield_record_count;
192 int ai_mode_bytes_frame; // how many bytes we've sent in the last second
193 int ai_mode_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
194 int ai_mode_record_count;
197 int extra_physics_stamp;
198 int extra_physics_bytes_frame; // how many bytes we've sent in the last second
199 int extra_physics_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
200 int extra_physics_record_count;
201 float extra_physics_avg;
204 int fthrust_bytes_frame; // how many bytes we've sent in the last second
205 int fthrust_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
206 int fthrust_record_count;
210 int subsys_bytes_frame; // how many bytes we've sent in the last second
211 int subsys_records[NUM_UPDATE_RECORDS]; // average of the last bunch of bytes we've sent
212 int subsys_record_count;
217 #define R_AVG(ct, ar, avg) do { int av_idx; float av_sum = 0.0f; if(ct == 0){avg = 0;} else { for(av_idx=0; av_idx<ct; av_idx++){ av_sum += (float)ar[av_idx]; } avg = av_sum / (float)ct;} } while(0);
218 #define R_POS_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->pos_bytes += byte_count; r->pos_bytes_frame += byte_count; if(time(NULL) > r->pos_stamp){ if(r->pos_record_count >= NUM_UPDATE_RECORDS) { memmove(r->pos_records, r->pos_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->pos_records[NUM_UPDATE_RECORDS-1] = r->pos_bytes_frame; } else {r->pos_records[r->pos_record_count++] = r->pos_bytes_frame; } r->pos_stamp = time(NULL); r->pos_bytes_frame = 0; R_AVG(r->pos_record_count, r->pos_records, r->pos_avg); } } while(0);
219 #define R_ORIENT_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->orient_bytes += byte_count; r->orient_bytes_frame += byte_count; if(time(NULL) > r->orient_stamp){ if(r->orient_record_count >= NUM_UPDATE_RECORDS) { memmove(r->orient_records, r->orient_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->orient_records[NUM_UPDATE_RECORDS-1] = r->orient_bytes_frame;} else { r->orient_records[r->orient_record_count++] = r->orient_bytes_frame; } r->orient_stamp = time(NULL); r->orient_bytes_frame = 0; R_AVG(r->orient_record_count, r->orient_records, r->orient_avg); } } while(0);
220 #define R_HULL_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->hull_bytes += byte_count; r->hull_bytes_frame += byte_count; if(time(NULL) > r->hull_stamp){ if(r->hull_record_count >= NUM_UPDATE_RECORDS) { memmove(r->hull_records, r->hull_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->hull_records[NUM_UPDATE_RECORDS-1] = r->hull_bytes_frame;} else { r->hull_records[r->hull_record_count++] = r->hull_bytes_frame; } r->hull_stamp = time(NULL); r->hull_bytes_frame = 0; R_AVG(r->hull_record_count, r->hull_records, r->hull_avg); } } while(0);
221 #define R_SHIELD_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->shield_bytes += byte_count; r->shield_bytes_frame += byte_count; if(time(NULL) > r->shield_stamp){ if(r->shield_record_count >= NUM_UPDATE_RECORDS) { memmove(r->shield_records, r->shield_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->shield_records[NUM_UPDATE_RECORDS-1] = r->shield_bytes_frame; } else { r->shield_records[r->shield_record_count++] = r->shield_bytes_frame; } r->shield_stamp = time(NULL); r->shield_bytes_frame = 0; R_AVG(r->shield_record_count, r->shield_records, r->shield_avg); } } while(0);
222 #define R_AI_MODE_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->ai_mode_bytes += byte_count; r->ai_mode_bytes_frame += byte_count; if(time(NULL) > r->ai_mode_stamp){ if(r->ai_mode_record_count >= NUM_UPDATE_RECORDS) { memmove(r->ai_mode_records, r->ai_mode_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->ai_mode_records[NUM_UPDATE_RECORDS-1] = r->ai_mode_bytes_frame;} else { r->ai_mode_records[r->ai_mode_record_count++] = r->ai_mode_bytes_frame; } r->ai_mode_stamp = time(NULL); r->ai_mode_bytes_frame = 0; R_AVG(r->ai_mode_record_count, r->ai_mode_records, r->ai_mode_avg); } } while(0);
223 #define R_EXTRA_PHYSICS_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->extra_physics_bytes += byte_count; r->extra_physics_bytes_frame += byte_count; if(time(NULL) > r->extra_physics_stamp){ if(r->extra_physics_record_count >= NUM_UPDATE_RECORDS) { memmove(r->extra_physics_records, r->extra_physics_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->extra_physics_records[NUM_UPDATE_RECORDS-1] = r->extra_physics_bytes_frame;} else { r->extra_physics_records[r->extra_physics_record_count++] = r->extra_physics_bytes_frame; } r->extra_physics_stamp = time(NULL); r->extra_physics_bytes_frame = 0; R_AVG(r->extra_physics_record_count, r->extra_physics_records, r->extra_physics_avg); } } while(0);
224 #define R_FTHRUST_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->fthrust_bytes += byte_count; r->fthrust_bytes_frame += byte_count; if(time(NULL) > r->fthrust_stamp){ if(r->fthrust_record_count >= NUM_UPDATE_RECORDS) { memmove(r->fthrust_records, r->fthrust_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->fthrust_records[NUM_UPDATE_RECORDS-1] = r->fthrust_bytes_frame; } else { r->fthrust_records[r->fthrust_record_count++] = r->fthrust_bytes_frame; } r->fthrust_stamp = time(NULL); r->fthrust_bytes_frame = 0; R_AVG(r->fthrust_record_count, r->fthrust_records, r->fthrust_avg); } } while(0);
225 #define R_SUBSYS_ADD(net_plr, byte_count) do { if((net_plr == NULL) || (!MULTIPLAYER_MASTER)){break;} np_update_record *r = &OO_update_records[NET_PLAYER_INDEX(net_plr)]; r->subsys_bytes += byte_count; r->subsys_bytes_frame += byte_count; if(time(NULL) > r->subsys_stamp){ if(r->subsys_record_count >= NUM_UPDATE_RECORDS) { memmove(r->subsys_records, r->subsys_records+1, sizeof(int) * (NUM_UPDATE_RECORDS - 1)); r->subsys_records[NUM_UPDATE_RECORDS-1] = r->subsys_bytes_frame; } else { r->subsys_records[r->subsys_record_count++] = r->subsys_bytes_frame; } r->subsys_stamp = time(NULL); r->subsys_bytes_frame = 0; R_AVG(r->subsys_record_count, r->subsys_records, r->subsys_avg); } } while(0);
226 np_update_record OO_update_records[MAX_PLAYERS];
228 #define R_POS_ADD(net_plr, byte_count)
229 #define R_ORIENT_ADD(net_plr, byte_count)
230 #define R_HULL_ADD(net_plr, byte_count)
231 #define R_SHIELD_ADD(net_plr, byte_count)
232 #define R_AI_MODE_ADD(net_plr, byte_count)
233 #define R_EXTRA_PHYSICS_ADD(net_plr, byte_count)
234 #define R_FTHRUST_ADD(net_plr, byte_count)
235 #define R_SUBSYS_ADD(net_plr, byte_count)
237 int OO_update_index = -1; // index into OO_update_records for displaying update record info
239 // ---------------------------------------------------------------------------------------------------
240 // OBJECT UPDATE FUNCTIONS
243 object *OO_player_obj;
246 int multi_oo_sort_func(const void *ship1, const void *ship2)
249 short index1, index2;
253 memcpy(&index1, ship1, sizeof(short));
254 memcpy(&index2, ship2, sizeof(short));
256 // if the indices are bogus, or the objnums are bogus, return ">"
257 if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
262 obj1 = &Objects[Ships[index1].objnum];
263 obj2 = &Objects[Ships[index2].objnum];
265 // get the distance to the player obj for both
266 dist1 = vm_vec_dist_quick(&OO_player_obj->pos, &obj1->pos);
267 dist2 = vm_vec_dist_quick(&OO_player_obj->pos, &obj2->pos);
269 return (dist1 <= dist2) ? -1 : 1;
272 // build the list of ship indices to use when updating for this player
273 void multi_oo_build_ship_list(net_player *pl)
280 // set all indices to be -1
281 for(idx = 0;idx<MAX_SHIPS; idx++){
282 OO_ship_index[idx] = -1;
285 // get the player object
286 if(pl->player->objnum < 0){
289 player_obj = &Objects[pl->player->objnum];
291 // go through all other relevant objects
293 for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
294 // if it is an invalid ship object, skip it
295 if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
299 // if we're a standalone server, don't send any data regarding its pseudo-ship
300 if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
304 // must be a ship, a weapon, and _not_ an observer
305 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
309 // don't send info for dying ships
310 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
314 // don't send him info for himself
315 if ( &Objects[moveup->objnum] == player_obj ){
319 // don't send info for his targeted ship here, since its always done first
320 if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
325 if(ship_index < MAX_SHIPS){
326 OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
330 // maybe qsort the thing here
331 OO_player_obj = player_obj;
333 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
337 // set global object update timestamp for this frame
338 void multi_oo_set_global_timestamp()
340 OO_global_time = timer_get_milliseconds();
344 int Interpolated_orient_inited[MAX_SHIPS];
345 float Interpolate_dot[MAX_SHIPS];
346 matrix Interpolated_orient[MAX_SHIPS];
348 void multi_oo_interpolate_init()
352 for (i=0; i<MAX_SHIPS;i++ ) {
353 Interpolated_orient_inited[i] = 0;
358 void matrix_to_quaternion( D3DRMQUATERNION *quat, matrix *mat )
363 vm_matrix_to_rot_axis_and_angle(mat, &theta, &rot_axis);
365 quat->v.x = rot_axis.x;
366 quat->v.y = rot_axis.y;
367 quat->v.z = rot_axis.z;
371 void quaternion_to_matrix( matrix *mat, D3DRMQUATERNION *quat )
376 rot_axis.x = quat->v.x;
377 rot_axis.y = quat->v.y;
378 rot_axis.z = quat->v.z;
381 vm_quaternion_rotate(mat, theta, &rot_axis);
382 vm_orthogonalize_matrix(mat);
386 // interpolate for this object
387 void multi_oo_interpolate(object *objp, interp_info *current, interp_info *last)
394 // save position and orientation
395 objp->last_pos = objp->pos;
396 objp->last_orient = objp->orient;
399 obj_move_all_pre(objp, flFrametime);
401 // level 1 interpolation
402 // if the key disabling level 1 interpolation is pressed, skip
403 if(!key_pressed(LEVEL_1_OFF_KEY)){
404 float lag = fl2i(current->lowest_ping)/1000.0f;
406 lag /= 2.0f; // Our ping time is round trip, we only account for 1/2 the trip.
408 // Correct bogus lags
413 // test - should behave like the old way of doing stuff
414 if (current->vel_time == OO_global_time){
415 objp->phys_info.vel = current->vel;
417 if (current->rotvel_time == OO_global_time){
418 objp->phys_info.rotvel = current->rotvel;
421 objp->phys_info.desired_vel = current->desired_vel;
422 objp->phys_info.desired_rotvel = current->desired_rotvel;
424 // if ( !SDL_strcasecmp( Ships[objp->instance].ship_name, "alpha 1")) {
425 // mprintf(( "Rotvel = %.3f, %.3f, %.3f\n", current->rotvel.x, current->rotvel.y, current->rotvel.z ));
428 if ( !Interpolated_orient_inited[objp->instance] ) {
429 Interpolated_orient[objp->instance] = objp->orient;
430 Interpolated_orient_inited[objp->instance] = 1;
433 matrix *actual_orient = &Interpolated_orient[objp->instance];
435 // if this is from a network update
436 if(current->orient_time == OO_global_time){
437 *actual_orient = current->orient;
440 physics_sim_rot(actual_orient, &objp->phys_info, lag );
442 physics_sim_rot(actual_orient, &objp->phys_info, flFrametime );
445 // otherwise, if between network updates
447 physics_sim_rot(actual_orient, &objp->phys_info, flFrametime );
450 if(key_pressed(LAG_OFF_KEY)){
451 // Make orient go quickly to actual_orient
456 vector angular_accel;
458 angular_accel.x = 1000.0f;
459 angular_accel.y = 1000.0f;
460 angular_accel.z = 1000.0f;
461 vm_matrix_interpolate(actual_orient, &objp->orient, &objp->phys_info.rotvel, flFrametime, &tmp,
462 &w_out, &objp->phys_info.max_rotvel, &angular_accel, 1 );
464 objp->phys_info.rotvel = w_out;
470 D3DRMQUATERNION in1, in2, out;
472 matrix_to_quaternion( &in1, &objp->orient );
473 matrix_to_quaternion( &in2, actual_orient );
475 D3DRMQuaternionSlerp( &out, &in1, &in2, 0.50f );
477 quaternion_to_matrix( &objp->orient, &out );
479 objp->orient = *actual_orient;
481 objp->orient = *actual_orient;
484 if(current->pos_time == OO_global_time){
485 objp->pos = current->pos;
488 physics_sim_vel(&objp->pos, &objp->phys_info, lag, &objp->orient );
490 physics_sim_vel(&objp->pos, &objp->phys_info, flFrametime, &objp->orient );
493 physics_sim_vel(&objp->pos, &objp->phys_info, flFrametime, &objp->orient );
497 objp->phys_info.speed = vm_vec_mag(&objp->phys_info.vel); // Note, cannot use quick version, causes cumulative error, increasing speed.
498 objp->phys_info.fspeed = vm_vec_dot(&objp->orient.fvec, &objp->phys_info.vel); // instead of vector magnitude -- use only forward vector since we are only interested in forward velocity
500 // no interpolation, so bash
502 objp->orient = current->orient;
503 objp->pos = current->pos;
506 // copy the "current info" to the "last" info
507 memcpy(last, current, sizeof(interp_info));
510 obj_move_all_post(objp, flFrametime);
512 // if I'm the server of the game, do firing stuff here
513 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (objp->flags & OF_PLAYER_SHIP)){
514 pnum = multi_find_player_by_object( objp );
516 pp = Net_players[pnum].player;
517 obj_player_fire_stuff( objp, pp->ci );
523 // do all interpolation for this frame
524 void multi_oo_interpolate_all()
530 // for the server, this means "interpolate all player ships"
531 // for the client, this means "interpolate all ships"
533 // iterate over all _ships_
534 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
535 // get the ships object
536 objp = &Objects[so->objnum];
537 shipp = &Ships[objp->instance];
539 // ignore dead or dying objects
540 if ( !(objp->flags&OF_SHOULD_BE_DEAD) ) {
541 // multiplayer servers should only do the interpolation for player ships
542 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
543 if((objp->type != OBJ_SHIP) || !(objp->flags & OF_PLAYER_SHIP) || (objp == Player_obj)){
547 if((objp->type != OBJ_SHIP) || (objp == Player_obj)){
553 multi_oo_interpolate(objp, &shipp->int_cur, &shipp->int_last);
558 // handle incoming new data for the given object
559 void multi_oo_handle_new_data(ship *shipp, object *obj_data, ubyte oo_flags, net_player *pl)
561 // fill in the int_current value for the passed ship
563 // position type update
564 if(oo_flags & OO_POS_NEW){
565 // copy in position for now
566 shipp->int_cur.pos = obj_data->pos;
567 shipp->int_cur.pos_time = OO_global_time;
570 shipp->int_cur.vel = obj_data->phys_info.vel;
571 shipp->int_cur.vel_time = OO_global_time;
574 if(oo_flags & OO_EXTRA_PHYSICS){
575 // copy in desired velocity
576 shipp->int_cur.desired_vel = obj_data->phys_info.desired_vel;
577 shipp->int_cur.desired_vel_time = OO_global_time;
580 shipp->int_cur.desired_vel = obj_data->phys_info.vel;
581 shipp->int_cur.desired_vel_time = OO_global_time;
585 // orientation type update
586 if(oo_flags & OO_ORIENT_NEW){
587 // copy in orientation for now
588 shipp->int_cur.orient = obj_data->orient;
589 shipp->int_cur.orient_time = OO_global_time;
592 shipp->int_cur.rotvel = obj_data->phys_info.rotvel;
593 shipp->int_cur.rotvel_time = OO_global_time;
595 if(oo_flags & OO_EXTRA_PHYSICS){
596 // copy in desired velocity
597 shipp->int_cur.desired_rotvel = obj_data->phys_info.desired_rotvel;
598 shipp->int_cur.desired_rotvel_time = OO_global_time;
600 shipp->int_cur.desired_rotvel = obj_data->phys_info.rotvel;
601 shipp->int_cur.desired_rotvel_time = OO_global_time;
605 // set the ping values
606 shipp->int_cur.lowest_ping = multi_ping_get_lowest(&pl->s_info.ping);
607 shipp->int_cur.lowest_ping_avg = multi_ping_lowest_avg(&pl->s_info.ping);
610 // increment the packet sequence # (used for object updates from the server, as well as "control info"
611 // update from clients
612 void multi_oo_increment_seq()
614 // increment the sequencing reference count for this frame if necessary
615 if(Netgame.server_update_frame_ref != Framecount){
616 Netgame.server_update_frame_ref = Framecount;
618 if(Netgame.server_update_seq == 0xffff){
619 Netgame.server_update_seq = 0;
621 Netgame.server_update_seq++;
626 // pack information for a client (myself), return bytes added
627 int multi_oo_pack_client_data(ubyte *data)
630 ushort tnet_signature;
631 char t_subsys, l_subsys;
634 // get our firing stuff
635 out_flags = Net_player->s_info.accum_buttons;
637 // zero these values for now
638 Net_player->s_info.accum_buttons = 0;
640 // add any necessary targeting flags
641 if ( Player_ai->current_target_is_locked ){
642 out_flags |= OOC_TARGET_LOCKED;
644 if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){
645 out_flags |= OOC_TARGET_SEEK_LOCK;
647 if ( Player->locking_on_center ){
648 out_flags |= OOC_LOCKING_ON_CENTER;
651 // copy the final flags in
652 memcpy(data, &out_flags, sizeof(ubyte));
655 // client eye information
656 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size, &Net_player->s_info.eye_pos );
657 SDL_assert(ret == OO_POS_RET_SIZE);
660 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size, &Net_player->s_info.eye_orient );
661 SDL_assert(ret == OO_ORIENT_RET_SIZE);
664 // client targeting information
668 // if nothing targeted
669 if(Player_ai->target_objnum == -1){
672 // if something is targeted
674 // target net signature
675 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
677 // targeted subsys index
678 if(Player_ai->targeted_subsys != NULL){
679 t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
682 // locked targeted subsys index
683 if(Player->locking_subsys != NULL){
684 l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
689 memcpy(data + packet_size, &tnet_signature, sizeof(ushort));
690 packet_size += sizeof(ushort);
691 memcpy(data + packet_size, &t_subsys, sizeof(char));
692 packet_size += sizeof(char);
693 memcpy(data + packet_size, &l_subsys, sizeof(char));
694 packet_size += sizeof(char);
699 // pack the appropriate info into the data
700 #define PACK_PERCENT(v) {ubyte upercent; if(v < 0.0f){v = 0.0f;} upercent = (v * 255.0f) <= 255.0f ? (ubyte)(v * 255.0f) : (ubyte)255; memcpy(data + packet_size + header_bytes, &upercent, sizeof(ubyte)); packet_size++; }
701 int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
713 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
719 SDL_assert(objp->type == OBJ_SHIP);
720 if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
721 shipp = &Ships[objp->instance];
726 // if we're a client (and therefore sending control info), send button info first
727 if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
728 packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);
732 if(oo_flags & OO_ORIENT_NEW){
733 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
734 SDL_assert(ret == OO_ORIENT_RET_SIZE);
736 R_ORIENT_ADD(pl, ret);
739 OO_orient_total += ret;
741 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
745 OO_rotvel_total += ret;
746 R_ORIENT_ADD(pl, ret);
748 if(oo_flags & OO_EXTRA_PHYSICS){
749 ret = (ubyte)multi_pack_unpack_desired_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info, &Ship_info[shipp->ship_info_index]);
753 OO_desired_rotvel_total += ret;
754 R_EXTRA_PHYSICS_ADD(pl, ret);
758 // position, velocity
759 if ( oo_flags & OO_POS_NEW ) {
760 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
767 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
775 if(oo_flags & OO_EXTRA_PHYSICS){
776 ret = (ubyte)multi_pack_unpack_desired_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info, &Ship_info[shipp->ship_info_index]);
786 percent = (char)(objp->phys_info.forward_thrust * 100.0f);
787 SDL_assert( percent <= 100 );
789 memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
793 OO_forward_thrust_total++;
794 R_FTHRUST_ADD(pl, 1);
797 if ( oo_flags & OO_HULL_NEW ){
798 // add the hull value for this guy
799 temp = (objp->hull_strength / Ship_info[shipp->ship_info_index].initial_hull_strength);
808 if( oo_flags & OO_SHIELD_NEW ){
809 // pack 2 shield values into each byte
812 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
816 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
820 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
824 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
827 OO_shield_total += 4;
832 if( oo_flags & OO_SUBSYSTEMS_NEW ){
834 ship_subsys *subsysp;
836 // just in case we have some kind of invalid data (should've been taken care of earlier in this function)
837 if(shipp->ship_info_index < 0){
839 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
843 // add the # of subsystems, and their data
845 ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
846 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
850 // now the subsystems.
851 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
852 temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits;
860 if( oo_flags & OO_AI_MODE_NEW){
861 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
862 short submode = (short)(Ai_info[shipp->ai_index].submode);
863 ushort target_signature;
865 target_signature = 0;
866 if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
867 target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
870 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
872 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
874 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));
877 R_AI_MODE_ADD(pl, 5);
880 SDL_assert(packet_size < 255);
881 data_size = (ubyte)packet_size;
883 // add the object's net signature, type and oo_flags
885 // don't add for clients
886 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
887 ADD_USHORT( objp->net_signature );
889 ADD_DATA( oo_flags );
890 ADD_DATA( data_size );
891 packet_size += data_size;
893 memcpy(data_out,data,packet_size);
898 // unpack information for a client , return bytes processed
899 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
906 memcpy(&in_flags, data, sizeof(ubyte));
909 // if we have a valid netplayer pointer
912 pl->player->ci.fire_primary_count = 0;
913 // if ( in_flags & OOC_FIRE_PRIMARY && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
914 if ( in_flags & OOC_FIRE_PRIMARY){
915 pl->player->ci.fire_primary_count = 1;
919 pl->player->ci.fire_secondary_count = 0;
920 if ( in_flags & OOC_FIRE_SECONDARY ){
921 pl->player->ci.fire_secondary_count = 1;
924 // countermeasure fired
925 pl->player->ci.fire_countermeasure_count = 0;
926 // if ( in_flags & OOC_FIRE_COUNTERMEASURE && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
927 if ( in_flags & OOC_FIRE_COUNTERMEASURE ){
928 pl->player->ci.fire_countermeasure_count = 1;
931 // set up aspect locking information
932 pl->player->locking_on_center = 0;
933 if ( in_flags & OOC_LOCKING_ON_CENTER ){
934 pl->player->locking_on_center = 1;
937 // other locking information
938 if((pl->player->objnum != -1) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Ships[Objects[pl->player->objnum].instance].ai_index != -1)){
939 shipp = &Ships[Objects[pl->player->objnum].instance];
941 Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
942 if ( in_flags & OOC_TARGET_SEEK_LOCK ) {
943 Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
945 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
950 // client eye information
956 memset(&pi,0,sizeof(physics_info));
958 ret = multi_pack_unpack_position( 0, data + offset, &eye_pos );
959 SDL_assert(ret == OO_POS_RET_SIZE);
962 ret = multi_pack_unpack_orient( 0, data + offset, &eye_orient );
963 SDL_assert(ret == OO_ORIENT_RET_SIZE);
966 // if we have a valid player, copy the info in
968 pl->s_info.eye_pos = eye_pos;
969 pl->s_info.eye_orient = eye_orient;
972 // client targeting information
974 char t_subsys,l_subsys;
978 GET_USHORT(tnet_sig);
982 // try and find the targeted object
985 tobj = multi_get_network_object( tnet_sig );
987 // maybe fill in targeted object values
988 if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){
989 // assign the target object
990 if(Objects[pl->player->objnum].type == OBJ_SHIP){
991 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
993 pl->s_info.target_objnum = OBJ_INDEX(tobj);
995 // assign subsystems if possible
996 if(Objects[pl->player->objnum].type == OBJ_SHIP){
997 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL;
998 if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
999 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
1003 pl->player->locking_subsys = NULL;
1004 if(Objects[pl->player->objnum].type == OBJ_SHIP){
1005 if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
1006 pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
1014 // unpack the object data, return bytes processed
1015 #define UNPACK_PERCENT(v) { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
1016 int multi_oo_unpack_data(net_player *pl, ubyte *data, ushort packet_sequence_num, ushort packet_sequence_ref, int time_born)
1019 object *objp,obj_fill,*pobjp;
1021 ubyte data_size, oo_flags;
1026 // add the object's net signature, type and oo_flags
1027 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1028 GET_USHORT( net_sig );
1030 GET_DATA( oo_flags );
1031 GET_DATA( data_size );
1033 // try and find the object
1034 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1035 pobjp = multi_get_network_object(net_sig);
1037 if((pl != NULL) && (pl->player->objnum != -1)){
1038 pobjp = &Objects[pl->player->objnum];
1044 // if we can't find the object, set pointer to bogus object to continue reading the data
1045 // ignore out of sequence packets here as well
1046 if ( (pobjp == NULL) || (pobjp->type != OBJ_SHIP) || (pobjp->instance < 0) || (Ships[pobjp->instance].ship_info_index < 0) || (packet_sequence_num < packet_sequence_ref)){
1047 if(packet_sequence_num < packet_sequence_ref){
1048 nprintf(("Network","Tossing out of order packet!!\n"));
1051 offset += data_size;
1057 shipp = &Ships[pobjp->instance];
1059 // use the "fill" object
1061 memset(objp,0,sizeof(object));
1062 objp->type = OBJ_SHIP;
1063 objp->phys_info.max_vel = pobjp->phys_info.max_vel;
1064 objp->phys_info.max_rotvel = pobjp->phys_info.max_rotvel;
1065 objp->phys_info.afterburner_max_vel = pobjp->phys_info.afterburner_max_vel;
1066 objp->orient = pobjp->orient;
1067 objp->instance = pobjp->instance;
1069 // if this is from a player, read his button info
1070 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1071 offset += multi_oo_unpack_client_data(pl, data + offset);
1077 if ( oo_flags & OO_ORIENT_NEW ) {
1078 int r2 = multi_pack_unpack_orient( 0, data + offset, &objp->orient );
1079 SDL_assert(fl_abs(objp->orient.fvec.x) < 10000.0f);
1080 SDL_assert(fl_abs(objp->orient.fvec.y) < 10000.0f);
1081 SDL_assert(fl_abs(objp->orient.fvec.z) < 10000.0f);
1082 SDL_assert(fl_abs(objp->orient.uvec.x) < 10000.0f);
1083 SDL_assert(fl_abs(objp->orient.uvec.y) < 10000.0f);
1084 SDL_assert(fl_abs(objp->orient.uvec.z) < 10000.0f);
1085 SDL_assert(fl_abs(objp->orient.rvec.x) < 10000.0f);
1086 SDL_assert(fl_abs(objp->orient.rvec.y) < 10000.0f);
1087 SDL_assert(fl_abs(objp->orient.rvec.z) < 10000.0f);
1090 int r5 = multi_pack_unpack_rotvel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info );
1093 if(oo_flags & OO_EXTRA_PHYSICS){
1094 int r6 = multi_pack_unpack_desired_rotvel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info, &Ship_info[Ships[pobjp->instance].ship_info_index] );
1100 if ( oo_flags & OO_POS_NEW ) {
1101 int r1 = multi_pack_unpack_position( 0, data + offset, &objp->pos );
1104 int r3 = multi_pack_unpack_vel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info );
1108 if(oo_flags & OO_EXTRA_PHYSICS){
1109 int r4 = multi_pack_unpack_desired_vel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info, &Ship_info[Ships[objp->instance].ship_info_index] );
1116 percent = (char)(objp->phys_info.forward_thrust * 100.0f);
1117 SDL_assert( percent <= 100 );
1121 if ( oo_flags & OO_HULL_NEW ){
1122 UNPACK_PERCENT(fpct);
1123 pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength;
1127 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1128 nprintf(("Network", "HULL UPDATE : %d, %s\n", pobjp->hull_strength, Ships[pobjp->instance].ship_name));
1134 if ( oo_flags & OO_SHIELD_NEW ){
1135 float shield_0, shield_1, shield_2, shield_3;
1137 // unpack the 4 quadrants
1138 UNPACK_PERCENT(shield_0);
1139 UNPACK_PERCENT(shield_1);
1140 UNPACK_PERCENT(shield_2);
1141 UNPACK_PERCENT(shield_3);
1143 pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
1144 pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
1145 pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
1146 pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
1150 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1151 nprintf(("Network", "SHIELD UPDATE %f %f %f %f: %s\n", pobjp->shields[0], pobjp->shields[1], pobjp->shields[2], pobjp->shields[3], Ships[pobjp->instance].ship_name));
1156 if ( oo_flags & OO_SUBSYSTEMS_NEW ) {
1157 ubyte n_subsystems, subsys_count;
1158 float subsystem_percent[MAX_MODEL_SUBSYSTEMS];
1159 ship_subsys *subsysp;
1163 // get the data for the subsystems
1164 GET_DATA( n_subsystems );
1165 for ( i = 0; i < n_subsystems; i++ ){
1166 UNPACK_PERCENT( subsystem_percent[i] );
1169 // fill in the subsystem data
1171 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
1174 val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
1175 subsysp->current_hits = val;
1177 // add the value just generated (it was zero'ed above) into the array of generic system types
1178 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
1179 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
1180 shipp->subsys_info[subsys_type].current_hits += val;
1183 // if we've reached max subsystems for some reason, bail out
1184 if(subsys_count >= n_subsystems){
1189 // recalculate all ship subsystems
1190 ship_recalc_subsys_strength( shipp );
1194 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1195 nprintf(("Network", "SUBSYS UPDATE : %s\n", Ships[pobjp->instance].ship_name));
1200 if ( oo_flags & OO_AI_MODE_NEW ) {
1203 ushort target_signature;
1204 object *target_objp;
1208 GET_USHORT( target_signature );
1210 if(shipp->ai_index > 0){
1211 Ai_info[shipp->ai_index].mode = umode;
1212 Ai_info[shipp->ai_index].submode = submode;
1214 // set this guys target objnum
1215 target_objp = multi_get_network_object( target_signature );
1216 if ( target_objp == NULL ){
1217 Ai_info[shipp->ai_index].target_objnum = -1;
1219 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
1224 // handle this new data
1226 multi_oo_handle_new_data(&Ships[pobjp->instance], objp, oo_flags, pl);
1232 // reset the timestamp appropriately for the passed in object
1233 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1236 int player_index = NET_PLAYER_INDEX(pl);
1238 // if this is the guy's target,
1239 if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1240 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1242 // reset the timestamp appropriately
1244 // base it upon range
1247 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1251 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1255 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1259 // base it upon range
1262 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1266 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1270 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1276 // reset the timestamp for this object
1277 if(objp->type == OBJ_SHIP){
1278 Ships[objp->instance].pos_update_stamp[player_index] = timestamp(stamp);
1282 // reset the timestamp appropriately for the passed in object
1283 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1285 Ships[objp->instance].status_update_stamp[player_index] = timestamp(OO_HULL_SHIELD_TIME);
1288 // reset the timestamp appropriately for the passed in object
1289 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1291 Ships[objp->instance].subsys_update_stamp[player_index] = timestamp(OO_SUBSYS_TIME);
1294 // determine what needs to get sent for this player regarding the passed object, and when
1295 int multi_oo_maybe_update(net_player *pl,object *pobj,object *obj,ubyte *data)
1302 float eye_dot, dist;
1307 // if the timestamp has elapsed for this guy, send stuff
1308 player_index = NET_PLAYER_INDEX(pl);
1309 if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1313 // determine what the timestamp is for this object
1314 if(obj->type == OBJ_SHIP){
1315 stamp = Ships[obj->instance].pos_update_stamp[player_index];
1316 ship_index = &Ships[obj->instance] - Ships;
1321 // if we're supposed to update this guy
1322 if((stamp == -1) || timestamp_elapsed(stamp)){
1323 // check dot products
1324 player_eye = pl->s_info.eye_orient.fvec;
1325 vm_vec_sub(&obj_dot,&obj->pos,&pobj->pos);
1326 vm_vec_normalize(&obj_dot);
1327 eye_dot = vm_vec_dot(&obj_dot,&player_eye);
1328 in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;
1330 // if he's in view or is "swinging into view", send info for him
1331 if(Interpolate_dot[ship_index] >= -1.0f){
1332 if((eye_dot - Interpolate_dot[ship_index]) >= OO_VIEW_DIFF_TOL){
1338 if(!SDL_strcasecmp(Ships[obj->instance].ship_name, "alpha 1")){
1340 nprintf(("Network","In cone\n"));
1342 nprintf(("Network","Not in cone\n"));
1347 // store the dot product for this frame
1348 Interpolate_dot[ship_index] = eye_dot;
1350 // determine distance (near, medium, far)
1351 vm_vec_sub(&obj_dot,&obj->pos,&pobj->pos);
1352 dist = vm_vec_mag(&obj_dot);
1353 if(dist < OO_NEAR_DIST){
1355 } else if(dist < OO_MIDRANGE_DIST){
1356 range = OO_MIDRANGE;
1361 // reset the timestamp for the next update for this guy
1362 multi_oo_reset_timestamp(pl, obj, range, in_cone);
1367 // if the player is on "high" updates, add in extra physics info
1368 if((pl->p_info.options.obj_update_level == OBJ_UPDATE_HIGH) || (pl->p_info.options.obj_update_level == OBJ_UPDATE_LAN)){
1369 oo_flags |= OO_EXTRA_PHYSICS;
1372 // if the object's hull/shield timestamp has expired
1373 if((Ships[obj->instance].status_update_stamp[player_index] == -1) || timestamp_elapsed(Ships[obj->instance].status_update_stamp[player_index])){
1374 oo_flags |= (OO_HULL_NEW | OO_SHIELD_NEW);
1376 // reset the timestamp
1377 multi_oo_reset_status_timestamp(obj, player_index);
1380 // if the object's hull/shield timestamp has expired
1381 if((Ships[obj->instance].subsys_update_stamp[player_index] == -1) || timestamp_elapsed(Ships[obj->instance].subsys_update_stamp[player_index])){
1382 oo_flags |= OO_SUBSYSTEMS_NEW | OO_AI_MODE_NEW;
1384 // reset the timestamp
1385 multi_oo_reset_subsys_timestamp(obj, player_index);
1388 // add info for a targeted object)
1389 if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
1390 oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW | OO_SHIELD_NEW);
1394 // if the position didn't change, don't send anything
1395 oo_flags |= OO_POS_NEW;
1397 // add info which is contingent upon being "in front"
1399 oo_flags |= OO_ORIENT_NEW;
1403 // if the ship is a cruiser or capital ship, always send extra physics
1404 if(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_CRUISER | SIF_CAPITAL)){
1405 oo_flags |= OO_EXTRA_PHYSICS;
1408 // pack stuff only if we have to
1410 return multi_oo_pack_data(pl,obj,oo_flags,data);
1413 // don't do anything
1417 // didn't do anything
1421 // process all other objects for this player
1422 void multi_oo_process_all(net_player *pl)
1424 ubyte data[MAX_PACKET_SIZE];
1425 ubyte data_add[MAX_PACKET_SIZE];
1428 int packet_size = 0;
1434 // if the player has an invalid objnum..
1435 if(pl->player->objnum < 0){
1439 // get the player's object
1440 pobj = &Objects[pl->player->objnum];
1444 // build the list of ships to check against
1445 multi_oo_build_ship_list(pl);
1447 // do nothing if he has no object targeted, or if he has a weapon targeted
1448 if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1450 BUILD_HEADER(OBJECT_UPDATE);
1452 // add the sequencing #
1453 ADD_USHORT(Netgame.server_update_seq);
1454 ADD_INT(OO_global_time);
1456 // get a pointer to the object
1457 targ_obj = &Objects[pl->s_info.target_objnum];
1459 // run through the maybe_update function
1460 add_size = multi_oo_maybe_update(pl,pobj,targ_obj,data_add);
1462 // copy in any relevant data
1467 memcpy(data + packet_size, data_add, add_size);
1468 packet_size += add_size;
1471 // just build the header for the rest of the function
1472 BUILD_HEADER(OBJECT_UPDATE);
1474 // add the sequencing #
1475 ADD_USHORT(Netgame.server_update_seq);
1476 ADD_INT(OO_global_time);
1480 while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1481 // if this guy is over his datarate limit, do nothing
1482 if(multi_oo_rate_exceeded(pl)){
1483 nprintf(("Network","Capping client\n"));
1490 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1492 // maybe send some info
1493 add_size = multi_oo_maybe_update(pl,pobj,moveup,data_add);
1495 // if this data is too much for the packet, send off what we currently have and start over
1496 if(packet_size + add_size > OO_MAX_SIZE){
1500 multi_io_send(pl data, packet_size);
1501 pl->s_info.rate_bytes += packet_size;
1504 BUILD_HEADER(OBJECT_UPDATE);
1506 // add the sequencing #
1507 ADD_USHORT(Netgame.server_update_seq);
1508 ADD_INT(OO_global_time);
1516 memcpy(data + packet_size,data_add,add_size);
1517 packet_size += add_size;
1524 // if we have anything more than 3 byte in the packet, send the last one off
1525 if(packet_size > 3){
1529 multi_io_send(pl, data, packet_size);
1530 pl->s_info.rate_bytes += packet_size;
1534 // process all object update details for this frame
1535 void multi_oo_process()
1539 // increment sequencing #
1540 multi_oo_increment_seq();
1542 // process each player
1543 for(idx=0; idx<MAX_PLAYERS; idx++){
1544 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
1545 // now process the rest of the objects
1546 multi_oo_process_all(&Net_players[idx]);
1551 // process incoming object update data
1552 void multi_oo_process_update(ubyte *data, header *hinfo)
1554 ushort packet_seq, sequence_ref;
1557 int server_stamp,time_born = 0;
1558 int offset = HEADER_LENGTH;
1559 net_player *pl = NULL;
1561 // process sequencing info here
1562 GET_USHORT(packet_seq);
1563 GET_INT(server_stamp);
1565 // if this is processed on the server, its a client object update packet
1567 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1568 // determine what player this came from and determine sequencing
1569 player_index = find_player_id(hinfo->id);
1570 if(player_index != -1){
1572 if(((packet_seq - Net_players[player_index].client_cinfo_seq) < 0) && ((Net_players[player_index].client_cinfo_seq - packet_seq) > 2000)){
1573 Net_players[player_index].client_cinfo_seq = packet_seq;
1574 } else if(packet_seq > Net_players[player_index].client_cinfo_seq){
1575 Net_players[player_index].client_cinfo_seq = packet_seq;
1578 // set the sequence reference
1579 sequence_ref = Net_players[player_index].client_cinfo_seq;
1581 pl = &Net_players[player_index];
1583 sequence_ref = packet_seq;
1587 // otherwise its a "regular" object update packet
1589 // do time latency stuff
1590 //multi_tbuf_add_time(&OO_diff,server_stamp,timer_get_milliseconds());
1593 if(((packet_seq - Net_player->client_server_seq) < 0) && ((Net_player->client_server_seq - packet_seq) > 2000)){
1594 Net_player->client_server_seq = packet_seq;
1595 } else if(packet_seq > Net_player->client_server_seq){
1596 Net_player->client_server_seq = packet_seq;
1599 // set the sequence reference
1600 sequence_ref = Net_player->client_server_seq;
1602 pl = Netgame.server;
1607 while(stop == 0xff){
1609 offset += multi_oo_unpack_data(pl, data + offset, packet_seq, sequence_ref, time_born);
1616 // initialize all object update timestamps (call whenever entering gameplay state)
1617 void multi_oo_gameplay_init()
1623 split = 3000 / ship_get_num_ships();
1626 extern multi_oo_new_gameplay_init();
1627 multi_oo_new_gameplay_init();
1630 // server should setup initial update timestamps
1631 // stagger initial updates over 3 seconds or so
1633 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1634 if(Objects[so->objnum].type == OBJ_SHIP){
1635 shipp = &Ships[Objects[so->objnum].instance];
1637 // update the timestamps
1638 for(idx=0;idx<MAX_PLAYERS;idx++){
1639 shipp->pos_update_stamp[idx] = timestamp(cur);
1640 shipp->status_update_stamp[idx] = timestamp(cur + split);
1641 shipp->subsys_update_stamp[idx] = timestamp(cur + (split * 2));
1644 // increment the time
1647 objp = &Objects[so->objnum];
1648 shipp->int_cur.pos = objp->pos;
1649 shipp->int_cur.orient = objp->orient;
1650 shipp->int_cur.vel = objp->phys_info.vel;
1651 shipp->int_cur.desired_vel = objp->phys_info.desired_vel;
1652 shipp->int_cur.rotvel = objp->phys_info.rotvel;
1653 shipp->int_cur.desired_rotvel = objp->phys_info.desired_rotvel;
1654 memcpy(&shipp->int_last, &shipp->int_cur, sizeof(interp_info));
1657 Interpolate_dot[MAX_SHIPS] = -2.0f;
1661 // reset our data #'s
1664 OO_desired_vel_total = 0;
1665 OO_orient_total = 0;
1666 OO_rotvel_total = 0;
1667 OO_desired_rotvel_total = 0;
1669 OO_shield_total = 0;
1670 OO_single_subsys_total = 0;
1671 OO_subsys_total = 0;
1672 OO_forward_thrust_total = 0;
1676 // process an object update sync packet
1677 void multi_oo_process_update_sync(ubyte *data, header *hinfo)
1679 int offset = HEADER_LENGTH;
1681 // we're ignoring this value for now
1684 // initialize the time buffer
1685 //multi_tbuf_init(&OO_diff);
1688 // send an update sync packet
1689 void multi_oo_send_update_sync(net_player *pl)
1692 int packet_size = 0;
1694 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1696 // build the header and add the data
1697 BUILD_HEADER(OBJ_UPDATE_SYNC);
1701 multi_io_send_to_all_reliable(data, packet_size);
1703 multi_io_send_reliable(pl, data, packet_size);
1707 // initialize the server's time sync stuff
1708 void multi_oo_sync_init()
1710 // OO_sync_stamp = timestamp(OO_SYNC_TIME);
1713 // send control info for a client (which is basically a "reverse" object update)
1714 void multi_oo_send_control_info()
1716 ubyte data[MAX_PACKET_SIZE], stop;
1717 ubyte data_add[MAX_PACKET_SIZE];
1720 int packet_size = 0;
1722 // if I'm dying or my object type is not a ship, bail here
1723 if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1727 // increment sequencing
1728 multi_oo_increment_seq();
1731 BUILD_HEADER(OBJECT_UPDATE);
1733 // add the sequencing #
1734 ADD_USHORT(Netgame.server_update_seq);
1735 ADD_INT(OO_global_time);
1737 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW | OO_EXTRA_PHYSICS);
1739 // pack the appropriate info into the data
1740 add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1742 // copy in any relevant data
1747 memcpy(data + packet_size, data_add, add_size);
1748 packet_size += add_size;
1751 // add the final stop byte
1755 // send to the server
1756 if(Netgame.server != NULL){
1757 multi_io_send(Net_player, data, packet_size);
1761 // reset all sequencing info
1762 void multi_oo_reset_sequencing()
1766 // reset outgoing sequence #'s
1767 Netgame.server_update_frame_ref = 0;
1768 Netgame.server_update_seq = 0;
1770 // reset all incoming seqeunce #'s
1771 for(idx=0;idx<MAX_PLAYERS;idx++){
1772 Net_players[idx].client_cinfo_seq = 0;
1773 Net_players[idx].client_server_seq = 0;
1777 // display any oo info on the hud
1778 void multi_oo_display()
1781 np_update_record *r;
1784 if(!MULTIPLAYER_MASTER || MULTIPLAYER_STANDALONE){
1787 if(OO_update_index < 0){
1790 if(Net_players[OO_update_index].player == NULL){
1793 r = &OO_update_records[OO_update_index];
1796 gr_string(gr_screen.max_w - 150, sy, Net_players[OO_update_index].player->callsign);
1798 gr_printf(gr_screen.max_w - 150, sy, "pos : %d (%d/s)", r->pos_bytes, (int)r->pos_avg);
1800 gr_printf(gr_screen.max_w - 150, sy, "ori : %d (%d/s)", r->orient_bytes, (int)r->orient_avg);
1802 gr_printf(gr_screen.max_w - 150, sy, "hul : %d (%d/s)", r->hull_bytes, (int)r->hull_avg);
1804 gr_printf(gr_screen.max_w - 150, sy, "shi : %d (%d/s)", r->shield_bytes, (int)r->shield_avg);
1806 gr_printf(gr_screen.max_w - 150, sy, "aim : %d (%d/s)", r->ai_mode_bytes, (int)r->ai_mode_avg);
1808 gr_printf(gr_screen.max_w - 150, sy, "ext : %d (%d/s)", r->extra_physics_bytes, (int)r->extra_physics_avg);
1810 gr_printf(gr_screen.max_w - 150, sy, "fth : %d (%d/s)", r->fthrust_bytes, (int)r->fthrust_avg);
1812 gr_printf(gr_screen.max_w - 150, sy, "sub : %d (%d/s)", r->subsys_bytes, (int)r->subsys_avg);
1817 // ---------------------------------------------------------------------------------------------------
1818 // DATARATE DEFINES/VARS
1821 // low object update datarate limit
1822 #define OO_LIMIT_LOW 2000
1823 #define OO_LIMIT_MED 4000
1825 // timestamp for sending control info (movement only - we'll send button info all the time)
1826 #define OO_CIRATE 67 // 15x a second
1827 int Multi_cirate_stamp = -1; // timestamp for waiting on control info time
1828 int Multi_cirate_can_send = 1; // if we can send control info this frame
1831 int OO_server_rate = -1; // max _total_ bandwidth to send to all clients
1832 int OO_client_rate = -1; // max bandwidth to go to an individual client
1834 // update timestamp for server datarate checking
1835 #define RATE_UPDATE_TIME 1250 // in ms
1836 int OO_server_rate_stamp = -1;
1838 // process datarate limiting stuff for the server
1839 void multi_oo_server_process();
1841 // process datarate limiting stuff for the client
1842 void multi_oo_client_process();
1844 // update the server datarate
1845 void multi_oo_update_server_rate();
1848 // ---------------------------------------------------------------------------------------------------
1849 // DATARATE FUNCTIONS
1852 // process all object update datarate details
1853 void multi_oo_rate_process()
1855 // if I have no valid player, drop out here
1856 if(Net_player == NULL){
1860 // if we're not in mission, don't do anything
1861 if(!(Game_mode & GM_IN_MISSION)){
1865 // if I'm the server of a game, process server stuff
1866 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1867 multi_oo_server_process();
1869 // otherwise process client-side stuff
1871 multi_oo_client_process();
1875 // process datarate limiting stuff for the server
1876 void multi_oo_server_process()
1880 // go through all players
1881 for(idx=0;idx<MAX_PLAYERS;idx++){
1882 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1883 // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1884 if((Net_players[idx].s_info.rate_stamp == -1) || timestamp_elapsed(Net_players[idx].s_info.rate_stamp)){
1885 Net_players[idx].s_info.rate_stamp = timestamp(1000);
1886 Net_players[idx].s_info.rate_bytes = 0;
1891 // determine if we should be updating the server datarate
1892 if((OO_server_rate_stamp == -1) || timestamp_elapsed(OO_server_rate_stamp)){
1893 // reset the timestamp
1894 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1896 // update the server datarate
1897 multi_oo_update_server_rate();
1899 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1902 // see if we should be updating sync times
1904 if((OO_sync_stamp != -1) && timestamp_elapsed(OO_sync_stamp)){
1905 // send an update sync packet
1906 multi_oo_send_update_sync();
1908 // initialize the server's time sync stuff
1909 multi_oo_sync_init();
1914 // process datarate limiting stuff for the client
1915 void multi_oo_client_process()
1917 // if the timestamp is -1 or has elapsed, reset it
1918 if((Multi_cirate_stamp == -1) || timestamp_elapsed(Multi_cirate_stamp)){
1919 Multi_cirate_can_send = 1;
1920 Multi_cirate_stamp = timestamp(OO_CIRATE);
1923 // calculate what time difference should be added to all prediction calculations
1926 // server time plus current ping relative to "original" time
1927 abs( (OO_server_time + OO_my_orig_ping + (Netgame.server->s_info.ping.ping_avg - OO_my_orig_ping)) -
1929 // whatever my original time was at the "original" time
1936 // datarate limiting system for server -------------------------------------
1938 // initialize the rate limiting system for all players
1939 void multi_oo_rate_init_all()
1943 // if I don't have a net_player, bail here
1944 if(Net_player == NULL){
1948 // if I'm the server of the game
1949 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1950 // go through all players
1951 for(idx=0;idx<MAX_PLAYERS;idx++){
1952 if(MULTI_CONNECTED(Net_players[idx])){
1953 multi_oo_rate_init(&Net_players[idx]);
1957 OO_server_rate_stamp = -1;
1959 // if i'm the client, initialize my control info datarate stuff
1961 Multi_cirate_stamp = -1;
1962 Multi_cirate_can_send = 1;
1966 // initialize the rate limiting for the passed in player
1967 void multi_oo_rate_init(net_player *pl)
1969 // reinitialize his datarate timestamp
1970 pl->s_info.rate_stamp = -1;
1971 pl->s_info.rate_bytes = 0;
1974 // if the given net-player has exceeded his datarate limit
1975 int multi_oo_rate_exceeded(net_player *pl)
1979 // check against the guy's object update level
1980 switch(pl->p_info.options.obj_update_level){
1982 case OBJ_UPDATE_LOW:
1983 // the low object update limit
1984 rate_compare = OO_LIMIT_LOW;
1987 // medium update level
1988 case OBJ_UPDATE_MEDIUM:
1989 // the low object update limit
1990 rate_compare = OO_LIMIT_MED;
1993 // high update level - super high datarate
1994 case OBJ_UPDATE_HIGH:
1995 rate_compare = 100000000;
1998 // LAN - no rate max
1999 case OBJ_UPDATE_LAN:
2005 rate_compare = OO_LIMIT_LOW;
2009 // if the server global rate is actually lower
2010 if(OO_client_rate < rate_compare){
2011 rate_compare = OO_client_rate;
2014 // compare his bytes sent against the allowable amount
2015 if(pl->s_info.rate_bytes >= rate_compare){
2019 // we're allowed to send
2023 // if it is ok for me to send a control info (will be ~N times a second)
2024 int multi_oo_cirate_can_send()
2026 // if we're allowed to send
2027 if(Multi_cirate_can_send){
2028 Multi_cirate_can_send = 0;
2035 // dynamically update the server capped bandwidth rate
2036 void multi_oo_update_server_rate()
2038 int num_connections;
2041 if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2045 // get the # of connections
2046 num_connections = multi_num_connections();
2047 if(!(Game_mode & GM_STANDALONE_SERVER)){
2050 // make sure we always pretend there's at least one guy available
2051 if(num_connections <= 0){
2052 num_connections = 1;
2055 // set the data rate
2056 switch(Net_player->p_info.options.obj_update_level){
2058 case OBJ_UPDATE_LAN:
2059 // set to 0 so we don't limit anything
2060 OO_server_rate = Multi_options_g.datarate_cap;
2063 // high update level
2064 case OBJ_UPDATE_HIGH:
2065 // set to 0 so we don't limit anything
2066 OO_server_rate = Multi_options_g.datarate_cap;
2069 // medium update level
2070 case OBJ_UPDATE_MEDIUM:
2071 // set the rate to be "medium" update level
2072 OO_server_rate = OO_LIMIT_MED;
2076 case OBJ_UPDATE_LOW:
2077 // set the rate to be the "low" update level
2078 OO_server_rate = OO_LIMIT_LOW;
2086 // set the individual client level
2087 OO_client_rate = OO_server_rate / num_connections;
2088 // nprintf(("Network","Setting client rate to %d\n",OO_client_rate));
2092 #endif // #ifndef OO_NEW