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