]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_oo.cpp
Initial revision
[taylor/freespace2.git] / src / network / multi_oo.cpp
1 // #include "vd3drm.h"
2 #include "freespace.h"
3 #include "timer.h"
4 #include "linklist.h"
5 #include "weapon.h"
6 #include "multimsgs.h"
7 #include "multiutil.h"
8 #include "multi_oo.h"
9 #include "multi.h"
10 #include "object.h"
11 #include "key.h"
12 #include "gamesnd.h"
13
14 #ifndef OO_NEW
15
16 // ---------------------------------------------------------------------------------------------------
17 // OBJECT UPDATE DEFINES/VARS
18 //
19
20 // how much data we're willing to put into a given oo packet
21 #define OO_MAX_SIZE                                     480
22
23 // new improved - more compacted info type
24 #define OO_POS_NEW                                      (1<<0)          // 
25 #define OO_ORIENT_NEW                           (1<<1)          // 
26 #define OO_HULL_NEW                                     (1<<2)          // 
27 #define OO_SHIELD_NEW                           (1<<3)          // 
28 #define OO_AI_MODE_NEW                          (1<<4)          // 
29 #define OO_SUBSYSTEMS_NEW                       (1<<5)          // 
30 #define OO_EXTRA_PHYSICS                        (1<<6)          // means pos update will have desired velocity and orient update will have desired rotvel as well
31
32 #define OO_VIEW_CONE_DOT                        (0.1f)
33 #define OO_VIEW_DIFF_TOL                        (0.15f)                 // if the dotproducts differ this far between frames, he's coming into view
34
35 // distance class
36 #define OO_NEAR                                         0
37 #define OO_NEAR_DIST                                    (200.0f)
38 #define OO_MIDRANGE                                     1
39 #define OO_MIDRANGE_DIST                        (600.0f)
40 #define OO_FAR                                                  2
41 #define OO_FAR_DIST                                     (1200.0f)
42
43 // how often we should send full hull/shield updates
44 #define OO_HULL_SHIELD_TIME             600
45 #define OO_SUBSYS_TIME                          1000
46
47 // timestamp values for object update times based on client's update level.
48 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] = 
49 {
50         150,                            // low update -- do every 1 second
51         125,                            // medium update -- do every 1/2 second
52         100,                            // high update -- every frame
53         30,
54 };
55
56 // for near ships
57 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
58 {
59         250,                            // low update
60         150,                            // medium update
61         100,                            // high update
62         30,
63 };
64
65 // for medium ships
66 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
67 {
68         400,                            // low update
69         300,                            // medium update
70         150,                            // high update
71         30,
72 };
73
74 // for far ships
75 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
76 {
77         1100,                           // low update
78         500,                            // medium update
79         300,                            // high update
80         30,
81 };
82
83 // for near ships
84 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] = 
85 {
86         1500,                           // low update
87         1000,                           // medium update
88         300,                            // high update
89         30,
90 };
91
92 // for medium ships
93 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] = 
94 {
95         1900,                           // low update
96         1200,                           // medium update
97         400,                            // high update
98         30,
99 };
100
101 // for far ships
102 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] = 
103 {
104         3000,                           // low update
105         1600,                           // medium update
106         500,                            // high update
107         30,
108 };
109
110 // let's keep track of the breakdown of individual object update elements
111 int OO_pos_total = 0;
112 int OO_vel_total = 0;
113 int OO_desired_vel_total = 0;
114 int OO_orient_total = 0;
115 int OO_rotvel_total = 0;
116 int OO_desired_rotvel_total = 0;
117 int OO_hull_total = 0;
118 int OO_shield_total = 0;
119 int OO_single_subsys_total = 0;
120 int OO_subsys_total = 0;
121 int OO_forward_thrust_total = 0;
122
123 // keys for selectively disabling interpolation - these keys can be used in combination with each other
124 #define LAG_OFF_KEY                                                     KEY_7
125 #define LEVEL_1_OFF_KEY                                         KEY_8
126 #define LEVEL_2_OFF_KEY                                         KEY_9
127
128 int OO_global_time;
129
130 // ship index list for possibly sorting ships based upon distance, etc
131 short OO_ship_index[MAX_SHIPS];
132
133 int OO_debug_info = 0 ;
134 DCF(ood, "switch on and off targeted object update info")
135 {
136         OO_debug_info = !OO_debug_info;
137 }
138
139 // object update stats storing stuff
140 #define OO_POS_NEW                                      (1<<0)          // 
141 #define OO_ORIENT_NEW                           (1<<1)          // 
142 #define OO_HULL_NEW                                     (1<<2)          // 
143 #define OO_SHIELD_NEW                           (1<<3)          // 
144 #define OO_AI_MODE_NEW                          (1<<4)          // 
145 #define OO_SUBSYSTEMS_NEW                       (1<<5)          // 
146 #define OO_EXTRA_PHYSICS                        (1<<6)          // means pos update will have desired velocity and orient update will have desired rotvel as well
147
148 #define NUM_UPDATE_RECORDS                      5                                               // last five second average
149 typedef struct np_update_record {       
150         int pos_bytes;                                                                                          // how many pos bytes we've sent
151         int orient_bytes;                                                                                       // how many orient bytes we've sent
152         int hull_bytes;                                                                                 // how many hull bytes we've sent
153         int shield_bytes;                                                                                       // how many shield bytes we've sent
154         int ai_mode_bytes;                                                                              // how many ai_mode bytes we've sent
155         int extra_physics_bytes;                                                                // how many bytes of extra physics info we've sent      
156         int fthrust_bytes;                                                                              // how many bytes of forward thrust we've sent  
157         int subsys_bytes;                                                                                       // how many bytes of subsys data we've sent
158         
159         int pos_stamp;
160         int pos_bytes_frame;                                                                            // how many bytes we've sent in the last second
161         int pos_records[NUM_UPDATE_RECORDS];                            //      average of the last bunch of bytes we've sent
162         int pos_record_count;
163         float pos_avg;
164
165         int orient_stamp;
166         int orient_bytes_frame;                                                                 // how many bytes we've sent in the last second
167         int orient_records[NUM_UPDATE_RECORDS];                 //      average of the last bunch of bytes we've sent
168         int orient_record_count;
169         float orient_avg;
170
171         int hull_stamp;
172         int hull_bytes_frame;                                                                   // how many bytes we've sent in the last second
173         int hull_records[NUM_UPDATE_RECORDS];                           //      average of the last bunch of bytes we've sent
174         int hull_record_count;
175         float hull_avg;
176
177         int shield_stamp;
178         int shield_bytes_frame;                                                                 // how many bytes we've sent in the last second
179         int shield_records[NUM_UPDATE_RECORDS];                 //      average of the last bunch of bytes we've sent
180         int shield_record_count;
181         float shield_avg;
182
183         int ai_mode_stamp;
184         int ai_mode_bytes_frame;                                                                // how many bytes we've sent in the last second
185         int ai_mode_records[NUM_UPDATE_RECORDS];                        //      average of the last bunch of bytes we've sent
186         int ai_mode_record_count;
187         float ai_mode_avg;
188
189         int extra_physics_stamp;
190         int extra_physics_bytes_frame;                                          // how many bytes we've sent in the last second
191         int extra_physics_records[NUM_UPDATE_RECORDS];  //      average of the last bunch of bytes we've sent
192         int extra_physics_record_count;
193         float extra_physics_avg;
194
195         int fthrust_stamp;
196         int fthrust_bytes_frame;                                                                //      how many bytes we've sent in the last second
197         int fthrust_records[NUM_UPDATE_RECORDS];                        //      average of the last bunch of bytes we've sent
198         int fthrust_record_count;
199         float fthrust_avg;
200
201         int subsys_stamp;
202         int subsys_bytes_frame;                                                                 // how many bytes we've sent in the last second
203         int subsys_records[NUM_UPDATE_RECORDS];                 //      average of the last bunch of bytes we've sent
204         int subsys_record_count;
205         float subsys_avg;
206 } np_update_record;
207
208 #ifndef NDEBUG
209 #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);
210 #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);
211 #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);
212 #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);
213 #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);
214 #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);
215 #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);
216 #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);
217 #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);
218 np_update_record OO_update_records[MAX_PLAYERS];
219 #else 
220 #define R_POS_ADD(net_plr, byte_count)          
221 #define R_ORIENT_ADD(net_plr, byte_count)               
222 #define R_HULL_ADD(net_plr, byte_count)         
223 #define R_SHIELD_ADD(net_plr, byte_count)               
224 #define R_AI_MODE_ADD(net_plr, byte_count)              
225 #define R_EXTRA_PHYSICS_ADD(net_plr, byte_count)
226 #define R_FTHRUST_ADD(net_plr, byte_count)
227 #define R_SUBSYS_ADD(net_plr, byte_count)
228 #endif
229 int OO_update_index = -1;                                               // index into OO_update_records for displaying update record info
230
231 // ---------------------------------------------------------------------------------------------------
232 // OBJECT UPDATE FUNCTIONS
233 //
234
235 object *OO_player_obj;
236 int OO_sort = 1;
237
238 int multi_oo_sort_func(const void *ship1, const void *ship2)
239 {
240         object *obj1, *obj2;
241         short index1, index2;
242         float dist1, dist2;
243
244         // get the 2 indices
245         memcpy(&index1, ship1, sizeof(short));
246         memcpy(&index2, ship2, sizeof(short));
247
248         // if the indices are bogus, or the objnums are bogus, return ">"
249         if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
250                 return 1;
251         }
252
253         // get the 2 objects
254         obj1 = &Objects[Ships[index1].objnum];
255         obj2 = &Objects[Ships[index2].objnum];
256
257         // get the distance to the player obj for both
258         dist1 = vm_vec_dist_quick(&OO_player_obj->pos, &obj1->pos);
259         dist2 = vm_vec_dist_quick(&OO_player_obj->pos, &obj2->pos);
260
261         return (dist1 <= dist2) ? -1 : 1;
262 }
263
264 // build the list of ship indices to use when updating for this player
265 void multi_oo_build_ship_list(net_player *pl)
266 {
267         int ship_index;
268         int idx;
269         ship_obj *moveup;
270         object *player_obj;
271
272         // set all indices to be -1
273         for(idx = 0;idx<MAX_SHIPS; idx++){
274                 OO_ship_index[idx] = -1;
275         }
276
277         // get the player object
278         if(pl->player->objnum < 0){
279                 return;
280         }
281         player_obj = &Objects[pl->player->objnum];
282         
283         // go through all other relevant objects
284         ship_index = 0;
285         for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
286                 // if it is an invalid ship object, skip it
287                 if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
288                         continue;
289                 }
290
291                 // if we're a standalone server, don't send any data regarding its pseudo-ship
292                 if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
293                         continue;
294                 }               
295                         
296                 // must be a ship, a weapon, and _not_ an observer
297                 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
298                         continue;
299                 }
300
301                 // don't send info for dying ships
302                 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
303                         continue;
304                 }               
305                                 
306                 // don't send him info for himself
307                 if ( &Objects[moveup->objnum] == player_obj ){
308                         continue;
309                 }
310
311                 // don't send info for his targeted ship here, since its always done first
312                 if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
313                         continue;
314                 }
315
316                 // add the ship 
317                 if(ship_index < MAX_SHIPS){
318                         OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
319                 }
320         }
321
322         // maybe qsort the thing here
323         OO_player_obj = player_obj;
324         if(OO_sort){
325                 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
326         }
327 }
328
329 // set global object update timestamp for this frame
330 void multi_oo_set_global_timestamp()
331 {
332         OO_global_time = timer_get_milliseconds();
333 }
334
335
336 int Interpolated_orient_inited[MAX_SHIPS];
337 float Interpolate_dot[MAX_SHIPS];
338 matrix Interpolated_orient[MAX_SHIPS];
339
340 void multi_oo_interpolate_init()
341 {
342         int i;
343
344         for (i=0; i<MAX_SHIPS;i++ )     {
345                 Interpolated_orient_inited[i] = 0;
346         }
347 }
348
349 /*
350 void matrix_to_quaternion( D3DRMQUATERNION *quat, matrix *mat )
351 {
352         float theta;
353         vector rot_axis;
354         
355         vm_matrix_to_rot_axis_and_angle(mat, &theta, &rot_axis);
356
357         quat->v.x = rot_axis.x;
358         quat->v.y = rot_axis.y;
359         quat->v.z = rot_axis.z;
360         quat->s = theta;
361 }
362
363 void quaternion_to_matrix( matrix *mat, D3DRMQUATERNION *quat )
364 {
365         float theta;
366         vector rot_axis;
367
368         rot_axis.x = quat->v.x;
369         rot_axis.y = quat->v.y;
370         rot_axis.z = quat->v.z;
371         theta = quat->s;
372
373         vm_quaternion_rotate(mat, theta, &rot_axis);
374         vm_orthogonalize_matrix(mat);
375 }
376 */
377
378 // interpolate for this object
379 void multi_oo_interpolate(object *objp, interp_info *current, interp_info *last)
380 {
381 #ifdef OO_NEW   
382 #else 
383         player *pp;
384         int pnum;
385
386         // save position and orientation
387         objp->last_pos = objp->pos;
388         objp->last_orient = objp->orient;       
389
390         // move all pre ?
391         obj_move_all_pre(objp, flFrametime);
392
393         // level 1 interpolation
394         // if the key disabling level 1 interpolation is pressed, skip
395         if(!keyd_pressed[LEVEL_1_OFF_KEY]){
396                 float lag = fl2i(current->lowest_ping)/1000.0f;
397
398                 lag /= 2.0f;            // Our ping time is round trip, we only account for 1/2 the trip.
399
400                 // Correct bogus lags
401                 if ( lag > 0.5f )       {
402                         lag = 0.5f;
403                 }
404
405                 // test - should behave like the old way of doing stuff         
406                 if (current->vel_time == OO_global_time){
407                         objp->phys_info.vel = current->vel;
408                 }
409                 if (current->rotvel_time == OO_global_time){
410                         objp->phys_info.rotvel = current->rotvel;
411                 }
412
413                 objp->phys_info.desired_vel = current->desired_vel;
414                 objp->phys_info.desired_rotvel = current->desired_rotvel;               
415
416 //              if ( !stricmp( Ships[objp->instance].ship_name, "alpha 1"))     {
417 //                      mprintf(( "Rotvel = %.3f, %.3f, %.3f\n", current->rotvel.x, current->rotvel.y, current->rotvel.z ));
418 //              }
419
420                 if ( !Interpolated_orient_inited[objp->instance] )      {
421                         Interpolated_orient[objp->instance] = objp->orient;
422                         Interpolated_orient_inited[objp->instance] = 1;
423                 }
424
425                 matrix *actual_orient = &Interpolated_orient[objp->instance];
426                 
427                 // if this is from a network update
428                 if(current->orient_time == OO_global_time){
429                         *actual_orient = current->orient;       
430                         if ( lag > 0.0f )       {
431                                 //adjust for lag
432                                 physics_sim_rot(actual_orient, &objp->phys_info, lag );
433                         } else {
434                                 physics_sim_rot(actual_orient, &objp->phys_info, flFrametime );
435                         }
436                 }
437                 // otherwise, if between network updates
438                 else {
439                         physics_sim_rot(actual_orient, &objp->phys_info, flFrametime );
440                 }
441                 
442                 if(keyd_pressed[LAG_OFF_KEY]){
443                         // Make orient go quickly to actual_orient
444
445                         /*
446                         matrix tmp;
447                         vector w_out;
448                         vector angular_accel;
449
450                         angular_accel.x  = 1000.0f;
451                         angular_accel.y  = 1000.0f;
452                         angular_accel.z  = 1000.0f;
453                         vm_matrix_interpolate(actual_orient, &objp->orient, &objp->phys_info.rotvel, flFrametime, &tmp,
454                                                                         &w_out, &objp->phys_info.max_rotvel, &angular_accel, 1 );
455
456                         objp->phys_info.rotvel = w_out; 
457                         objp->orient = tmp; 
458                         */
459
460                         // 
461                         /*
462                         D3DRMQUATERNION in1, in2, out;
463
464                         matrix_to_quaternion( &in1, &objp->orient );
465                         matrix_to_quaternion( &in2, actual_orient );
466
467                         D3DRMQuaternionSlerp( &out, &in1, &in2, 0.50f );
468
469                         quaternion_to_matrix( &objp->orient, &out );
470                         */
471                         objp->orient = *actual_orient;
472                 } else {
473                         objp->orient = *actual_orient;
474                 }               
475                 
476                 if(current->pos_time == OO_global_time){
477                         objp->pos = current->pos;
478                         if ( lag > 0.0f )       {
479                                 //adjust for lag
480                                 physics_sim_vel(&objp->pos, &objp->phys_info, lag, &objp->orient );
481                         } else {
482                                 physics_sim_vel(&objp->pos, &objp->phys_info, flFrametime, &objp->orient );
483                         }
484                 } else {
485                         physics_sim_vel(&objp->pos, &objp->phys_info, flFrametime, &objp->orient );
486                 }
487
488
489                 objp->phys_info.speed = vm_vec_mag(&objp->phys_info.vel);                                                       //      Note, cannot use quick version, causes cumulative error, increasing speed.
490                 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
491         }
492         // no interpolation, so bash
493         else {
494                 objp->orient = current->orient; 
495                 objp->pos = current->pos;
496         }
497
498         // copy the "current info" to the "last" info
499         memcpy(last, current, sizeof(interp_info));
500
501         // move all post ?
502         obj_move_all_post(objp, flFrametime);
503
504         // if I'm the server of the game, do firing stuff here
505         if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (objp->flags & OF_PLAYER_SHIP)){
506                 pnum = multi_find_player_by_object( objp );
507                 if ( pnum != -1 ) {
508                         pp = Net_players[pnum].player;
509                         obj_player_fire_stuff( objp, pp->ci );
510                 }
511         }
512 #endif
513 }
514
515 // do all interpolation for this frame
516 void multi_oo_interpolate_all()
517 {
518         object *objp;
519         ship *shipp;
520         ship_obj *so;           
521         
522         // for the server, this means "interpolate all player ships"
523         // for the client, this means "interpolate all ships"
524
525         // iterate over all _ships_
526         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
527                 // get the ships object
528                 objp = &Objects[so->objnum];
529                 shipp = &Ships[objp->instance];
530         
531                 // ignore dead or dying objects
532                 if ( !(objp->flags&OF_SHOULD_BE_DEAD) ) {                                                                                       
533                         // multiplayer servers should only do the interpolation for player ships                        
534                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
535                                 if((objp->type != OBJ_SHIP) || !(objp->flags & OF_PLAYER_SHIP) || (objp == Player_obj)){
536                                         continue;
537                                 }
538                         } else {
539                                 if((objp->type != OBJ_SHIP) || (objp == Player_obj)){
540                                         continue;
541                                 }
542                         }                                               
543
544                         // INTERPOLATE HERE
545                         multi_oo_interpolate(objp, &shipp->int_cur, &shipp->int_last);                                                                  
546                 }                                       
547         }
548 }
549
550 // handle incoming new data for the given object
551 void multi_oo_handle_new_data(ship *shipp, object *obj_data, ubyte oo_flags, net_player *pl)
552 {               
553         // fill in the int_current value for the passed ship
554         
555         // position type update
556         if(oo_flags & OO_POS_NEW){
557                 // copy in position for now
558                 shipp->int_cur.pos = obj_data->pos;
559                 shipp->int_cur.pos_time = OO_global_time;
560                 
561                 // copy in velocity
562                 shipp->int_cur.vel = obj_data->phys_info.vel;
563                 shipp->int_cur.vel_time = OO_global_time;
564
565 /*
566                 if(oo_flags & OO_EXTRA_PHYSICS){
567                         // copy in desired velocity
568                         shipp->int_cur.desired_vel = obj_data->phys_info.desired_vel;
569                         shipp->int_cur.desired_vel_time = OO_global_time;
570                 } else {
571                 */
572                         shipp->int_cur.desired_vel = obj_data->phys_info.vel;
573                         shipp->int_cur.desired_vel_time = OO_global_time;
574                 // }
575         }
576
577         // orientation type update
578         if(oo_flags & OO_ORIENT_NEW){
579                 // copy in orientation for now
580                 shipp->int_cur.orient = obj_data->orient;
581                 shipp->int_cur.orient_time = OO_global_time;
582
583                 // copy in velocity
584                 shipp->int_cur.rotvel = obj_data->phys_info.rotvel;
585                 shipp->int_cur.rotvel_time = OO_global_time;
586
587                 if(oo_flags & OO_EXTRA_PHYSICS){
588                         // copy in desired velocity
589                         shipp->int_cur.desired_rotvel = obj_data->phys_info.desired_rotvel;
590                         shipp->int_cur.desired_rotvel_time = OO_global_time;
591                 } else {
592                         shipp->int_cur.desired_rotvel = obj_data->phys_info.rotvel;
593                         shipp->int_cur.desired_rotvel_time = OO_global_time;
594                 }
595         }
596
597         // set the ping values
598         shipp->int_cur.lowest_ping = multi_ping_get_lowest(&pl->s_info.ping);
599         shipp->int_cur.lowest_ping_avg = multi_ping_lowest_avg(&pl->s_info.ping);
600 }
601
602 // increment the packet sequence # (used for object updates from the server, as well as "control info"
603 // update from clients
604 void multi_oo_increment_seq()
605 {
606         // increment the sequencing reference count for this frame if necessary
607         if(Netgame.server_update_frame_ref != Framecount){
608                 Netgame.server_update_frame_ref = Framecount;
609
610                 if(Netgame.server_update_seq == 0xffff){
611                         Netgame.server_update_seq = 0;
612                 } else {
613                         Netgame.server_update_seq++;
614                 }
615         }
616 }
617
618 // pack information for a client (myself), return bytes added
619 int multi_oo_pack_client_data(ubyte *data)
620 {
621         ubyte out_flags,ret;
622         ushort tnet_signature;
623         char t_subsys, l_subsys;
624         int packet_size = 0;
625
626         // get our firing stuff
627         out_flags = Net_player->s_info.accum_buttons;   
628
629         // zero these values for now
630         Net_player->s_info.accum_buttons = 0;
631
632         // add any necessary targeting flags
633         if ( Player_ai->current_target_is_locked ){
634                 out_flags |= OOC_TARGET_LOCKED;
635         }
636         if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){     
637                 out_flags |= OOC_TARGET_SEEK_LOCK;
638         }
639         if ( Player->locking_on_center ){
640                 out_flags |= OOC_LOCKING_ON_CENTER;
641         }
642
643         // copy the final flags in
644         memcpy(data, &out_flags, sizeof(ubyte));
645         packet_size++;
646         
647         // client eye information       
648         ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size, &Net_player->s_info.eye_pos );
649         Assert(ret == OO_POS_RET_SIZE);
650         packet_size += ret;
651
652         ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size, &Net_player->s_info.eye_orient );
653         Assert(ret == OO_ORIENT_RET_SIZE);
654         packet_size += ret;     
655
656         // client targeting information 
657         t_subsys = -1;
658         l_subsys = -1;
659
660         // if nothing targeted
661         if(Player_ai->target_objnum == -1){
662                 tnet_signature = 0;
663         }
664         // if something is targeted 
665         else {
666                 // target net signature
667                 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
668                         
669                 // targeted subsys index
670                 if(Player_ai->targeted_subsys != NULL){
671                         t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
672                 }
673
674                 // locked targeted subsys index
675                 if(Player->locking_subsys != NULL){
676                         l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
677                 }
678         }
679
680         // add them all
681         memcpy(data + packet_size, &tnet_signature, sizeof(ushort));
682         packet_size += sizeof(ushort);
683         memcpy(data + packet_size, &t_subsys, sizeof(char));
684         packet_size += sizeof(char);
685         memcpy(data + packet_size, &l_subsys, sizeof(char));
686         packet_size += sizeof(char);            
687
688         return packet_size;
689 }
690
691 // pack the appropriate info into the data
692 #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++; }
693 int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
694 {       
695         ubyte data[255];
696         ubyte data_size;        
697         char percent;
698         ship *shipp;    
699         ubyte ret;
700         float temp;     
701         int packet_size = 0;
702
703         int header_bytes;
704
705         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
706                 header_bytes = 4;
707         } else {
708                 header_bytes = 2;
709         }
710
711         Assert(objp->type == OBJ_SHIP);
712         if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
713                 shipp = &Ships[objp->instance];
714         } else {
715                 return 0;
716         }               
717
718         // if we're a client (and therefore sending control info), send button info first
719         if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
720                 packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);            
721         }
722
723         // orientation  
724         if(oo_flags & OO_ORIENT_NEW){
725                 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
726                 Assert(ret == OO_ORIENT_RET_SIZE);
727                 packet_size += ret;
728                 R_ORIENT_ADD(pl, ret);
729
730                 // global records
731                 OO_orient_total += ret;
732
733                 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
734                 packet_size += ret;     
735
736                 // global records
737                 OO_rotvel_total += ret;
738                 R_ORIENT_ADD(pl, ret);
739
740                 if(oo_flags & OO_EXTRA_PHYSICS){
741                         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]);
742                         packet_size += ret;
743
744                         // global records
745                         OO_desired_rotvel_total += ret;                                         
746                         R_EXTRA_PHYSICS_ADD(pl, ret);
747                 }
748         }
749
750         // position, velocity
751         if ( oo_flags & OO_POS_NEW ) {          
752                 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
753                 packet_size += ret;
754                 
755                 // global records
756                 OO_pos_total += ret;
757                 R_POS_ADD(pl, ret);
758                 
759                 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
760                 packet_size += ret;             
761                 
762                 // global records
763                 OO_vel_total += ret;            
764                 R_POS_ADD(pl, ret);
765
766                 /*
767                 if(oo_flags & OO_EXTRA_PHYSICS){
768                         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]);
769                         packet_size += ret;             
770                 
771                         // global records
772                         OO_vel_total += ret;    
773                 }
774                 */
775         }       
776                 
777         // forward thrust       
778         percent = (char)(objp->phys_info.forward_thrust * 100.0f);
779         Assert( percent <= 100 );
780
781         memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
782         packet_size += 1;
783
784         // global records
785         OO_forward_thrust_total++;      
786         R_FTHRUST_ADD(pl, 1);
787
788         // hull info
789         if ( oo_flags & OO_HULL_NEW ){
790                 // add the hull value for this guy              
791                 temp = (objp->hull_strength  / Ship_info[shipp->ship_info_index].initial_hull_strength);                
792                 PACK_PERCENT(temp);             
793                 R_HULL_ADD(pl, 1);
794                                 
795                 // global records
796                 OO_hull_total++;
797         }
798
799         // shield info
800         if( oo_flags & OO_SHIELD_NEW ){
801                 // pack 2 shield values into each byte
802
803                 // pack quadrant 1
804                 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
805                 PACK_PERCENT(temp);
806                                 
807                 // pack quadrant 2
808                 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
809                 PACK_PERCENT(temp);                             
810
811                 // pack quadrant 3
812                 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
813                 PACK_PERCENT(temp);
814                                 
815                 // pack quadrant 2
816                 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));                          
817                 PACK_PERCENT(temp);                             
818
819                 OO_shield_total += 4;
820                 R_SHIELD_ADD(pl, 4);
821         }       
822
823         // subsystem info
824         if( oo_flags & OO_SUBSYSTEMS_NEW ){
825                 ubyte ns;               
826                 ship_subsys *subsysp;
827                                 
828                 // just in case we have some kind of invalid data (should've been taken care of earlier in this function)
829                 if(shipp->ship_info_index < 0){
830                         ns = 0;
831                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
832                         packet_size++;
833                         R_SUBSYS_ADD(pl, 1);
834                 }
835                 // add the # of subsystems, and their data
836                 else {
837                         ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
838                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
839                         packet_size++;
840                         R_SUBSYS_ADD(pl, 1);
841
842                         // now the subsystems.
843                         for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
844                                 temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits;
845                                 PACK_PERCENT(temp);
846                                 R_SUBSYS_ADD(pl, 1);
847                         }
848                 }
849         }
850
851         // ai mode info
852         if( oo_flags & OO_AI_MODE_NEW){
853                 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
854                 short submode = (short)(Ai_info[shipp->ai_index].submode);
855                 ushort target_signature;
856
857                 target_signature = 0;
858                 if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
859                         target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
860                 }
861
862                 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
863                 packet_size++;
864                 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
865                 packet_size += 2;
866                 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));           
867                 packet_size += 2;               
868
869                 R_AI_MODE_ADD(pl, 5);
870         }
871
872         Assert(packet_size < 255);
873         data_size = (ubyte)packet_size;
874
875         // add the object's net signature, type and oo_flags
876         packet_size = 0;
877         // don't add for clients
878         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
879                 ADD_DATA( objp->net_signature );        
880         }
881         ADD_DATA( oo_flags );
882         ADD_DATA( data_size );  
883         packet_size += data_size;
884
885         memcpy(data_out,data,packet_size);
886         
887         return packet_size;     
888 }
889
890 // unpack information for a client , return bytes processed
891 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
892 {
893         ubyte in_flags;
894         ship *shipp;
895         int ret;
896         int offset = 0;
897         
898         memcpy(&in_flags, data, sizeof(ubyte)); 
899         offset++;
900                 
901         // if we have a valid netplayer pointer
902         if(pl != NULL){
903                 // primary fired
904                 pl->player->ci.fire_primary_count = 0;
905                 // if ( in_flags & OOC_FIRE_PRIMARY && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
906                 if ( in_flags & OOC_FIRE_PRIMARY){
907                         pl->player->ci.fire_primary_count = 1;
908                 }       
909
910                 // secondary fired
911                 pl->player->ci.fire_secondary_count = 0;
912                 if ( in_flags & OOC_FIRE_SECONDARY ){
913                         pl->player->ci.fire_secondary_count = 1;
914                 }
915
916                 // countermeasure fired
917                 pl->player->ci.fire_countermeasure_count = 0;
918                 // if ( in_flags & OOC_FIRE_COUNTERMEASURE && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
919                 if ( in_flags & OOC_FIRE_COUNTERMEASURE ){
920                         pl->player->ci.fire_countermeasure_count = 1;
921                 }
922
923                 // set up aspect locking information
924                 pl->player->locking_on_center = 0;
925                 if ( in_flags & OOC_LOCKING_ON_CENTER ){
926                         pl->player->locking_on_center = 1;
927                 }
928
929                 // other locking information
930                 if((pl->player->objnum != -1) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Ships[Objects[pl->player->objnum].instance].ai_index != -1)){
931                         shipp = &Ships[Objects[pl->player->objnum].instance];
932
933                         Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
934                         if      ( in_flags & OOC_TARGET_SEEK_LOCK ) {
935                                 Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
936                         } else {
937                                 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
938                         }
939                 }
940         }
941
942         // client eye information
943         vector eye_pos;
944         matrix eye_orient;
945         physics_info pi;
946
947         // unpack the stuff
948         memset(&pi,0,sizeof(physics_info));
949
950         ret = multi_pack_unpack_position( 0, data + offset, &eye_pos );
951         Assert(ret == OO_POS_RET_SIZE);
952         offset += ret;
953
954         ret = multi_pack_unpack_orient( 0, data + offset, &eye_orient );
955         Assert(ret == OO_ORIENT_RET_SIZE);
956         offset += ret;
957
958         // if we have a valid player, copy the info in
959         if(pl != NULL){
960                 pl->s_info.eye_pos = eye_pos;
961                 pl->s_info.eye_orient = eye_orient;
962         }       
963
964         // client targeting information 
965         ushort tnet_sig;
966         char t_subsys,l_subsys;
967         object *tobj;
968
969         // get the data
970         GET_DATA(tnet_sig);
971         GET_DATA(t_subsys);
972         GET_DATA(l_subsys);
973
974         // try and find the targeted object
975         tobj = NULL;
976         if(tnet_sig != 0){
977                 tobj = multi_get_network_object( tnet_sig );
978         }
979         // maybe fill in targeted object values
980         if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){
981                 // assign the target object
982                 if(Objects[pl->player->objnum].type == OBJ_SHIP){
983                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
984                 }
985                 pl->s_info.target_objnum = OBJ_INDEX(tobj);
986
987                 // assign subsystems if possible                                        
988                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
989                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL;
990                         if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
991                                 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
992                         }
993                 }
994
995                 pl->player->locking_subsys = NULL;
996                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
997                         if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
998                                 pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
999                         }                               
1000                 }
1001         }                               
1002
1003         return offset;
1004 }
1005
1006 // unpack the object data, return bytes processed
1007 #define UNPACK_PERCENT(v)                                       { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
1008 int multi_oo_unpack_data(net_player *pl, ubyte *data, ushort packet_sequence_num, ushort packet_sequence_ref, int time_born)
1009 {       
1010         int offset = 0;         
1011         object *objp,obj_fill,*pobjp;
1012         ushort net_sig = 0;
1013         ubyte data_size, oo_flags;
1014         char percent;   
1015         float fpct;
1016         ship *shipp;
1017
1018         // add the object's net signature, type and oo_flags
1019         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1020                 GET_DATA( net_sig );    
1021         }
1022         GET_DATA( oo_flags );   
1023         GET_DATA( data_size );  
1024
1025         // try and find the object
1026         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1027                 pobjp = multi_get_network_object(net_sig);      
1028         } else {        
1029                 if((pl != NULL) && (pl->player->objnum != -1)){
1030                         pobjp = &Objects[pl->player->objnum];
1031                 } else {
1032                         pobjp = NULL;
1033                 }
1034         }
1035         
1036         // if we can't find the object, set pointer to bogus object to continue reading the data
1037         // ignore out of sequence packets here as well
1038         if ( (pobjp == NULL) || (pobjp->type != OBJ_SHIP) || (pobjp->instance < 0) || (Ships[pobjp->instance].ship_info_index < 0) || (packet_sequence_num < packet_sequence_ref)){
1039                 if(packet_sequence_num < packet_sequence_ref){
1040                         nprintf(("Network","Tossing out of order packet!!\n"));
1041                 }
1042
1043                 offset += data_size;
1044
1045                 return offset;
1046         }               
1047
1048         // ship pointer
1049         shipp = &Ships[pobjp->instance];
1050
1051         // use the "fill" object
1052         objp = &obj_fill;
1053         memset(objp,0,sizeof(object));
1054         objp->type = OBJ_SHIP;
1055         objp->phys_info.max_vel = pobjp->phys_info.max_vel;
1056         objp->phys_info.max_rotvel = pobjp->phys_info.max_rotvel;
1057         objp->phys_info.afterburner_max_vel = pobjp->phys_info.afterburner_max_vel;
1058         objp->orient = pobjp->orient;
1059         objp->instance = pobjp->instance;
1060
1061         // if this is from a player, read his button info
1062         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1063                 offset += multi_oo_unpack_client_data(pl, data + offset);               
1064         }
1065
1066         // read in the data
1067
1068         // orientation  
1069         if ( oo_flags & OO_ORIENT_NEW ) {               
1070                 int r2 = multi_pack_unpack_orient( 0, data + offset, &objp->orient );                           
1071                 Assert(fl_abs(objp->orient.fvec.x) < 10000.0f);
1072                 Assert(fl_abs(objp->orient.fvec.y) < 10000.0f);
1073                 Assert(fl_abs(objp->orient.fvec.z) < 10000.0f);         
1074                 Assert(fl_abs(objp->orient.uvec.x) < 10000.0f);
1075                 Assert(fl_abs(objp->orient.uvec.y) < 10000.0f);
1076                 Assert(fl_abs(objp->orient.uvec.z) < 10000.0f);         
1077                 Assert(fl_abs(objp->orient.rvec.x) < 10000.0f);
1078                 Assert(fl_abs(objp->orient.rvec.y) < 10000.0f);
1079                 Assert(fl_abs(objp->orient.rvec.z) < 10000.0f);         
1080                 offset += r2;           
1081
1082                 int r5 = multi_pack_unpack_rotvel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info );             
1083                 offset += r5;           
1084
1085                 if(oo_flags & OO_EXTRA_PHYSICS){
1086                         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] );
1087                         offset += r6;   
1088                 }
1089         } 
1090
1091         // position
1092         if ( oo_flags & OO_POS_NEW ) {          
1093                 int r1 = multi_pack_unpack_position( 0, data + offset, &objp->pos );
1094                 offset += r1;                           
1095
1096                 int r3 = multi_pack_unpack_vel( 0, data + offset, &objp->orient, &objp->pos, &objp->phys_info );
1097                 offset += r3;                   
1098
1099                 /*
1100                 if(oo_flags & OO_EXTRA_PHYSICS){
1101                         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] );
1102                         offset += r4;
1103                 }
1104                 */
1105         }       
1106                 
1107         // forward thrust       
1108         percent = (char)(objp->phys_info.forward_thrust * 100.0f);
1109         Assert( percent <= 100 );
1110         GET_DATA(percent);      
1111         
1112         // hull info
1113         if ( oo_flags & OO_HULL_NEW ){
1114                 UNPACK_PERCENT(fpct);
1115                 pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength;
1116
1117                 // TEST code
1118 #ifndef NDEBUG          
1119                 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1120                         nprintf(("Network", "HULL UPDATE : %d, %s\n", pobjp->hull_strength, Ships[pobjp->instance].ship_name));
1121                 }               
1122 #endif
1123         }
1124
1125         // shield info
1126         if ( oo_flags & OO_SHIELD_NEW ){
1127                 float shield_0, shield_1, shield_2, shield_3;
1128                 
1129                 // unpack the 4 quadrants
1130                 UNPACK_PERCENT(shield_0);
1131                 UNPACK_PERCENT(shield_1);
1132                 UNPACK_PERCENT(shield_2);
1133                 UNPACK_PERCENT(shield_3);
1134
1135                 pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;                               
1136                 pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;                               
1137                 pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;                               
1138                 pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;                               
1139
1140                 // TEST code
1141 #ifndef NDEBUG
1142                 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1143                         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));
1144                 }
1145 #endif
1146         }       
1147
1148         if ( oo_flags & OO_SUBSYSTEMS_NEW ) {
1149                 ubyte n_subsystems, subsys_count;
1150                 float subsystem_percent[MAX_MODEL_SUBSYSTEMS];          
1151                 ship_subsys *subsysp;           
1152                 float val;              
1153                 int i;          
1154
1155                 // get the data for the subsystems
1156                 GET_DATA( n_subsystems );
1157                 for ( i = 0; i < n_subsystems; i++ ){
1158                         UNPACK_PERCENT( subsystem_percent[i] );
1159                 }               
1160                 
1161                 // fill in the subsystem data
1162                 subsys_count = 0;
1163                 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
1164                         int subsys_type;
1165
1166                         val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
1167                         subsysp->current_hits = val;
1168
1169                         // add the value just generated (it was zero'ed above) into the array of generic system types
1170                         subsys_type = subsysp->system_info->type;                                       // this is the generic type of subsystem
1171                         Assert ( subsys_type < SUBSYSTEM_MAX );
1172                         shipp->subsys_info[subsys_type].current_hits += val;
1173                         subsys_count++;
1174
1175                         // if we've reached max subsystems for some reason, bail out
1176                         if(subsys_count >= n_subsystems){
1177                                 break;
1178                         }
1179                 }
1180                 
1181                 // recalculate all ship subsystems
1182                 ship_recalc_subsys_strength( shipp );   
1183
1184                 // TEST code
1185 #ifndef NDEBUG
1186                 if(OO_debug_info && (Player_ai != NULL) && (pobjp != NULL) && (pobjp->instance >= 0) && (Player_ai->target_objnum == OBJ_INDEX(pobjp))){
1187                         nprintf(("Network", "SUBSYS UPDATE : %s\n", Ships[pobjp->instance].ship_name));
1188                 }
1189 #endif
1190         }
1191
1192         if ( oo_flags & OO_AI_MODE_NEW ) {
1193                 ubyte umode;
1194                 short submode;
1195                 ushort target_signature;
1196                 object *target_objp;
1197
1198                 GET_DATA(umode);
1199                 GET_DATA(submode);
1200                 GET_DATA( target_signature );           
1201
1202                 if(shipp->ai_index > 0){
1203                         Ai_info[shipp->ai_index].mode = umode;
1204                         Ai_info[shipp->ai_index].submode = submode;             
1205
1206                         // set this guys target objnum
1207                         target_objp = multi_get_network_object( target_signature );
1208                         if ( target_objp == NULL ){
1209                                 Ai_info[shipp->ai_index].target_objnum = -1;
1210                         } else {
1211                                 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
1212                         }
1213                 }
1214         }
1215                 
1216         // handle this new data 
1217         if(pl != NULL){
1218                 multi_oo_handle_new_data(&Ships[pobjp->instance], objp, oo_flags, pl);  
1219         }
1220         
1221         return offset;
1222 }
1223
1224 // reset the timestamp appropriately for the passed in object
1225 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1226 {
1227         int stamp = 0;
1228         int player_index = NET_PLAYER_INDEX(pl);
1229
1230         // if this is the guy's target, 
1231         if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1232                 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1233         } else {
1234                 // reset the timestamp appropriately
1235                 if(in_cone){
1236                         // base it upon range
1237                         switch(range){
1238                         case OO_NEAR:
1239                                 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1240                                 break;
1241
1242                         case OO_MIDRANGE:
1243                                 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1244                                 break;
1245
1246                         case OO_FAR:
1247                                 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1248                                 break;
1249                         }
1250                 } else {
1251                         // base it upon range
1252                         switch(range){
1253                         case OO_NEAR:
1254                                 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1255                                 break;
1256
1257                         case OO_MIDRANGE:
1258                                 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1259                                 break;
1260
1261                         case OO_FAR:
1262                                 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1263                                 break;
1264                         }
1265                 }                                               
1266         }
1267
1268         // reset the timestamp for this object
1269         if(objp->type == OBJ_SHIP){
1270                 Ships[objp->instance].pos_update_stamp[player_index] = timestamp(stamp);
1271         } 
1272 }
1273
1274 // reset the timestamp appropriately for the passed in object
1275 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1276 {
1277         Ships[objp->instance].status_update_stamp[player_index] = timestamp(OO_HULL_SHIELD_TIME);
1278 }
1279
1280 // reset the timestamp appropriately for the passed in object
1281 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1282 {
1283         Ships[objp->instance].subsys_update_stamp[player_index] = timestamp(OO_SUBSYS_TIME);
1284 }
1285
1286 // determine what needs to get sent for this player regarding the passed object, and when
1287 int multi_oo_maybe_update(net_player *pl,object *pobj,object *obj,ubyte *data)
1288 {
1289         ubyte oo_flags;
1290         int stamp;
1291         int player_index;
1292         vector player_eye;
1293         vector obj_dot;
1294         float eye_dot, dist;
1295         int in_cone;
1296         int range;
1297         int ship_index;
1298
1299         // if the timestamp has elapsed for this guy, send stuff
1300         player_index = NET_PLAYER_INDEX(pl);
1301         if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1302                 return 0;
1303         }
1304
1305         // determine what the timestamp is for this object
1306         if(obj->type == OBJ_SHIP){
1307                 stamp = Ships[obj->instance].pos_update_stamp[player_index];
1308                 ship_index = &Ships[obj->instance] - Ships;
1309         } else {
1310                 return 0;
1311         }
1312         
1313         // if we're supposed to update this guy
1314         if((stamp == -1) || timestamp_elapsed(stamp)){
1315                 // check dot products           
1316                 player_eye = pl->s_info.eye_orient.fvec;
1317                 vm_vec_sub(&obj_dot,&obj->pos,&pobj->pos);
1318                 vm_vec_normalize(&obj_dot);
1319                 eye_dot = vm_vec_dot(&obj_dot,&player_eye);             
1320                 in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;                
1321                 
1322                 // if he's in view or is "swinging into view", send info for him
1323                 if(Interpolate_dot[ship_index] >= -1.0f){
1324                         if((eye_dot - Interpolate_dot[ship_index]) >= OO_VIEW_DIFF_TOL){
1325                                 in_cone = 1;
1326                         }
1327                 }
1328
1329                 /*
1330                 if(!stricmp(Ships[obj->instance].ship_name, "alpha 1")){
1331                         if(in_cone){
1332                                 nprintf(("Network","In cone\n"));
1333                         } else {
1334                                 nprintf(("Network","Not in cone\n"));
1335                         }
1336                 }
1337                 */
1338                 
1339                 // store the dot product for this frame
1340                 Interpolate_dot[ship_index] = eye_dot;          
1341
1342                 // determine distance (near, medium, far)
1343                 vm_vec_sub(&obj_dot,&obj->pos,&pobj->pos);
1344                 dist = vm_vec_mag(&obj_dot);            
1345                 if(dist < OO_NEAR_DIST){
1346                         range = OO_NEAR;
1347                 } else if(dist < OO_MIDRANGE_DIST){
1348                         range = OO_MIDRANGE;
1349                 } else {
1350                         range = OO_FAR;
1351                 }
1352
1353                 // reset the timestamp for the next update for this guy
1354                 multi_oo_reset_timestamp(pl, obj, range, in_cone);
1355
1356                 // zero oo_flags
1357                 oo_flags = 0;
1358                 
1359                 // if the player is on "high" updates, add in extra physics info
1360                 if((pl->p_info.options.obj_update_level == OBJ_UPDATE_HIGH) || (pl->p_info.options.obj_update_level == OBJ_UPDATE_LAN)){
1361                         oo_flags |= OO_EXTRA_PHYSICS;
1362                 }
1363
1364                 // if the object's hull/shield timestamp has expired
1365                 if((Ships[obj->instance].status_update_stamp[player_index] == -1) || timestamp_elapsed(Ships[obj->instance].status_update_stamp[player_index])){
1366                         oo_flags |= (OO_HULL_NEW | OO_SHIELD_NEW);
1367
1368                         // reset the timestamp
1369                         multi_oo_reset_status_timestamp(obj, player_index);                     
1370                 }
1371
1372                 // if the object's hull/shield timestamp has expired
1373                 if((Ships[obj->instance].subsys_update_stamp[player_index] == -1) || timestamp_elapsed(Ships[obj->instance].subsys_update_stamp[player_index])){
1374                         oo_flags |= OO_SUBSYSTEMS_NEW | OO_AI_MODE_NEW;
1375
1376                         // reset the timestamp
1377                         multi_oo_reset_subsys_timestamp(obj, player_index);
1378                 }
1379
1380                 // add info for a targeted object)
1381                 if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
1382                         oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW | OO_SHIELD_NEW);
1383                 }
1384                 // all other cases
1385                 else {                  
1386                         // if the position didn't change, don't send anything                   
1387                         oo_flags |= OO_POS_NEW;                 
1388
1389                         // add info which is contingent upon being "in front"                   
1390                         if(in_cone){
1391                                 oo_flags |= OO_ORIENT_NEW;
1392                         }                                               
1393                 }
1394
1395                 // if the ship is a cruiser or capital ship, always send extra physics
1396                 if(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_CRUISER | SIF_CAPITAL)){
1397                         oo_flags |= OO_EXTRA_PHYSICS;
1398                 }
1399
1400                 // pack stuff only if we have to 
1401                 if(oo_flags){
1402                         return multi_oo_pack_data(pl,obj,oo_flags,data);
1403                 } 
1404                 
1405                 // don't do anything
1406                 return 0;
1407         }
1408         
1409         // didn't do anything
1410         return 0;
1411 }
1412
1413 // process all other objects for this player
1414 void multi_oo_process_all(net_player *pl)
1415 {
1416         ubyte data[MAX_PACKET_SIZE];
1417         ubyte data_add[MAX_PACKET_SIZE];
1418         ubyte stop;
1419         int add_size;   
1420         int packet_size = 0;
1421         int idx;
1422                 
1423         object *moveup;
1424         object *pobj;   
1425
1426         // if the player has an invalid objnum..
1427         if(pl->player->objnum < 0){
1428                 return;
1429         }
1430
1431         // get the player's object
1432         pobj = &Objects[pl->player->objnum];
1433
1434         object *targ_obj;       
1435
1436         // build the list of ships to check against
1437         multi_oo_build_ship_list(pl);
1438
1439         // do nothing if he has no object targeted, or if he has a weapon targeted
1440         if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1441                 // build the header
1442                 BUILD_HEADER(OBJECT_UPDATE);
1443
1444                 // add the sequencing #
1445                 ADD_DATA(Netgame.server_update_seq);
1446                 ADD_DATA(OO_global_time);
1447         
1448                 // get a pointer to the object
1449                 targ_obj = &Objects[pl->s_info.target_objnum];
1450         
1451                 // run through the maybe_update function
1452                 add_size = multi_oo_maybe_update(pl,pobj,targ_obj,data_add);
1453
1454                 // copy in any relevant data
1455                 if(add_size){
1456                         stop = 0xff;
1457                         ADD_DATA(stop);
1458
1459                         memcpy(data + packet_size, data_add, add_size);
1460                         packet_size += add_size;                
1461                 }
1462         } else {
1463                 // just build the header for the rest of the function
1464                 BUILD_HEADER(OBJECT_UPDATE);
1465
1466                 // add the sequencing #
1467                 ADD_DATA(Netgame.server_update_seq);
1468                 ADD_DATA(OO_global_time);
1469         }
1470                 
1471         idx = 0;
1472         while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1473                 // if this guy is over his datarate limit, do nothing
1474                 if(multi_oo_rate_exceeded(pl)){
1475                         nprintf(("Network","Capping client\n"));
1476                         idx++;
1477
1478                         continue;
1479                 }                       
1480
1481                 // get the object
1482                 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1483
1484                 // maybe send some info         
1485                 add_size = multi_oo_maybe_update(pl,pobj,moveup,data_add);
1486
1487                 // if this data is too much for the packet, send off what we currently have and start over
1488                 if(packet_size + add_size > OO_MAX_SIZE){
1489                         stop = 0x00;
1490                         ADD_DATA(stop);
1491                         
1492                         multi_io_send(pl data, packet_size);
1493                         pl->s_info.rate_bytes += packet_size;
1494
1495                         packet_size = 0;
1496                         BUILD_HEADER(OBJECT_UPDATE);
1497
1498                         // add the sequencing #
1499                         ADD_DATA(Netgame.server_update_seq);
1500                         ADD_DATA(OO_global_time);
1501                 }
1502
1503                 if(add_size){
1504                         stop = 0xff;
1505                         ADD_DATA(stop);
1506
1507                         // copy in the data
1508                         memcpy(data + packet_size,data_add,add_size);
1509                         packet_size += add_size;
1510                 }
1511
1512                 // next ship
1513                 idx++;
1514         }
1515
1516         // if we have anything more than 3 byte in the packet, send the last one off
1517         if(packet_size > 3){
1518                 stop = 0x00;
1519                 ADD_DATA(stop);
1520                 
1521                 multi_io_send(pl, data, packet_size);
1522                 pl->s_info.rate_bytes += packet_size;
1523         }
1524 }
1525
1526 // process all object update details for this frame
1527 void multi_oo_process()
1528 {
1529         int idx;
1530
1531         // increment sequencing #
1532         multi_oo_increment_seq();
1533         
1534         // process each player
1535         for(idx=0; idx<MAX_PLAYERS; idx++){
1536                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
1537                         // now process the rest of the objects
1538                         multi_oo_process_all(&Net_players[idx]);
1539                 }
1540         }
1541 }
1542
1543 // process incoming object update data
1544 void multi_oo_process_update(ubyte *data, header *hinfo)
1545 {
1546         ushort packet_seq, sequence_ref;
1547         ubyte stop;     
1548         int player_index;
1549         int server_stamp,time_born = 0;
1550         int offset = HEADER_LENGTH;
1551         net_player *pl = NULL;
1552
1553         // process sequencing info here
1554         GET_DATA(packet_seq);
1555         GET_DATA(server_stamp);
1556
1557         // if this is processed on the server, its a client object update packet
1558         player_index = -1;
1559         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1560                 // determine what player this came from and determine sequencing                                
1561                 player_index = find_player_id(hinfo->id);
1562                 if(player_index != -1){
1563                         // wrap around
1564                         if(((packet_seq - Net_players[player_index].client_cinfo_seq) < 0) && ((Net_players[player_index].client_cinfo_seq - packet_seq) > 2000)){
1565                                 Net_players[player_index].client_cinfo_seq = packet_seq;
1566                         } else if(packet_seq > Net_players[player_index].client_cinfo_seq){
1567                                 Net_players[player_index].client_cinfo_seq = packet_seq;
1568                         }
1569
1570                         // set the sequence reference
1571                         sequence_ref = Net_players[player_index].client_cinfo_seq;
1572
1573                         pl = &Net_players[player_index];
1574                 } else {
1575                         sequence_ref = packet_seq;
1576                         pl = NULL;
1577                 }
1578         }
1579         // otherwise its a "regular" object update packet
1580         else {
1581                 // do time latency stuff
1582                 //multi_tbuf_add_time(&OO_diff,server_stamp,timer_get_milliseconds());          
1583
1584                 // wrap around
1585                 if(((packet_seq - Net_player->client_server_seq) < 0) && ((Net_player->client_server_seq - packet_seq) > 2000)){
1586                         Net_player->client_server_seq = packet_seq;
1587                 } else if(packet_seq > Net_player->client_server_seq){
1588                         Net_player->client_server_seq = packet_seq;
1589                 }
1590
1591                 // set the sequence reference
1592                 sequence_ref = Net_player->client_server_seq;
1593
1594                 pl = Netgame.server;
1595         }
1596
1597         GET_DATA(stop);
1598         
1599         while(stop == 0xff){
1600                 // process the data
1601                 offset += multi_oo_unpack_data(pl, data + offset, packet_seq, sequence_ref, time_born);
1602
1603                 GET_DATA(stop);
1604         }
1605         PACKET_SET_SIZE();
1606 }
1607
1608 // initialize all object update timestamps (call whenever entering gameplay state)
1609 void multi_oo_gameplay_init()
1610 {
1611         int split,cur,idx;
1612         ship_obj *so;           
1613         object *objp;
1614         ship *shipp;
1615         split = 3000 / ship_get_num_ships();
1616
1617 #ifdef OO_NEW
1618         extern multi_oo_new_gameplay_init();
1619         multi_oo_new_gameplay_init();
1620 #else   
1621
1622         // server should setup initial update timestamps        
1623         // stagger initial updates over 3 seconds or so
1624         cur = 0;
1625         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1626                 if(Objects[so->objnum].type == OBJ_SHIP){
1627                         shipp = &Ships[Objects[so->objnum].instance];
1628                 
1629                         // update the timestamps
1630                         for(idx=0;idx<MAX_PLAYERS;idx++){
1631                                 shipp->pos_update_stamp[idx] = timestamp(cur);
1632                                 shipp->status_update_stamp[idx] = timestamp(cur + split);
1633                                 shipp->subsys_update_stamp[idx] = timestamp(cur + (split * 2));
1634                         }
1635
1636                         // increment the time
1637                         cur += split;
1638
1639                         objp = &Objects[so->objnum];
1640                         shipp->int_cur.pos = objp->pos;
1641                         shipp->int_cur.orient = objp->orient;
1642                         shipp->int_cur.vel = objp->phys_info.vel;
1643                         shipp->int_cur.desired_vel = objp->phys_info.desired_vel;
1644                         shipp->int_cur.rotvel = objp->phys_info.rotvel;
1645                         shipp->int_cur.desired_rotvel = objp->phys_info.desired_rotvel;
1646                         memcpy(&shipp->int_last, &shipp->int_cur, sizeof(interp_info));
1647
1648                         // uninitialized
1649                         Interpolate_dot[MAX_SHIPS] = -2.0f;
1650                 }
1651         }               
1652         
1653         // reset our data #'s
1654         OO_pos_total = 0;
1655         OO_vel_total = 0;
1656         OO_desired_vel_total = 0;
1657         OO_orient_total = 0;
1658         OO_rotvel_total = 0;
1659         OO_desired_rotvel_total = 0;
1660         OO_hull_total = 0;
1661         OO_shield_total = 0;
1662         OO_single_subsys_total = 0;
1663         OO_subsys_total = 0;
1664         OO_forward_thrust_total = 0;
1665 #endif
1666 }
1667
1668 // process an object update sync packet
1669 void multi_oo_process_update_sync(ubyte *data, header *hinfo)
1670 {
1671         int offset = HEADER_LENGTH;     
1672
1673         // we're ignoring this value for now    
1674         PACKET_SET_SIZE();              
1675
1676         // initialize the time buffer
1677         //multi_tbuf_init(&OO_diff);
1678 }
1679
1680 // send an update sync packet
1681 void multi_oo_send_update_sync(net_player *pl)
1682 {
1683         ubyte data[20];
1684         int packet_size = 0;    
1685
1686         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1687
1688         // build the header and add the data
1689         BUILD_HEADER(OBJ_UPDATE_SYNC);  
1690
1691         // send to everyone
1692         if(pl == NULL){         
1693                 multi_io_send_to_all_reliable(data, packet_size);
1694         } else {                
1695                 multi_io_send_reliable(pl, data, packet_size);
1696         }
1697 }
1698
1699 // initialize the server's time sync stuff
1700 void multi_oo_sync_init()
1701 {       
1702 //      OO_sync_stamp = timestamp(OO_SYNC_TIME);
1703 }
1704
1705 // send control info for a client (which is basically a "reverse" object update)
1706 void multi_oo_send_control_info()
1707 {
1708         ubyte data[MAX_PACKET_SIZE], stop;
1709         ubyte data_add[MAX_PACKET_SIZE];
1710         ubyte oo_flags; 
1711         int add_size;
1712         int packet_size = 0;
1713
1714         // if I'm dying or my object type is not a ship, bail here
1715         if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1716                 return;
1717         }
1718
1719         // increment sequencing
1720         multi_oo_increment_seq();
1721         
1722         // build the header
1723         BUILD_HEADER(OBJECT_UPDATE);    
1724
1725         // add the sequencing #
1726         ADD_DATA(Netgame.server_update_seq);
1727         ADD_DATA(OO_global_time);
1728
1729         oo_flags = (OO_POS_NEW | OO_ORIENT_NEW | OO_EXTRA_PHYSICS);
1730
1731         // pack the appropriate info into the data
1732         add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1733
1734         // copy in any relevant data
1735         if(add_size){
1736                 stop = 0xff;
1737                 ADD_DATA(stop);
1738
1739                 memcpy(data + packet_size, data_add, add_size);
1740                 packet_size += add_size;                
1741         }
1742
1743         // add the final stop byte
1744         stop = 0x0;
1745         ADD_DATA(stop);
1746
1747         // send to the server
1748         if(Netgame.server != NULL){             
1749                 multi_io_send(Net_player, data, packet_size);
1750         }
1751 }
1752
1753 // reset all sequencing info
1754 void multi_oo_reset_sequencing()
1755 {       
1756         int idx;
1757
1758         // reset outgoing sequence #'s
1759         Netgame.server_update_frame_ref = 0;
1760         Netgame.server_update_seq = 0;
1761         
1762         // reset all incoming seqeunce #'s
1763         for(idx=0;idx<MAX_PLAYERS;idx++){
1764                 Net_players[idx].client_cinfo_seq = 0;
1765                 Net_players[idx].client_server_seq = 0;
1766         }
1767 }
1768
1769 // display any oo info on the hud
1770 void multi_oo_display()
1771 {
1772 #ifndef NDEBUG
1773         np_update_record *r;
1774         int sy = 0;
1775
1776         if(!MULTIPLAYER_MASTER || MULTIPLAYER_STANDALONE){
1777                 return;
1778         }
1779         if(OO_update_index < 0){
1780                 return;
1781         }
1782         if(Net_players[OO_update_index].player == NULL){
1783                 return;
1784         }
1785         r = &OO_update_records[OO_update_index];
1786
1787         // display info
1788         gr_string(gr_screen.max_w - 150, sy, Net_players[OO_update_index].player->callsign);
1789         sy += 10;
1790         gr_printf(gr_screen.max_w - 150, sy, "pos : %d (%d/s)", r->pos_bytes, (int)r->pos_avg);
1791         sy += 10;
1792         gr_printf(gr_screen.max_w - 150, sy, "ori : %d (%d/s)", r->orient_bytes, (int)r->orient_avg);
1793         sy += 10;
1794         gr_printf(gr_screen.max_w - 150, sy, "hul : %d (%d/s)", r->hull_bytes, (int)r->hull_avg);
1795         sy += 10;
1796         gr_printf(gr_screen.max_w - 150, sy, "shi : %d (%d/s)", r->shield_bytes, (int)r->shield_avg);
1797         sy += 10;
1798         gr_printf(gr_screen.max_w - 150, sy, "aim : %d (%d/s)", r->ai_mode_bytes, (int)r->ai_mode_avg);
1799         sy += 10;
1800         gr_printf(gr_screen.max_w - 150, sy, "ext : %d (%d/s)", r->extra_physics_bytes, (int)r->extra_physics_avg);
1801         sy += 10;
1802         gr_printf(gr_screen.max_w - 150, sy, "fth : %d (%d/s)", r->fthrust_bytes, (int)r->fthrust_avg); 
1803         sy += 10;
1804         gr_printf(gr_screen.max_w - 150, sy, "sub : %d (%d/s)", r->subsys_bytes, (int)r->subsys_avg);   
1805 #endif
1806 }
1807
1808
1809 // ---------------------------------------------------------------------------------------------------
1810 // DATARATE DEFINES/VARS
1811 //
1812
1813 // low object update datarate limit
1814 #define OO_LIMIT_LOW                            2000
1815 #define OO_LIMIT_MED                            4000
1816
1817 // timestamp for sending control info (movement only - we'll send button info all the time)
1818 #define OO_CIRATE                                       67                                      // 15x a second
1819 int Multi_cirate_stamp                  = -1;                           // timestamp for waiting on control info time
1820 int Multi_cirate_can_send               = 1;                            // if we can send control info this frame
1821
1822 // global max rates
1823 int OO_server_rate = -1;                                                        // max _total_ bandwidth to send to all clients
1824 int OO_client_rate = -1;                                                        // max bandwidth to go to an individual client
1825
1826 // update timestamp for server datarate checking
1827 #define RATE_UPDATE_TIME                1250                            // in ms
1828 int OO_server_rate_stamp = -1;
1829
1830 // process datarate limiting stuff for the server
1831 void multi_oo_server_process();
1832
1833 // process datarate limiting stuff for the client
1834 void multi_oo_client_process();
1835
1836 // update the server datarate
1837 void multi_oo_update_server_rate();
1838
1839
1840 // ---------------------------------------------------------------------------------------------------
1841 // DATARATE FUNCTIONS
1842 //
1843
1844 // process all object update datarate details
1845 void multi_oo_rate_process()
1846 {
1847         // if I have no valid player, drop out here
1848         if(Net_player == NULL){
1849                 return;
1850         }
1851
1852         // if we're not in mission, don't do anything
1853         if(!(Game_mode & GM_IN_MISSION)){
1854                 return;
1855         }
1856
1857         // if I'm the server of a game, process server stuff
1858         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1859                 multi_oo_server_process();
1860         }
1861         // otherwise process client-side stuff
1862         else {
1863                 multi_oo_client_process();
1864         }
1865 }
1866
1867 // process datarate limiting stuff for the server
1868 void multi_oo_server_process()
1869 {
1870         int idx;
1871         
1872         // go through all players
1873         for(idx=0;idx<MAX_PLAYERS;idx++){
1874                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1875                         // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1876                         if((Net_players[idx].s_info.rate_stamp == -1) || timestamp_elapsed(Net_players[idx].s_info.rate_stamp)){
1877                                 Net_players[idx].s_info.rate_stamp = timestamp(1000);
1878                                 Net_players[idx].s_info.rate_bytes = 0;
1879                         }
1880                 }
1881         }
1882
1883         // determine if we should be updating the server datarate
1884         if((OO_server_rate_stamp == -1) || timestamp_elapsed(OO_server_rate_stamp)){
1885                 // reset the timestamp
1886                 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1887
1888                 // update the server datarate
1889                 multi_oo_update_server_rate();
1890
1891                 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1892         }
1893
1894         // see if we should be updating sync times
1895         /*
1896         if((OO_sync_stamp != -1) && timestamp_elapsed(OO_sync_stamp)){
1897                 // send an update sync packet
1898                 multi_oo_send_update_sync();
1899
1900                 // initialize the server's time sync stuff
1901                 multi_oo_sync_init();
1902         }
1903         */
1904 }
1905
1906 // process datarate limiting stuff for the client
1907 void multi_oo_client_process()
1908 {
1909         // if the timestamp is -1 or has elapsed, reset it
1910         if((Multi_cirate_stamp == -1) || timestamp_elapsed(Multi_cirate_stamp)){
1911                 Multi_cirate_can_send = 1;
1912                 Multi_cirate_stamp = timestamp(OO_CIRATE);
1913         }
1914
1915         // calculate what time difference should be added to all prediction calculations
1916         /*
1917         OO_time_diff = 
1918                                                 // server time plus current ping relative to "original" time
1919                                                 abs(    (OO_server_time + OO_my_orig_ping + (Netgame.server->s_info.ping.ping_avg - OO_my_orig_ping)) -
1920
1921                                                         // whatever my original time was at the "original" time
1922                                                         OO_my_time
1923                                                 );
1924                                                 */      
1925 }
1926
1927
1928 // datarate limiting system for server -------------------------------------
1929
1930 // initialize the rate limiting system for all players
1931 void multi_oo_rate_init_all()
1932 {
1933         int idx;
1934
1935         // if I don't have a net_player, bail here
1936         if(Net_player == NULL){
1937                 return;
1938         }
1939
1940         // if I'm the server of the game
1941         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
1942                 // go through all players
1943                 for(idx=0;idx<MAX_PLAYERS;idx++){
1944                         if(MULTI_CONNECTED(Net_players[idx])){
1945                                 multi_oo_rate_init(&Net_players[idx]);
1946                         }
1947                 }
1948
1949                 OO_server_rate_stamp = -1;
1950         }
1951         // if i'm the client, initialize my control info datarate stuff
1952         else {
1953                 Multi_cirate_stamp = -1;
1954                 Multi_cirate_can_send = 1;
1955         }
1956 }
1957
1958 // initialize the rate limiting for the passed in player
1959 void multi_oo_rate_init(net_player *pl)
1960 {
1961         // reinitialize his datarate timestamp
1962         pl->s_info.rate_stamp = -1;
1963         pl->s_info.rate_bytes = 0;
1964 }
1965
1966 // if the given net-player has exceeded his datarate limit
1967 int multi_oo_rate_exceeded(net_player *pl)
1968 {
1969         int rate_compare;
1970                 
1971         // check against the guy's object update level
1972         switch(pl->p_info.options.obj_update_level){
1973         // low update level
1974         case OBJ_UPDATE_LOW:
1975                 // the low object update limit
1976                 rate_compare = OO_LIMIT_LOW;
1977                 break;
1978
1979         // medium update level
1980         case OBJ_UPDATE_MEDIUM:         
1981                 // the low object update limit
1982                 rate_compare = OO_LIMIT_MED;
1983                 break;
1984
1985         // high update level - super high datarate
1986         case OBJ_UPDATE_HIGH:
1987                 rate_compare = 100000000;
1988                 break;
1989
1990         // LAN - no rate max
1991         case OBJ_UPDATE_LAN:
1992                 return 0;
1993
1994         // default level
1995         default:
1996                 Int3();
1997                 rate_compare = OO_LIMIT_LOW;
1998                 break;
1999         }
2000
2001         // if the server global rate is actually lower
2002         if(OO_client_rate < rate_compare){
2003                 rate_compare = OO_client_rate;
2004         }
2005
2006         // compare his bytes sent against the allowable amount
2007         if(pl->s_info.rate_bytes >= rate_compare){
2008                 return 1;
2009         }
2010
2011         // we're allowed to send
2012         return 0;
2013 }
2014
2015 // if it is ok for me to send a control info (will be ~N times a second)
2016 int multi_oo_cirate_can_send()
2017 {
2018         // if we're allowed to send
2019         if(Multi_cirate_can_send){
2020                 Multi_cirate_can_send = 0;
2021                 return 1;
2022         } 
2023         
2024         return 0;               
2025 }
2026
2027 // dynamically update the server capped bandwidth rate
2028 void multi_oo_update_server_rate()
2029 {       
2030         int num_connections;    
2031         
2032         // bail conditions
2033         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2034                 return;
2035         }
2036
2037         // get the # of connections
2038         num_connections = multi_num_connections();
2039         if(!(Game_mode & GM_STANDALONE_SERVER)){
2040                 num_connections--;
2041         }
2042         // make sure we always pretend there's at least one guy available
2043         if(num_connections <= 0){
2044                 num_connections = 1;
2045         }
2046                 
2047         // set the data rate    
2048         switch(Net_player->p_info.options.obj_update_level){
2049         // LAN update level
2050         case OBJ_UPDATE_LAN:
2051                 // set to 0 so we don't limit anything
2052                 OO_server_rate = Multi_options_g.datarate_cap;
2053                 break;
2054
2055         // high update level
2056         case OBJ_UPDATE_HIGH:
2057                 // set to 0 so we don't limit anything
2058                 OO_server_rate = Multi_options_g.datarate_cap;
2059                 break;
2060
2061         // medium update level
2062         case OBJ_UPDATE_MEDIUM:
2063                 // set the rate to be "medium" update level
2064                 OO_server_rate = OO_LIMIT_MED;
2065                 break;
2066
2067         // low update level 
2068         case OBJ_UPDATE_LOW:
2069                 // set the rate to be the "low" update level
2070                 OO_server_rate = OO_LIMIT_LOW;
2071                 break;
2072
2073         default:
2074                 Int3();
2075                 return;
2076         }       
2077
2078         // set the individual client level
2079         OO_client_rate = OO_server_rate / num_connections;
2080         // nprintf(("Network","Setting client rate to %d\n",OO_client_rate));
2081 }
2082
2083         
2084 #endif // #ifndef OO_NEW