]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_obj.cpp
Initial revision
[taylor/freespace2.git] / src / network / multi_obj.cpp
1 #include "freespace.h"
2 #include "timer.h"
3 #include "linklist.h"
4 #include "weapon.h"
5 #include "multimsgs.h"
6 #include "multiutil.h"
7 #include "multi_obj.h"
8 #include "multi_rate.h"
9 #include "multi.h"
10 #include "object.h"
11 #include "key.h"
12 #include "gamesnd.h"
13 #include "spline.h"
14 #include "alphacolors.h"
15 #include "afterburner.h"
16
17 // ---------------------------------------------------------------------------------------------------
18 // OBJECT UPDATE DEFINES/VARS
19 //
20
21 // test stuff
22 float oo_arrive_time[MAX_SHIPS][5];                             // the last 5 arrival times for each ship
23 int oo_arrive_time_count[MAX_SHIPS];                    // size of the arrival queue
24 float oo_arrive_time_avg_diff[MAX_SHIPS];               // the average time between arrivals
25 float oo_arrive_time_next[MAX_SHIPS];                   // how many seconds have gone by. should be equal to oo_arrive_time_avg_diff[] the next time we get an update
26
27 // interp stuff
28 int oo_interp_count[MAX_SHIPS];
29 vector oo_interp_points[MAX_SHIPS][2];
30 bez_spline oo_interp_splines[MAX_SHIPS][2];
31 void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info);
32
33 // how much data we're willing to put into a given oo packet
34 #define OO_MAX_SIZE                                     480
35
36 // tolerance for bashing position
37 #define OO_POS_UPDATE_TOLERANCE 100.0f
38
39 // new improved - more compacted info type
40 #define OO_POS_NEW                                      (1<<0)          // 
41 #define OO_ORIENT_NEW                           (1<<1)          // 
42 #define OO_HULL_NEW                                     (1<<2)          // Hull AND shields
43 #define OO_AFTERBURNER_NEW                      (1<<3)          // 
44 #define OO_SUBSYSTEMS_AND_AI_NEW        (1<<4)          // 
45 #define OO_PRIMARY_BANK                         (1<<5)          // if this is set, fighter has selected bank one
46 #define OO_PRIMARY_LINKED                       (1<<6)          // if this is set, banks are linked
47 #define OO_TRIGGER_DOWN                         (1<<7)          // if this is set, trigger is DOWN
48
49 #define OO_VIEW_CONE_DOT                        (0.1f)
50 #define OO_VIEW_DIFF_TOL                        (0.15f)                 // if the dotproducts differ this far between frames, he's coming into view
51
52 // no timestamp should ever have sat for longer than this. 
53 #define OO_MAX_TIMESTAMP                        2500
54
55 // distance class
56 #define OO_NEAR                                         0
57 #define OO_NEAR_DIST                                    (200.0f)
58 #define OO_MIDRANGE                                     1
59 #define OO_MIDRANGE_DIST                        (600.0f)
60 #define OO_FAR                                                  2
61 #define OO_FAR_DIST                                     (1400.0f)
62
63 // how often we should send full hull/shield updates
64 #define OO_HULL_SHIELD_TIME             600
65 #define OO_SUBSYS_TIME                          1000
66
67 // timestamp values for object update times based on client's update level.
68 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] = 
69 {
70         100,                            // 15x a second 
71         100,                            // 15x a second
72         66,                             // 30x a second
73         66,
74 };
75
76 // for near ships
77 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
78 {
79         150,                            // low update
80         100,                            // medium update
81         66,                             // high update
82         66,
83 };
84
85 // for medium ships
86 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
87 {
88         250,                            // low update
89         180,                            // medium update
90         120,                            // high update
91         66,
92 };
93
94 // for far ships
95 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
96 {
97         750,                            // low update
98         350,                            // medium update
99         150,                            // high update
100         66,
101 };
102
103 // for near ships
104 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] = 
105 {
106         300,                            // low update
107         200,                            // medium update
108         100,                            // high update
109         66,
110 };
111
112 // for medium ships
113 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] = 
114 {
115         800,                            // low update
116         600,                            // medium update
117         300,                            // high update
118         66,
119 };
120
121 // for far ships
122 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] = 
123 {
124         2500,                   // low update
125         1500,                           // medium update
126         400,                            // high update
127         66,
128 };
129
130 // ship index list for possibly sorting ships based upon distance, etc
131 short OO_ship_index[MAX_SHIPS];
132
133 int OO_update_index = -1;                                                       // index into OO_update_records for displaying update record info
134
135 // ---------------------------------------------------------------------------------------------------
136 // OBJECT UPDATE FUNCTIONS
137 //
138
139 object *OO_player_obj;
140 int OO_sort = 1;
141
142 int multi_oo_sort_func(const void *ship1, const void *ship2)
143 {
144         object *obj1, *obj2;
145         short index1, index2;
146         float dist1, dist2;
147         float dot1, dot2;
148         vector v1, v2;
149         vector vn1, vn2;
150
151         // get the 2 indices
152         memcpy(&index1, ship1, sizeof(short));
153         memcpy(&index2, ship2, sizeof(short));
154
155         // if the indices are bogus, or the objnums are bogus, return ">"
156         if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
157                 return 1;
158         }
159
160         // get the 2 objects
161         obj1 = &Objects[Ships[index1].objnum];
162         obj2 = &Objects[Ships[index2].objnum];
163
164         // get the distance and dot product to the player obj for both
165         vm_vec_sub(&v1, &OO_player_obj->pos, &obj1->pos);
166         dist1 = vm_vec_copy_normalize(&vn1, &v1);
167         vm_vec_sub(&v2, &OO_player_obj->pos, &obj2->pos);
168         dist2 = vm_vec_copy_normalize(&vn2, &v2);
169         dot1 = vm_vec_dotprod(&OO_player_obj->orient.fvec, &vn1);
170         dot2 = vm_vec_dotprod(&OO_player_obj->orient.fvec, &vn2);
171
172         // objects in front take precedence
173         if((dot1 < 0.0f) && (dot2 >= 0.0f)){
174                 return 1;
175         } else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
176                 return -1;
177         }
178
179         // otherwise go by distance
180         return (dist1 <= dist2) ? -1 : 1;
181 }
182
183 // build the list of ship indices to use when updating for this player
184 void multi_oo_build_ship_list(net_player *pl)
185 {
186         int ship_index;
187         int idx;
188         ship_obj *moveup;
189         object *player_obj;
190
191         // set all indices to be -1
192         for(idx = 0;idx<MAX_SHIPS; idx++){
193                 OO_ship_index[idx] = -1;
194         }
195
196         // get the player object
197         if(pl->player->objnum < 0){
198                 return;
199         }
200         player_obj = &Objects[pl->player->objnum];
201         
202         // go through all other relevant objects
203         ship_index = 0;
204         for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
205                 // if it is an invalid ship object, skip it
206                 if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
207                         continue;
208                 }
209
210                 // if we're a standalone server, don't send any data regarding its pseudo-ship
211                 if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
212                         continue;
213                 }               
214                         
215                 // must be a ship, a weapon, and _not_ an observer
216                 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
217                         continue;
218                 }
219
220                 // don't send info for dying ships
221                 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
222                         continue;
223                 }               
224
225                 // never update the knossos device
226                 if ((Ships[Objects[moveup->objnum].instance].ship_info_index >= 0) && (Ships[Objects[moveup->objnum].instance].ship_info_index < Num_ship_types) && (Ship_info[Ships[Objects[moveup->objnum].instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE)){
227                         continue;
228                 }
229                                 
230                 // don't send him info for himself
231                 if ( &Objects[moveup->objnum] == player_obj ){
232                         continue;
233                 }
234
235                 // don't send info for his targeted ship here, since its always done first
236                 if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
237                         continue;
238                 }
239
240                 // add the ship 
241                 if(ship_index < MAX_SHIPS){
242                         OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
243                 }
244         }
245
246         // maybe qsort the thing here
247         OO_player_obj = player_obj;
248         if(OO_sort){
249                 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
250         }
251 }
252
253 // pack information for a client (myself), return bytes added
254 int multi_oo_pack_client_data(ubyte *data)
255 {
256         ubyte out_flags;
257         ushort tnet_signature;
258         char t_subsys, l_subsys;
259         int packet_size = 0;
260
261         // get our firing stuff
262         out_flags = Net_player->s_info.accum_buttons;   
263
264         // zero these values for now
265         Net_player->s_info.accum_buttons = 0;
266
267         // add any necessary targeting flags
268         if ( Player_ai->current_target_is_locked ){
269                 out_flags |= OOC_TARGET_LOCKED;
270         }
271         if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){     
272                 out_flags |= OOC_TARGET_SEEK_LOCK;
273         }
274         if ( Player->locking_on_center ){
275                 out_flags |= OOC_LOCKING_ON_CENTER;
276         }
277         if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){
278                 out_flags |= OOC_TRIGGER_DOWN;
279         }
280
281         // send my bank info
282         if(Player_ship != NULL){
283                 if(Player_ship->weapons.current_primary_bank > 0){
284                         out_flags |= OOC_PRIMARY_BANK;
285                 }
286
287                 // linked or not
288                 if(Player_ship->flags & SF_PRIMARY_LINKED){
289                         out_flags |= OOC_PRIMARY_LINKED;
290                 }
291         }
292
293         // copy the final flags in
294         memcpy(data, &out_flags, sizeof(ubyte));
295         packet_size++;  
296
297         // client targeting information 
298         t_subsys = -1;
299         l_subsys = -1;
300
301         // if nothing targeted
302         if(Player_ai->target_objnum == -1){
303                 tnet_signature = 0;
304         }
305         // if something is targeted 
306         else {
307                 // target net signature
308                 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
309                         
310                 // targeted subsys index
311                 if(Player_ai->targeted_subsys != NULL){
312                         t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
313                 }
314
315                 // locked targeted subsys index
316                 if(Player->locking_subsys != NULL){
317                         l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
318                 }
319         }
320
321         // add them all
322         memcpy(data + packet_size, &tnet_signature, sizeof(ushort));
323         packet_size += sizeof(ushort);
324         memcpy(data + packet_size, &t_subsys, sizeof(char));
325         packet_size += sizeof(char);
326         memcpy(data + packet_size, &l_subsys, sizeof(char));
327         packet_size += sizeof(char);    
328
329         return packet_size;
330 }
331
332 // pack the appropriate info into the data
333 #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++; }
334 int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
335 {       
336         ubyte data[255];
337         ubyte data_size = 0;    
338         char percent;
339         ship *shipp;    
340         ship_info *sip;
341         ubyte ret;
342         float temp;     
343         int header_bytes;
344         int packet_size = 0;    
345
346         // make sure we have a valid ship
347         Assert(objp->type == OBJ_SHIP);
348         if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
349                 shipp = &Ships[objp->instance];
350                 sip = &Ship_info[shipp->ship_info_index];
351         } else {
352                 return 0;
353         }                       
354
355         // invalid player
356         if(pl == NULL){
357                 return 0;
358         }
359
360         // no flags -> do nothing
361         if(oo_flags == 0){
362                 // Int3();
363                 return 0;
364         }
365
366         // if i'm the client, make sure I only send certain things      
367         if(!MULTIPLAYER_MASTER){
368                 Assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW));
369                 Assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW)));
370         } 
371         // server 
372         else {
373                 // Assert(oo_flags & OO_POS_NEW);
374         }
375
376         // header sizes
377         if(MULTIPLAYER_MASTER){
378                 header_bytes = 5;
379         } else {
380                 header_bytes = 2;
381         }       
382
383         // if we're a client (and therefore sending control info), pack client-specific info
384         if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
385                 packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);            
386         }               
387                 
388         // position, velocity
389         if ( oo_flags & OO_POS_NEW ) {          
390                 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
391                 packet_size += ret;
392                 
393                 // global records
394                 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);         
395                         
396                 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
397                 packet_size += ret;             
398                         
399                 // global records               
400                 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);                         
401         }       
402
403         // orientation  
404         if(oo_flags & OO_ORIENT_NEW){
405                 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
406                 // Assert(ret == OO_ORIENT_RET_SIZE);
407                 packet_size += ret;
408                 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);                         
409
410                 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
411                 packet_size += ret;     
412
413                 // global records               
414                 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);         
415         }
416                         
417         // forward thrust       
418         percent = (char)(objp->phys_info.forward_thrust * 100.0f);
419         Assert( percent <= 100 );
420
421         memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
422         packet_size += 1;
423
424         // global records       
425         multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1);   
426
427         // hull info
428         if ( oo_flags & OO_HULL_NEW ){
429                 // add the hull value for this guy              
430                 temp = (objp->hull_strength  / Ship_info[shipp->ship_info_index].initial_hull_strength);                
431                 PACK_PERCENT(temp);                             
432                 multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1);   
433
434                 // pack 2 shield values into each byte
435
436                 // pack quadrant 1
437                 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
438                 PACK_PERCENT(temp);
439                                 
440                 // pack quadrant 2
441                 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
442                 PACK_PERCENT(temp);                             
443
444                 // pack quadrant 3
445                 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
446                 PACK_PERCENT(temp);
447                                 
448                 // pack quadrant 2
449                 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));                          
450                 PACK_PERCENT(temp);                             
451                                 
452                 multi_rate_add(NET_PLAYER_NUM(pl), "shl", 4);   
453         }       
454
455         // subsystem info
456         if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){
457                 ubyte ns;               
458                 ship_subsys *subsysp;
459                                 
460                 // just in case we have some kind of invalid data (should've been taken care of earlier in this function)
461                 if(shipp->ship_info_index < 0){
462                         ns = 0;
463                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
464                         packet_size++;
465                         
466                         multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);   
467                 }
468                 // add the # of subsystems, and their data
469                 else {
470                         ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
471                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
472                         packet_size++;                  
473                         multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);   
474
475                         // now the subsystems.
476                         for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
477                                 temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits;
478                                 PACK_PERCENT(temp);
479                                 
480                                 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
481                         }
482                 }
483
484                 // ai mode info
485                 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
486                 short submode = (short)(Ai_info[shipp->ai_index].submode);
487                 ushort target_signature;
488
489                 target_signature = 0;
490                 if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
491                         target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
492                 }
493
494                 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
495                 packet_size++;
496                 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
497                 packet_size += 2;
498                 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));           
499                 packet_size += 2;               
500                 
501                 multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5);
502
503                 // primary weapon energy
504                 temp = shipp->weapon_energy / sip->max_weapon_reserve;
505                 PACK_PERCENT(temp);
506         }               
507
508         // afterburner info
509         oo_flags &= ~PF_AFTERBURNER_ON;
510         if(objp->phys_info.flags & PF_AFTERBURNER_ON){
511                 oo_flags |= OO_AFTERBURNER_NEW;
512         }
513
514         // if this ship is a support ship, send some extra info
515         ubyte support_extra = 0;
516         if(MULTIPLAYER_MASTER && (sip->flags & SIF_SUPPORT) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
517                 ushort dock_sig;
518
519                 // flag
520                 support_extra = 1;              
521                 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
522                 packet_size += 1;
523                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].ai_flags, sizeof(int));
524                 packet_size += 4;
525                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].mode, sizeof(int));
526                 packet_size += 4;
527                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].submode, sizeof(int));
528                 packet_size += 4;
529                 if((Ai_info[shipp->ai_index].dock_objnum < 0) || (Ai_info[shipp->ai_index].dock_objnum >= MAX_OBJECTS)){
530                         dock_sig = 0;
531                 } else {
532                         dock_sig = Objects[Ai_info[shipp->ai_index].dock_objnum].net_signature;
533                 }               
534                 memcpy(data + packet_size + header_bytes, &dock_sig, sizeof(ushort));
535                 packet_size += 2;
536         } else {
537                 support_extra = 0;              
538                 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
539                 packet_size += 1;
540         }                       
541
542         // make sure we have a valid chunk of data
543         Assert(packet_size < 255);
544         if(packet_size >= 255){
545                 return 0;
546         }
547         data_size = (ubyte)packet_size;
548
549         // add the object's net signature, type and oo_flags
550         packet_size = 0;
551         // don't add for clients
552         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
553                 multi_rate_add(NET_PLAYER_NUM(pl), "sig", 2);
554                 ADD_DATA( objp->net_signature );                
555                 
556                 multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1);
557                 ADD_DATA( oo_flags );
558         }       
559         multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1);
560         ADD_DATA( data_size );  
561         
562         multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1);
563         ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq );
564
565         packet_size += data_size;
566
567         // copy to the outgoing data
568         memcpy(data_out, data, packet_size);    
569         
570         return packet_size;     
571 }
572
573 // unpack information for a client , return bytes processed
574 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
575 {
576         ubyte in_flags;
577         ship *shipp;    
578         int offset = 0;
579         
580         memcpy(&in_flags, data, sizeof(ubyte)); 
581         offset++;
582
583         // get the player ship
584         shipp = NULL;
585         if((pl->player->objnum >= 0) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Objects[pl->player->objnum].instance >= 0)){
586                 shipp = &Ships[Objects[pl->player->objnum].instance];
587         }
588                 
589         // if we have a valid netplayer pointer
590         if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){
591                 // primary fired
592                 pl->player->ci.fire_primary_count = 0;          
593
594                 // secondary fired
595                 pl->player->ci.fire_secondary_count = 0;
596                 if ( in_flags & OOC_FIRE_SECONDARY ){
597                         pl->player->ci.fire_secondary_count = 1;
598                 }
599
600                 // countermeasure fired         
601                 pl->player->ci.fire_countermeasure_count = 0;           
602
603                 // set up aspect locking information
604                 pl->player->locking_on_center = 0;
605                 if ( in_flags & OOC_LOCKING_ON_CENTER ){
606                         pl->player->locking_on_center = 1;
607                 }               
608
609                 // trigger down, bank info
610                 if(shipp != NULL){
611                         if(in_flags & OOC_TRIGGER_DOWN){
612                                 shipp->flags |= SF_TRIGGER_DOWN;
613                         } else {
614                                 shipp->flags &= ~SF_TRIGGER_DOWN;
615                         }
616                         
617                         if(in_flags & OOC_PRIMARY_BANK){                
618                                 shipp->weapons.current_primary_bank = 1;
619                         } else {
620                                 shipp->weapons.current_primary_bank = 0;
621                         }
622
623                         // linked or not                                                                
624                         shipp->flags &= ~SF_PRIMARY_LINKED;
625                         if(in_flags & OOC_PRIMARY_LINKED){                              
626                                 shipp->flags |= SF_PRIMARY_LINKED;
627                         }
628                 }
629
630                 // other locking information
631                 if((shipp != NULL) && (shipp->ai_index != -1)){                 
632                         Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
633                         if      ( in_flags & OOC_TARGET_SEEK_LOCK ) {
634                                 Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
635                         } else {
636                                 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
637                         }
638                 }
639         }
640         
641         // client targeting information 
642         ushort tnet_sig;
643         char t_subsys,l_subsys;
644         object *tobj;
645
646         // get the data
647         GET_DATA(tnet_sig);
648         GET_DATA(t_subsys);
649         GET_DATA(l_subsys);
650
651         // try and find the targeted object
652         tobj = NULL;
653         if(tnet_sig != 0){
654                 tobj = multi_get_network_object( tnet_sig );
655         }
656         // maybe fill in targeted object values
657         if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){
658                 // assign the target object
659                 if(Objects[pl->player->objnum].type == OBJ_SHIP){
660                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
661                 }
662                 pl->s_info.target_objnum = OBJ_INDEX(tobj);
663
664                 // assign subsystems if possible                                        
665                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
666                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL;
667                         if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
668                                 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
669                         }
670                 }
671
672                 pl->player->locking_subsys = NULL;
673                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
674                         if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
675                                 pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
676                         }                               
677                 }
678         }                               
679
680         return offset;
681 }
682
683 // unpack the object data, return bytes processed
684 #define UNPACK_PERCENT(v)                                       { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
685 int multi_oo_unpack_data(net_player *pl, ubyte *data)
686 {       
687         int offset = 0;         
688         object *pobjp;
689         ushort net_sig = 0;
690         ubyte data_size, oo_flags;
691         ubyte seq_num;
692         char percent;   
693         float fpct;
694         ship *shipp;
695         ship_info *sip;
696
697         // add the object's net signature, type and oo_flags
698         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
699                 GET_DATA( net_sig );            
700                 GET_DATA( oo_flags );   
701         }
702         // clients always pos and orient stuff only
703         else {          
704                 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
705         }
706         GET_DATA( data_size );  
707         GET_DATA( seq_num );
708
709         // try and find the object
710         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
711                 pobjp = multi_get_network_object(net_sig);      
712         } else {        
713                 if((pl != NULL) && (pl->player->objnum != -1)){
714                         pobjp = &Objects[pl->player->objnum];
715                 } else {
716                         pobjp = NULL;
717                 }
718         }       
719         
720         // if we can't find the object, set pointer to bogus object to continue reading the data
721         // ignore out of sequence packets here as well
722         if ( (pobjp == NULL) || (pobjp->type != OBJ_SHIP) || (pobjp->instance < 0) || (pobjp->instance >= MAX_SHIPS) || (Ships[pobjp->instance].ship_info_index < 0) || (Ships[pobjp->instance].ship_info_index >= Num_ship_types)){            
723                 offset += data_size;
724                 return offset;
725         }
726         
727         // ship pointer
728         shipp = &Ships[pobjp->instance];
729         sip = &Ship_info[shipp->ship_info_index];
730
731         // ---------------------------------------------------------------------------------------------------------------
732         // CRITICAL OBJECT UPDATE SHIZ
733         // ---------------------------------------------------------------------------------------------------------------
734         
735         // if the packet is out of order
736         if(seq_num < shipp->np_updates[NET_PLAYER_NUM(pl)].seq){
737                 // non-wraparound case
738                 if((shipp->np_updates[NET_PLAYER_NUM(pl)].seq - seq_num) <= 100){
739                         offset += data_size;
740                         return offset;
741                 }
742         }       
743
744         // if this is from a player, read his button info
745         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
746                 offset += multi_oo_unpack_client_data(pl, data + offset);               
747         }       
748
749         // new info
750         vector new_pos = pobjp->pos;
751         physics_info new_phys_info = pobjp->phys_info;
752         matrix new_orient = pobjp->orient;
753         
754         // position
755         if ( oo_flags & OO_POS_NEW ) {                                          
756                 // AVERAGE TIME BETWEEN PACKETS FOR THIS SHIP
757                 // store this latest time stamp
758                 if(oo_arrive_time_count[shipp - Ships] == 5){
759                         memmove(&oo_arrive_time[shipp - Ships][0], &oo_arrive_time[shipp - Ships][1], sizeof(float) * 4);
760                         oo_arrive_time[shipp - Ships][4] = f2fl(Missiontime);
761                 } else {
762                         oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime);
763                 }
764                 // if we've got 5 elements calculate the average
765                 if(oo_arrive_time_count[shipp - Ships] == 5){
766                         int idx;
767                         oo_arrive_time_avg_diff[shipp - Ships] = 0.0f;
768                         for(idx=0; idx<4; idx++){
769                                 oo_arrive_time_avg_diff[shipp - Ships] += oo_arrive_time[shipp - Ships][idx + 1] - oo_arrive_time[shipp - Ships][idx];
770                         }
771                         oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f;
772                 }
773                 // next expected arrival time
774                 oo_arrive_time_next[shipp - Ships] = 0.0f;
775
776                 // int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos );
777                 int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos );
778                 offset += r1;                           
779
780                 // int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
781                 int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &new_pos, &new_phys_info );
782                 offset += r3;
783                 
784                 // bash desired vel to be velocity
785                 // pobjp->phys_info.desired_vel = pobjp->phys_info.vel;         
786         }       
787
788         // orientation  
789         if ( oo_flags & OO_ORIENT_NEW ) {               
790                 // int r2 = multi_pack_unpack_orient( 0, data + offset, &pobjp->orient );
791                 int r2 = multi_pack_unpack_orient( 0, data + offset, &new_orient );
792                 offset += r2;           
793
794                 // int r5 = multi_pack_unpack_rotvel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
795                 int r5 = multi_pack_unpack_rotvel( 0, data + offset, &new_orient, &new_pos, &new_phys_info );
796                 offset += r5;
797
798                 // bash desired rotvel to be 0
799                 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
800         }
801         
802         // forward thrust       
803         percent = (char)(pobjp->phys_info.forward_thrust * 100.0f);
804         Assert( percent <= 100 );
805         GET_DATA(percent);              
806
807         // now stuff all this new info
808         if(oo_flags & OO_POS_NEW){
809                 // if we're past the position update tolerance, bash.
810                 // this should cause our 2 interpolation splines to be exactly the same. so we'll see a jump,
811                 // but it should be nice and smooth immediately afterwards
812                 if(vm_vec_dist(&new_pos, &pobjp->pos) > OO_POS_UPDATE_TOLERANCE){
813                         pobjp->pos = new_pos;
814                 }
815
816                 // recalc any interpolation info                
817                 if(oo_interp_count[shipp - Ships] < 2){
818                         oo_interp_points[shipp - Ships][oo_interp_count[shipp - Ships]++] = new_pos;                    
819                 } else {
820                         oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1];
821                         oo_interp_points[shipp - Ships][1] = new_pos;                   
822
823                         multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info);
824                 }
825                 
826                 pobjp->phys_info.vel = new_phys_info.vel;               
827                 pobjp->phys_info.desired_vel = new_phys_info.vel;
828         } 
829
830         // we'll just sim rotation straight. it works fine.
831         if(oo_flags & OO_ORIENT_NEW){
832                 pobjp->orient = new_orient;
833                 pobjp->phys_info.rotvel = new_phys_info.rotvel;
834                 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
835                 pobjp->phys_info.desired_rotvel = new_phys_info.rotvel;
836         }       
837
838
839         // ---------------------------------------------------------------------------------------------------------------
840         // ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE!
841         // ---------------------------------------------------------------------------------------------------------------
842         
843         // hull info
844         if ( oo_flags & OO_HULL_NEW ){
845                 UNPACK_PERCENT(fpct);
846                 pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength;          
847
848                 float shield_0, shield_1, shield_2, shield_3;
849                 
850                 // unpack the 4 quadrants
851                 UNPACK_PERCENT(shield_0);
852                 UNPACK_PERCENT(shield_1);
853                 UNPACK_PERCENT(shield_2);
854                 UNPACK_PERCENT(shield_3);
855
856                 pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
857                 pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
858                 pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
859                 pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;               
860         }       
861
862         if ( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ) {
863                 ubyte n_subsystems, subsys_count;
864                 float subsystem_percent[MAX_MODEL_SUBSYSTEMS];          
865                 ship_subsys *subsysp;           
866                 float val;              
867                 int i;          
868
869                 // get the data for the subsystems
870                 GET_DATA( n_subsystems );
871                 for ( i = 0; i < n_subsystems; i++ ){
872                         UNPACK_PERCENT( subsystem_percent[i] );
873                 }               
874                 
875                 // fill in the subsystem data
876                 subsys_count = 0;
877                 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
878                         int subsys_type;
879
880                         val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
881                         subsysp->current_hits = val;
882
883                         // add the value just generated (it was zero'ed above) into the array of generic system types
884                         subsys_type = subsysp->system_info->type;                                       // this is the generic type of subsystem
885                         Assert ( subsys_type < SUBSYSTEM_MAX );
886                         shipp->subsys_info[subsys_type].current_hits += val;
887                         subsys_count++;
888
889                         // if we've reached max subsystems for some reason, bail out
890                         if(subsys_count >= n_subsystems){
891                                 break;
892                         }
893                 }
894                 
895                 // recalculate all ship subsystems
896                 ship_recalc_subsys_strength( shipp );                   
897
898                 // ai mode info
899                 ubyte umode;
900                 short submode;
901                 ushort target_signature;
902                 object *target_objp;
903
904                 GET_DATA( umode );
905                 GET_DATA( submode );
906                 GET_DATA( target_signature );           
907
908                 if(shipp->ai_index >= 0){
909                         Ai_info[shipp->ai_index].mode = umode;
910                         Ai_info[shipp->ai_index].submode = submode;             
911
912                         // set this guys target objnum
913                         target_objp = multi_get_network_object( target_signature );
914                         if ( target_objp == NULL ){
915                                 Ai_info[shipp->ai_index].target_objnum = -1;
916                         } else {
917                                 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
918                         }
919                 }               
920
921                 // primary weapon energy                
922                 float weapon_energy_pct;
923                 UNPACK_PERCENT(weapon_energy_pct);
924                 shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct;             
925         }       
926
927         // support ship extra info
928         ubyte support_extra;
929         GET_DATA(support_extra);
930         if(support_extra){
931                 ushort dock_sig;
932                 int ai_flags, ai_mode, ai_submode;
933
934                 // flag         
935                 GET_DATA(ai_flags);
936                 GET_DATA(ai_mode);
937                 GET_DATA(ai_submode);
938                 GET_DATA(dock_sig);             
939
940                 // valid ship?                                                  
941                 if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
942                         Ai_info[shipp->ai_index].ai_flags = ai_flags;
943                         Ai_info[shipp->ai_index].mode = ai_mode;
944                         Ai_info[shipp->ai_index].submode = ai_submode;
945
946                         object *objp = multi_get_network_object( dock_sig );
947                         if(objp != NULL){
948                                 Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp);
949                         }
950                 }                       
951         } 
952
953         // afterburner info
954         if(oo_flags & OO_AFTERBURNER_NEW){
955                 // maybe turn them on
956                 if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){
957                         afterburners_start(pobjp);
958                 }
959         } else {
960                 // maybe turn them off
961                 if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
962                         afterburners_stop(pobjp);
963                 }
964         }
965
966         // primary info (only clients care about this)
967         if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
968                 // what bank
969                 if(oo_flags & OO_PRIMARY_BANK){
970                         shipp->weapons.current_primary_bank = 1;
971                 } else {
972                         shipp->weapons.current_primary_bank = 0;
973                 }
974
975                 // linked or not
976                 shipp->flags &= ~SF_PRIMARY_LINKED;
977                 if(oo_flags & OO_PRIMARY_LINKED){
978                         shipp->flags |= SF_PRIMARY_LINKED;
979                 }
980
981                 // trigger down or not - server doesn't care about this. he'll get it from clients anyway               
982                 shipp->flags &= ~SF_TRIGGER_DOWN;
983                 if(oo_flags & OO_TRIGGER_DOWN){
984                         shipp->flags |= SF_TRIGGER_DOWN;
985                 }               
986         }
987         
988         // if we're the multiplayer server, set eye position and orient
989         if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){
990                 pl->s_info.eye_pos = pobjp->pos;
991                 pl->s_info.eye_orient = pobjp->orient;
992         }               
993
994         // update the sequence #
995         shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
996
997         // flag the object as just updated
998         // pobjp->flags |= OF_JUST_UPDATED;
999         
1000         return offset;
1001 }
1002
1003 // reset the timestamp appropriately for the passed in object
1004 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1005 {
1006         int stamp = 0;  
1007
1008         // if this is the guy's target, 
1009         if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1010                 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1011         } else {
1012                 // reset the timestamp appropriately
1013                 if(in_cone){
1014                         // base it upon range
1015                         switch(range){
1016                         case OO_NEAR:
1017                                 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1018                                 break;
1019
1020                         case OO_MIDRANGE:
1021                                 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1022                                 break;
1023
1024                         case OO_FAR:
1025                                 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1026                                 break;
1027                         }
1028                 } else {
1029                         // base it upon range
1030                         switch(range){
1031                         case OO_NEAR:
1032                                 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1033                                 break;
1034
1035                         case OO_MIDRANGE:
1036                                 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1037                                 break;
1038
1039                         case OO_FAR:
1040                                 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1041                                 break;
1042                         }
1043                 }                                               
1044         }
1045
1046         // reset the timestamp for this object
1047         if(objp->type == OBJ_SHIP){
1048                 Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp);
1049         } 
1050 }
1051
1052 // reset the timestamp appropriately for the passed in object
1053 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1054 {
1055         Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
1056 }
1057
1058 // reset the timestamp appropriately for the passed in object
1059 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1060 {
1061         Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
1062 }
1063
1064 // determine what needs to get sent for this player regarding the passed object, and when
1065 int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data)
1066 {
1067         ubyte oo_flags;
1068         int stamp;
1069         int player_index;
1070         vector player_eye;
1071         vector obj_dot;
1072         float eye_dot, dist;
1073         int in_cone;
1074         int range;
1075         int ship_index;
1076         ship *shipp;
1077         ship_info *sip;
1078         ushort cur_pos_chksum = 0;
1079         ushort cur_orient_chksum = 0;
1080
1081         // if the timestamp has elapsed for this guy, send stuff
1082         player_index = NET_PLAYER_INDEX(pl);
1083         if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1084                 return 0;
1085         }
1086
1087         // determine what the timestamp is for this object
1088         if(obj->type == OBJ_SHIP){
1089                 stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp;
1090                 ship_index = &Ships[obj->instance] - Ships;
1091         } else {
1092                 return 0;
1093         }
1094
1095         // stamp hasn't popped yet
1096         if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){
1097                 return 0;
1098         }
1099         
1100         // if we're supposed to update this guy 
1101
1102         // get the ship pointer
1103         shipp = &Ships[obj->instance];
1104
1105         // get ship info pointer
1106         sip = NULL;
1107         if(shipp->ship_info_index >= 0){
1108                 sip = &Ship_info[shipp->ship_info_index];
1109         }
1110         
1111         // check dot products           
1112         player_eye = pl->s_info.eye_orient.fvec;
1113         vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1114         vm_vec_normalize(&obj_dot);
1115         eye_dot = vm_vec_dot(&obj_dot, &player_eye);            
1116         in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;                
1117                                                         
1118         // determine distance (near, medium, far)
1119         vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1120         dist = vm_vec_mag(&obj_dot);            
1121         if(dist < OO_NEAR_DIST){
1122                 range = OO_NEAR;
1123         } else if(dist < OO_MIDRANGE_DIST){
1124                 range = OO_MIDRANGE;
1125         } else {
1126                 range = OO_FAR;
1127         }
1128
1129         // reset the timestamp for the next update for this guy
1130         multi_oo_reset_timestamp(pl, obj, range, in_cone);
1131
1132         // base oo_flags
1133         oo_flags = OO_POS_NEW | OO_ORIENT_NEW;
1134
1135         // if its a small ship, add weapon link info
1136         if((sip != NULL) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_STEALTH))){
1137                 // primary bank 0 or 1
1138                 if(shipp->weapons.current_primary_bank > 0){
1139                         oo_flags |= OO_PRIMARY_BANK;
1140                 }
1141
1142                 // linked or not
1143                 if(shipp->flags & SF_PRIMARY_LINKED){
1144                         oo_flags |= OO_PRIMARY_LINKED;
1145                 }
1146
1147                 // trigger down or not
1148                 if(shipp->flags & SF_TRIGGER_DOWN){
1149                         oo_flags |= OO_TRIGGER_DOWN;
1150                 }
1151         }       
1152                 
1153         // if the object's hull/shield timestamp has expired
1154         if((Ships[obj->instance].np_updates[player_index].status_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].status_update_stamp, OO_MAX_TIMESTAMP)){
1155                 oo_flags |= (OO_HULL_NEW);
1156
1157                 // reset the timestamp
1158                 multi_oo_reset_status_timestamp(obj, player_index);                     
1159         }
1160
1161         // if the object's hull/shield timestamp has expired
1162         if((Ships[obj->instance].np_updates[player_index].subsys_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].subsys_update_stamp, OO_MAX_TIMESTAMP)){
1163                 oo_flags |= OO_SUBSYSTEMS_AND_AI_NEW;
1164
1165                 // reset the timestamp
1166                 multi_oo_reset_subsys_timestamp(obj, player_index);
1167         }
1168
1169         // add info for a targeted object
1170         if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
1171                 oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW);
1172         }
1173         // all other cases
1174         else {                                  
1175                 // add info which is contingent upon being "in front"                   
1176                 if(in_cone){
1177                         oo_flags |= OO_ORIENT_NEW;
1178                 }                                               
1179         }               
1180
1181         // get current position and orient checksums            
1182         cur_pos_chksum = cf_add_chksum_short(cur_pos_chksum, (char*)(&obj->pos), sizeof(vector));
1183         cur_orient_chksum = cf_add_chksum_short(cur_orient_chksum, (char*)(&obj->orient), sizeof(matrix));
1184
1185         // if position or orientation haven't changed   
1186         if((shipp->np_updates[player_index].pos_chksum != 0) && (shipp->np_updates[player_index].pos_chksum == cur_pos_chksum)){
1187                 // if we otherwise would have been sending it, keep track of it (debug only)
1188 #ifndef NDEBUG
1189                 if(oo_flags & OO_POS_NEW){
1190                         multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE);
1191                 }               
1192 #endif
1193                 oo_flags &= ~(OO_POS_NEW);
1194         }
1195         if((shipp->np_updates[player_index].orient_chksum != 0) && (shipp->np_updates[player_index].orient_chksum == cur_orient_chksum)){
1196                 // if we otherwise would have been sending it, keep track of it (debug only)
1197 #ifndef NDEBUG
1198                 if(oo_flags & OO_ORIENT_NEW){
1199                         multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE);
1200                 }               
1201 #endif
1202                 oo_flags &= ~(OO_ORIENT_NEW);
1203         }
1204         shipp->np_updates[player_index].pos_chksum = cur_pos_chksum;
1205         shipp->np_updates[player_index].orient_chksum = cur_orient_chksum;
1206
1207         // pack stuff only if we have to        
1208         int packed = multi_oo_pack_data(pl, obj, oo_flags ,data);       
1209
1210         // increment sequence #
1211         Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++;
1212
1213         // bytes packed
1214         return packed;
1215 }
1216
1217 // process all other objects for this player
1218 void multi_oo_process_all(net_player *pl)
1219 {
1220         ubyte data[MAX_PACKET_SIZE];
1221         ubyte data_add[MAX_PACKET_SIZE];
1222         ubyte stop;
1223         int add_size;   
1224         int packet_size = 0;
1225         int idx;
1226                 
1227         object *moveup;
1228         object *pobj;   
1229
1230         // if the player has an invalid objnum..
1231         if(pl->player->objnum < 0){
1232                 return;
1233         }
1234
1235         // get the player's object
1236         pobj = &Objects[pl->player->objnum];
1237
1238         object *targ_obj;       
1239
1240         // build the list of ships to check against
1241         multi_oo_build_ship_list(pl);
1242
1243         // do nothing if he has no object targeted, or if he has a weapon targeted
1244         if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1245                 // build the header
1246                 BUILD_HEADER(OBJECT_UPDATE);            
1247         
1248                 // get a pointer to the object
1249                 targ_obj = &Objects[pl->s_info.target_objnum];
1250         
1251                 // run through the maybe_update function
1252                 add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
1253
1254                 // copy in any relevant data
1255                 if(add_size){
1256                         stop = 0xff;                    
1257                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1258                         ADD_DATA(stop);
1259
1260                         memcpy(data + packet_size, data_add, add_size);
1261                         packet_size += add_size;                
1262                 }
1263         } else {
1264                 // just build the header for the rest of the function
1265                 BUILD_HEADER(OBJECT_UPDATE);            
1266         }
1267                 
1268         idx = 0;
1269         while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1270                 // if this guy is over his datarate limit, do nothing
1271                 if(multi_oo_rate_exceeded(pl)){
1272                         nprintf(("Network","Capping client\n"));
1273                         idx++;
1274
1275                         continue;
1276                 }                       
1277
1278                 // get the object
1279                 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1280
1281                 // maybe send some info         
1282                 add_size = multi_oo_maybe_update(pl, moveup, data_add);
1283
1284                 // if this data is too much for the packet, send off what we currently have and start over
1285                 if(packet_size + add_size > OO_MAX_SIZE){
1286                         stop = 0x00;                    
1287                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1288                         ADD_DATA(stop);
1289                                                                         
1290                         multi_io_send(pl, data, packet_size);
1291                         pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1292
1293                         packet_size = 0;
1294                         BUILD_HEADER(OBJECT_UPDATE);                    
1295                 }
1296
1297                 if(add_size){
1298                         stop = 0xff;                    
1299                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1300                         ADD_DATA(stop);
1301
1302                         // copy in the data
1303                         memcpy(data + packet_size,data_add,add_size);
1304                         packet_size += add_size;
1305                 }
1306
1307                 // next ship
1308                 idx++;
1309         }
1310
1311         // if we have anything more than 3 byte in the packet, send the last one off
1312         if(packet_size > 3){
1313                 stop = 0x00;            
1314                 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1315                 ADD_DATA(stop);
1316                                                                 
1317                 multi_io_send(pl, data, packet_size);
1318                 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1319         }
1320 }
1321
1322 // process all object update details for this frame
1323 void multi_oo_process()
1324 {
1325         int idx;        
1326         
1327         // process each player
1328         for(idx=0; idx<MAX_PLAYERS; idx++){
1329                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) /*&& !MULTI_OBSERVER(Net_players[idx])*/ ){
1330                         // now process the rest of the objects
1331                         multi_oo_process_all(&Net_players[idx]);
1332
1333                         // do firing stuff for this player
1334                         if((Net_players[idx].player != NULL) && (Net_players[idx].player->objnum >= 0) && !(Net_players[idx].flags & NETINFO_FLAG_LIMBO) && !(Net_players[idx].flags & NETINFO_FLAG_RESPAWNING)){
1335                                 if((Objects[Net_players[idx].player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].player->objnum].flags & OF_SHOULD_BE_DEAD)){
1336                                         obj_player_fire_stuff( &Objects[Net_players[idx].player->objnum], Net_players[idx].player->ci );
1337                                 }
1338                         }
1339                 }
1340         }
1341 }
1342
1343 // process incoming object update data
1344 void multi_oo_process_update(ubyte *data, header *hinfo)
1345 {       
1346         ubyte stop;     
1347         int player_index;       
1348         int offset = HEADER_LENGTH;
1349         net_player *pl = NULL;  
1350
1351         // if this is processed on the server, its a client object update packet
1352         player_index = -1;
1353         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1354                 // determine what player this came from 
1355                 player_index = find_player_id(hinfo->id);
1356                 if(player_index != -1){                                         
1357                         pl = &Net_players[player_index];
1358                 } else {                        
1359                         pl = NULL;
1360                 }
1361         }
1362         // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
1363         else {                                          
1364                 pl = Net_player;
1365         }
1366
1367         GET_DATA(stop);
1368         
1369         while(stop == 0xff){
1370                 // process the data
1371                 offset += multi_oo_unpack_data(pl, data + offset);
1372
1373                 GET_DATA(stop);
1374         }
1375         PACKET_SET_SIZE();
1376 }
1377
1378 // initialize all object update timestamps (call whenever entering gameplay state)
1379 void multi_oo_gameplay_init()
1380 {
1381         int cur, s_idx, idx;
1382         //ship_obj *so;                         
1383         ship *shipp;
1384         /*
1385         int num_ships = ship_get_num_ships();
1386
1387         if(num_ships <= 0){
1388                 return;
1389         }
1390         split = 3000 / num_ships;
1391         */
1392         //split = 1;
1393         //split = 150;
1394
1395         // server should setup initial update timestamps        
1396         // stagger initial updates over 3 seconds or so
1397         cur = 0;
1398         //for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1399                 //if(Objects[so->objnum].type == OBJ_SHIP){
1400         for(s_idx=0; s_idx<MAX_SHIPS; s_idx++){
1401                         shipp = &Ships[s_idx];
1402                 
1403                         // update the timestamps
1404                         for(idx=0;idx<MAX_PLAYERS;idx++){
1405                                 shipp->np_updates[idx].update_stamp = timestamp(cur);
1406                                 shipp->np_updates[idx].status_update_stamp = timestamp(cur);
1407                                 shipp->np_updates[idx].subsys_update_stamp = timestamp(cur);
1408                                 shipp->np_updates[idx].seq = 0;         
1409                                 shipp->np_updates[idx].pos_chksum = 0;
1410                                 shipp->np_updates[idx].orient_chksum = 0;
1411                         } 
1412                         
1413                         oo_arrive_time_count[shipp - Ships] = 0;                        
1414                         oo_interp_count[shipp - Ships] = 0;
1415
1416                         // increment the time
1417 //                      cur += split;                   
1418                 }
1419         //}                     
1420
1421         // reset datarate stamp now
1422         extern int OO_gran;
1423         for(idx=0; idx<MAX_PLAYERS; idx++){
1424                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1425         }
1426 }
1427
1428 // send control info for a client (which is basically a "reverse" object update)
1429 void multi_oo_send_control_info()
1430 {
1431         ubyte data[MAX_PACKET_SIZE], stop;
1432         ubyte data_add[MAX_PACKET_SIZE];
1433         ubyte oo_flags; 
1434         int add_size;
1435         int packet_size = 0;
1436
1437         // if I'm dying or my object type is not a ship, bail here
1438         if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1439                 return;
1440         }       
1441         
1442         // build the header
1443         BUILD_HEADER(OBJECT_UPDATE);            
1444
1445         // pos and orient always
1446         oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);                
1447
1448         // pack the appropriate info into the data
1449         add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1450
1451         // copy in any relevant data
1452         if(add_size){
1453                 stop = 0xff;            
1454                 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1455                 
1456                 ADD_DATA(stop);
1457
1458                 memcpy(data + packet_size, data_add, add_size);
1459                 packet_size += add_size;                
1460         }
1461
1462         // add the final stop byte
1463         stop = 0x0;     
1464         multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1465         ADD_DATA(stop);
1466
1467         // increment sequence #
1468         Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
1469
1470         // send to the server
1471         if(Netgame.server != NULL){                                                             
1472                 multi_io_send(Net_player, data, packet_size);
1473         }
1474 }
1475
1476 // display any oo info on the hud
1477 void multi_oo_display()
1478 {
1479 #ifndef NDEBUG  
1480 #endif
1481 }
1482
1483
1484 // ---------------------------------------------------------------------------------------------------
1485 // DATARATE DEFINES/VARS
1486 //
1487
1488 // low object update datarate limit
1489 #define OO_LIMIT_LOW                            1800
1490 #define OO_LIMIT_MED                            3400
1491
1492 // timestamp for sending control info (movement only - we'll send button info all the time)
1493 #define OO_CIRATE                                       85                                      // 15x a second
1494 int Multi_cirate_stamp                  = -1;                           // timestamp for waiting on control info time
1495 int Multi_cirate_can_send               = 1;                            // if we can send control info this frame
1496
1497 // global max rates
1498 int OO_server_rate = -1;                                                        // max _total_ bandwidth to send to all clients
1499 int OO_client_rate = -1;                                                        // max bandwidth to go to an individual client
1500
1501 // update timestamp for server datarate checking
1502 #define RATE_UPDATE_TIME                1250                            // in ms
1503 int OO_server_rate_stamp = -1;
1504
1505 // bandwidth granularity
1506 int OO_gran = 1;
1507 DCF(oog, "")
1508 {
1509         dc_get_arg(ARG_INT);
1510         OO_gran = Dc_arg_int;
1511 }
1512
1513 // process datarate limiting stuff for the server
1514 void multi_oo_server_process();
1515
1516 // process datarate limiting stuff for the client
1517 void multi_oo_client_process();
1518
1519 // update the server datarate
1520 void multi_oo_update_server_rate();
1521
1522
1523 // ---------------------------------------------------------------------------------------------------
1524 // DATARATE FUNCTIONS
1525 //
1526
1527 // process all object update datarate details
1528 void multi_oo_rate_process()
1529 {
1530         // if I have no valid player, drop out here
1531         if(Net_player == NULL){
1532                 return;
1533         }
1534
1535         // if we're not in mission, don't do anything
1536         if(!(Game_mode & GM_IN_MISSION)){
1537                 return;
1538         }
1539
1540         // if I'm the server of a game, process server stuff
1541         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1542                 multi_oo_server_process();
1543         }
1544         // otherwise process client-side stuff
1545         else {
1546                 multi_oo_client_process();
1547         }
1548 }
1549
1550 // process datarate limiting stuff for the server
1551 void multi_oo_server_process()
1552 {
1553         int idx;
1554         
1555         // go through all players
1556         for(idx=0;idx<MAX_PLAYERS;idx++){
1557                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1558                         // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1559                         if((Net_players[idx].s_info.rate_stamp == -1) || timestamp_elapsed_safe(Net_players[idx].s_info.rate_stamp, OO_MAX_TIMESTAMP) || (abs(timestamp_ticker - Net_players[idx].s_info.rate_stamp) >= (int)(1000.0f / (float)OO_gran)) ){
1560                                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1561                                 Net_players[idx].s_info.rate_bytes = 0;
1562                         }
1563                 }
1564         }
1565
1566         // determine if we should be updating the server datarate
1567         if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){
1568                 // reset the timestamp
1569                 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1570
1571                 // update the server datarate
1572                 multi_oo_update_server_rate();
1573
1574                 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1575         }       
1576 }
1577
1578 // process datarate limiting stuff for the client
1579 void multi_oo_client_process()
1580 {
1581         // if the timestamp is -1 or has elapsed, reset it
1582         if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){
1583                 Multi_cirate_can_send = 1;
1584                 Multi_cirate_stamp = timestamp(OO_CIRATE);
1585         }       
1586 }
1587
1588
1589 // datarate limiting system for server -------------------------------------
1590
1591 // initialize the rate limiting system for all players
1592 void multi_oo_rate_init_all()
1593 {
1594         int idx;
1595
1596         // if I don't have a net_player, bail here
1597         if(Net_player == NULL){
1598                 return;
1599         }
1600
1601         // if I'm the server of the game
1602         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
1603                 // go through all players
1604                 for(idx=0;idx<MAX_PLAYERS;idx++){
1605                         if(MULTI_CONNECTED(Net_players[idx])){
1606                                 multi_oo_rate_init(&Net_players[idx]);
1607                         }
1608                 }
1609
1610                 OO_server_rate_stamp = -1;
1611         }
1612         // if i'm the client, initialize my control info datarate stuff
1613         else {
1614                 Multi_cirate_stamp = -1;
1615                 Multi_cirate_can_send = 1;
1616         }
1617 }
1618
1619 // initialize the rate limiting for the passed in player
1620 void multi_oo_rate_init(net_player *pl)
1621 {
1622         // reinitialize his datarate timestamp
1623         pl->s_info.rate_stamp = -1;
1624         pl->s_info.rate_bytes = 0;
1625 }
1626
1627 // if the given net-player has exceeded his datarate limit
1628 int multi_oo_rate_exceeded(net_player *pl)
1629 {
1630         int rate_compare;
1631                 
1632         // check against the guy's object update level
1633         switch(pl->p_info.options.obj_update_level){
1634         // low update level
1635         case OBJ_UPDATE_LOW:
1636                 // the low object update limit
1637                 rate_compare = OO_LIMIT_LOW;
1638                 break;
1639
1640         // medium update level
1641         case OBJ_UPDATE_MEDIUM:         
1642                 // the low object update limit
1643                 rate_compare = OO_LIMIT_MED;
1644                 break;
1645
1646         // high update level - super high datarate (no capping, just intelligent updating)
1647         case OBJ_UPDATE_HIGH:
1648                 rate_compare = 100000000;
1649                 break;
1650
1651         // LAN - no rate max
1652         case OBJ_UPDATE_LAN:
1653                 return 0;
1654
1655         // default level
1656         default:
1657                 Int3();
1658                 rate_compare = OO_LIMIT_LOW;
1659                 break;
1660         }
1661
1662         // if the server global rate PER CLIENT (OO_client_rate) is actually lower
1663         if(OO_client_rate < rate_compare){
1664                 rate_compare = OO_client_rate;
1665         }
1666
1667         // compare his bytes sent against the allowable amount
1668         if(pl->s_info.rate_bytes >= rate_compare){
1669                 return 1;
1670         }
1671
1672         // we're allowed to send
1673         return 0;
1674 }
1675
1676 // if it is ok for me to send a control info (will be ~N times a second)
1677 int multi_oo_cirate_can_send()
1678 {
1679         // if we're allowed to send
1680         if(Multi_cirate_can_send){
1681                 Multi_cirate_can_send = 0;
1682                 return 1;
1683         } 
1684         
1685         return 0;               
1686 }
1687
1688 // dynamically update the server capped bandwidth rate
1689 void multi_oo_update_server_rate()
1690 {       
1691         int num_connections;    
1692         
1693         // bail conditions
1694         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1695                 return;
1696         }
1697
1698         // get the # of connections
1699         num_connections = multi_num_connections();
1700         if(!(Game_mode & GM_STANDALONE_SERVER)){
1701                 num_connections--;
1702         }
1703         // make sure we always pretend there's at least one guy available
1704         if(num_connections <= 0){
1705                 num_connections = 1;
1706         }
1707                 
1708         // set the data rate    
1709         switch(Net_player->p_info.options.obj_update_level){
1710         // LAN update level
1711         case OBJ_UPDATE_LAN:
1712                 // set to something super big so we don't limit anything
1713                 OO_server_rate = 500000000;
1714                 break;
1715
1716         // high update level
1717         case OBJ_UPDATE_HIGH:
1718                 // set to 0 so we don't limit anything
1719                 OO_server_rate = Multi_options_g.datarate_cap;
1720                 break;
1721
1722         // medium update level
1723         case OBJ_UPDATE_MEDIUM:
1724                 // set the rate to be "medium" update level
1725                 OO_server_rate = OO_LIMIT_MED;
1726                 break;
1727
1728         // low update level 
1729         case OBJ_UPDATE_LOW:
1730                 // set the rate to be the "low" update level
1731                 OO_server_rate = OO_LIMIT_LOW;
1732                 break;
1733
1734         default:
1735                 Int3();
1736                 return;
1737         }       
1738
1739         // set the individual client level
1740         OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
1741 }
1742
1743 // reset all sequencing info (obsolete for new object update stuff)
1744 void multi_oo_reset_sequencing()
1745 {               
1746 }
1747
1748 // is this object one which needs to go through the interpolation
1749 int multi_oo_is_interp_object(object *objp)
1750 {       
1751         // if not multiplayer, skip it
1752         if(!(Game_mode & GM_MULTIPLAYER)){
1753                 return 0;
1754         }
1755
1756         // if its not a ship, skip it
1757         if(objp->type != OBJ_SHIP){
1758                 return 0;
1759         }
1760
1761         // other bogus cases
1762         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1763                 return 0;
1764         }
1765
1766         // if I'm a client and this is not me, I need to interp it
1767         if(!MULTIPLAYER_MASTER){
1768                 if(objp != Player_obj){
1769                         return 1;
1770                 } else {
1771                         return 0;
1772                 }
1773         }
1774
1775         // servers only interpolate other player ships
1776         if(!(objp->flags & OF_PLAYER_SHIP)){
1777                 return 0;
1778         }
1779
1780         // here we know its a player ship - is it mine?
1781         if(objp == Player_obj){
1782                 return 0;
1783         }
1784
1785         // interp it
1786         return 1;
1787 }
1788
1789 // interp
1790 void multi_oo_interp(object *objp)
1791 {               
1792         // make sure its a valid ship
1793         Assert(Game_mode & GM_MULTIPLAYER);
1794         if(objp->type != OBJ_SHIP){
1795                 return;
1796         }
1797         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1798                 return;
1799         }       
1800
1801         // increment his approx "next" time
1802         oo_arrive_time_next[objp->instance] += flFrametime;
1803
1804         // do stream weapon firing for this ship
1805         Assert(objp != Player_obj);
1806         if(objp != Player_obj){
1807                 ship_fire_primary(objp, 1, 0);
1808         }
1809
1810         // if this ship doesn't have enough data points yet, skip it
1811         if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){
1812                 return;
1813         }
1814
1815         // store the magnitude of his velocity
1816         // float vel_mag = vm_vec_mag(&objp->phys_info.vel);
1817
1818         // determine how far along we are (0.0 to 1.0) until we should be getting the next packet
1819         float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance];
1820
1821         // gr_set_color_fast(&Color_bright);
1822         // gr_printf(100, 10, "%f\n", t);
1823         
1824         // we've overshot. hmm. just keep the sim running I guess       
1825         if(t > 1.0f){
1826                 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1827                 return;
1828         }       
1829
1830         // otherwise, blend the two curves together to get the new point
1831         float u = 0.5f + (t * 0.5f);
1832         vector p_bad, p_good;
1833         oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u);
1834         oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u);         
1835         vm_vec_scale(&p_good, t);
1836         vm_vec_scale(&p_bad, 1.0f - t);
1837         vm_vec_add(&objp->pos, &p_bad, &p_good);        
1838
1839         // set new velocity
1840         // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
1841         
1842         // run the sim for rotation     
1843         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1844
1845         // blend velocity vectors together with an average weight
1846         /*
1847         vector v_bad, v_good;
1848         oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0);
1849         oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0);     
1850
1851         // t -= 1.0f;
1852         vm_vec_scale(&v_good, t);
1853         vm_vec_scale(&v_bad, 1.0f - t);
1854         vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good);
1855         
1856         // run the sim
1857         physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1858         */
1859
1860         /*
1861         vector v_bad, v_good;
1862         oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0);
1863         oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0);     
1864
1865         // t -= 1.0f;
1866         vm_vec_scale(&v_good, t);
1867         vm_vec_scale(&v_bad, 1.0f - t);
1868         vm_vec_avg(&objp->pos, &v_bad, &v_good);        
1869         
1870         // run the sim
1871         // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1872         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1873         */
1874 }
1875
1876 float oo_error = 0.8f;
1877 DCF(oo_error, "")
1878 {
1879         dc_get_arg(ARG_FLOAT);
1880         oo_error = Dc_arg_float;
1881 }
1882
1883 void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info)
1884 {
1885         vector a, b, c;
1886         // vector da, db, dc;
1887         matrix m_copy;
1888         physics_info p_copy;
1889         vector *pts[3] = {&a, &b, &c};  
1890         // vector *d_pts[3] = {&da, &db, &dc};
1891         
1892         // average time between packets
1893         float avg_diff = oo_arrive_time_avg_diff[ship_index];   
1894
1895         // would this cause us to rubber-band?
1896         vector v_norm = cur_phys_info->vel;     
1897         vector v_dir;
1898         vm_vec_sub(&v_dir, new_pos, cur_pos);   
1899         if(!IS_VEC_NULL(&v_norm) && !IS_VEC_NULL(&v_dir)){
1900                 vm_vec_normalize(&v_dir);
1901                 vm_vec_normalize(&v_norm);      
1902                 if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){
1903                         *new_pos = *cur_pos;
1904                 }
1905         }
1906         
1907         // get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether
1908         a = oo_interp_points[ship_index][0];
1909         b = *cur_pos;
1910         c = *cur_pos;
1911         m_copy = *cur_orient;
1912         p_copy = *cur_phys_info;
1913         physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error);                 // next point, assuming we followed our current path
1914         oo_interp_splines[ship_index][0].bez_set_points(3, pts);
1915
1916         // get the spline representing where this new point tells us we'd be heading
1917         a = oo_interp_points[ship_index][0];
1918         b = oo_interp_points[ship_index][1];
1919         c = oo_interp_points[ship_index][1];
1920         m_copy = *new_orient;
1921         p_copy = *new_phys_info;
1922         physics_sim(&c, &m_copy, &p_copy, avg_diff);                    // next point, given this new info
1923         oo_interp_splines[ship_index][1].bez_set_points(3, pts);        
1924
1925         // get the spline for our "bad" movement
1926         /*
1927         a = oo_interp_points[ship_index][0];
1928         b = *cur_pos;
1929         da = oo_interp_vel[ship_index][0];
1930         db = cur_phys_info->vel;
1931         oo_interp_splines[ship_index][0].herm_set_points(2, pts, d_pts);
1932
1933         // get the spline for our "good" movement
1934         a = oo_interp_points[ship_index][0];
1935         b = oo_interp_points[ship_index][1];
1936         da = oo_interp_vel[ship_index][0];
1937         db = oo_interp_vel[ship_index][1];
1938         oo_interp_splines[ship_index][1].herm_set_points(2, pts, d_pts);
1939         */
1940
1941         // now we've got a spline representing our "new" path and where we would've gone had we been perfect before
1942         // we'll modify our velocity to move along a blend of these splines.
1943 }
1944
1945 #include "alphacolors.h"
1946
1947 void oo_update_time()
1948 {       
1949 }
1950
1951 int display_oo_bez = 0;
1952 DCF(bez, "")
1953 {
1954         display_oo_bez = !display_oo_bez;
1955         if(display_oo_bez){
1956                 dc_printf("Showing interp splines");
1957         } else {
1958                 dc_printf("Not showing interp splines");
1959         }
1960 }
1961
1962 void oo_display()
1963 {
1964 /*      int idx;        
1965
1966
1967         gr_set_color_fast(&Color_bright);
1968
1969         for(idx=0; idx<MAX_SHIPS; idx++){               
1970                 // invalid ship
1971                 if(Ships[idx].objnum < 0){
1972                         continue;
1973                 }
1974
1975                 // time between updates
1976                 if( (oo_arrive_time_count[idx] == 5) && (idx != (Player_ship - Ships)) ){
1977                         gr_printf(20, 40, "avg time between updates : %f", oo_arrive_time_avg_diff[idx]);                       
1978                 }                       
1979                 
1980                 // interpolation splines
1981                 if( (oo_interp_count[idx] == 2) && (display_oo_bez) ){
1982                         oo_interp_splines[idx][0].bez_render(10, &Color_bright_red);                    // bad path
1983                         oo_interp_splines[idx][1].bez_render(10, &Color_bright_green);          // good path
1984                 }
1985         }
1986         */
1987 }