8 #include "multi_rate.h"
14 #include "alphacolors.h"
15 #include "afterburner.h"
17 // ---------------------------------------------------------------------------------------------------
18 // OBJECT UPDATE DEFINES/VARS
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
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);
33 // how much data we're willing to put into a given oo packet
34 #define OO_MAX_SIZE 480
36 // tolerance for bashing position
37 #define OO_POS_UPDATE_TOLERANCE 100.0f
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
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
52 // no timestamp should ever have sat for longer than this.
53 #define OO_MAX_TIMESTAMP 2500
57 #define OO_NEAR_DIST (200.0f)
59 #define OO_MIDRANGE_DIST (600.0f)
61 #define OO_FAR_DIST (1400.0f)
63 // how often we should send full hull/shield updates
64 #define OO_HULL_SHIELD_TIME 600
65 #define OO_SUBSYS_TIME 1000
67 // timestamp values for object update times based on client's update level.
68 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] =
77 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
86 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
95 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
104 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
107 200, // medium update
113 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
116 600, // medium update
122 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
125 1500, // medium update
130 // ship index list for possibly sorting ships based upon distance, etc
131 short OO_ship_index[MAX_SHIPS];
133 int OO_update_index = -1; // index into OO_update_records for displaying update record info
135 // ---------------------------------------------------------------------------------------------------
136 // OBJECT UPDATE FUNCTIONS
139 object *OO_player_obj;
142 int multi_oo_sort_func(const void *ship1, const void *ship2)
145 short index1, index2;
152 memcpy(&index1, ship1, sizeof(short));
153 memcpy(&index2, ship2, sizeof(short));
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)){
161 obj1 = &Objects[Ships[index1].objnum];
162 obj2 = &Objects[Ships[index2].objnum];
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);
172 // objects in front take precedence
173 if((dot1 < 0.0f) && (dot2 >= 0.0f)){
175 } else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
179 // otherwise go by distance
180 return (dist1 <= dist2) ? -1 : 1;
183 // build the list of ship indices to use when updating for this player
184 void multi_oo_build_ship_list(net_player *pl)
191 // set all indices to be -1
192 for(idx = 0;idx<MAX_SHIPS; idx++){
193 OO_ship_index[idx] = -1;
196 // get the player object
197 if(pl->player->objnum < 0){
200 player_obj = &Objects[pl->player->objnum];
202 // go through all other relevant objects
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)){
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)) ){
215 // must be a ship, a weapon, and _not_ an observer
216 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
220 // don't send info for dying ships
221 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
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)){
230 // don't send him info for himself
231 if ( &Objects[moveup->objnum] == player_obj ){
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)){
241 if(ship_index < MAX_SHIPS){
242 OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
246 // maybe qsort the thing here
247 OO_player_obj = player_obj;
249 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
253 // pack information for a client (myself), return bytes added
254 int multi_oo_pack_client_data(ubyte *data)
257 ushort tnet_signature;
258 char t_subsys, l_subsys;
261 // get our firing stuff
262 out_flags = Net_player->s_info.accum_buttons;
264 // zero these values for now
265 Net_player->s_info.accum_buttons = 0;
267 // add any necessary targeting flags
268 if ( Player_ai->current_target_is_locked ){
269 out_flags |= OOC_TARGET_LOCKED;
271 if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){
272 out_flags |= OOC_TARGET_SEEK_LOCK;
274 if ( Player->locking_on_center ){
275 out_flags |= OOC_LOCKING_ON_CENTER;
277 if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){
278 out_flags |= OOC_TRIGGER_DOWN;
282 if(Player_ship != NULL){
283 if(Player_ship->weapons.current_primary_bank > 0){
284 out_flags |= OOC_PRIMARY_BANK;
288 if(Player_ship->flags & SF_PRIMARY_LINKED){
289 out_flags |= OOC_PRIMARY_LINKED;
293 // copy the final flags in
294 memcpy(data, &out_flags, sizeof(ubyte));
297 // client targeting information
301 // if nothing targeted
302 if(Player_ai->target_objnum == -1){
305 // if something is targeted
307 // target net signature
308 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
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 );
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 );
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);
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)
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];
360 // no flags -> do nothing
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)));
373 // Assert(oo_flags & OO_POS_NEW);
377 if(MULTIPLAYER_MASTER){
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);
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 );
394 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
396 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
400 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
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);
408 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
410 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
414 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
418 percent = (char)(objp->phys_info.forward_thrust * 100.0f);
419 Assert( percent <= 100 );
421 memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
425 multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1);
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);
432 multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1);
434 // pack 2 shield values into each byte
437 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
441 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
445 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
449 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
452 multi_rate_add(NET_PLAYER_NUM(pl), "shl", 4);
456 if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){
458 ship_subsys *subsysp;
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){
463 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
466 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
468 // add the # of subsystems, and their data
470 ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
471 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
473 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
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;
480 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
485 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
486 short submode = (short)(Ai_info[shipp->ai_index].submode);
487 ushort target_signature;
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;
494 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
496 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
498 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));
501 multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5);
503 // primary weapon energy
504 temp = shipp->weapon_energy / sip->max_weapon_reserve;
509 oo_flags &= ~PF_AFTERBURNER_ON;
510 if(objp->phys_info.flags & PF_AFTERBURNER_ON){
511 oo_flags |= OO_AFTERBURNER_NEW;
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)){
521 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
523 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].ai_flags, sizeof(int));
525 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].mode, sizeof(int));
527 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].submode, sizeof(int));
529 if((Ai_info[shipp->ai_index].dock_objnum < 0) || (Ai_info[shipp->ai_index].dock_objnum >= MAX_OBJECTS)){
532 dock_sig = Objects[Ai_info[shipp->ai_index].dock_objnum].net_signature;
534 memcpy(data + packet_size + header_bytes, &dock_sig, sizeof(ushort));
538 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
542 // make sure we have a valid chunk of data
543 Assert(packet_size < 255);
544 if(packet_size >= 255){
547 data_size = (ubyte)packet_size;
549 // add the object's net signature, type and oo_flags
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 );
556 multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1);
557 ADD_DATA( oo_flags );
559 multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1);
560 ADD_DATA( data_size );
562 multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1);
563 ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq );
565 packet_size += data_size;
567 // copy to the outgoing data
568 memcpy(data_out, data, packet_size);
573 // unpack information for a client , return bytes processed
574 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
580 memcpy(&in_flags, data, sizeof(ubyte));
583 // get the player ship
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];
589 // if we have a valid netplayer pointer
590 if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){
592 pl->player->ci.fire_primary_count = 0;
595 pl->player->ci.fire_secondary_count = 0;
596 if ( in_flags & OOC_FIRE_SECONDARY ){
597 pl->player->ci.fire_secondary_count = 1;
600 // countermeasure fired
601 pl->player->ci.fire_countermeasure_count = 0;
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;
609 // trigger down, bank info
611 if(in_flags & OOC_TRIGGER_DOWN){
612 shipp->flags |= SF_TRIGGER_DOWN;
614 shipp->flags &= ~SF_TRIGGER_DOWN;
617 if(in_flags & OOC_PRIMARY_BANK){
618 shipp->weapons.current_primary_bank = 1;
620 shipp->weapons.current_primary_bank = 0;
624 shipp->flags &= ~SF_PRIMARY_LINKED;
625 if(in_flags & OOC_PRIMARY_LINKED){
626 shipp->flags |= SF_PRIMARY_LINKED;
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;
636 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
641 // client targeting information
643 char t_subsys,l_subsys;
651 // try and find the targeted object
654 tobj = multi_get_network_object( tnet_sig );
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);
662 pl->s_info.target_objnum = OBJ_INDEX(tobj);
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);
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);
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)
690 ubyte data_size, oo_flags;
697 // add the object's net signature, type and oo_flags
698 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
700 GET_DATA( oo_flags );
702 // clients always pos and orient stuff only
704 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
706 GET_DATA( data_size );
709 // try and find the object
710 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
711 pobjp = multi_get_network_object(net_sig);
713 if((pl != NULL) && (pl->player->objnum != -1)){
714 pobjp = &Objects[pl->player->objnum];
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)){
728 shipp = &Ships[pobjp->instance];
729 sip = &Ship_info[shipp->ship_info_index];
731 // ---------------------------------------------------------------------------------------------------------------
732 // CRITICAL OBJECT UPDATE SHIZ
733 // ---------------------------------------------------------------------------------------------------------------
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){
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);
750 vector new_pos = pobjp->pos;
751 physics_info new_phys_info = pobjp->phys_info;
752 matrix new_orient = pobjp->orient;
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);
762 oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime);
764 // if we've got 5 elements calculate the average
765 if(oo_arrive_time_count[shipp - Ships] == 5){
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];
771 oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f;
773 // next expected arrival time
774 oo_arrive_time_next[shipp - Ships] = 0.0f;
776 // int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos );
777 int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos );
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 );
784 // bash desired vel to be velocity
785 // pobjp->phys_info.desired_vel = pobjp->phys_info.vel;
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 );
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 );
798 // bash desired rotvel to be 0
799 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
803 percent = (char)(pobjp->phys_info.forward_thrust * 100.0f);
804 Assert( percent <= 100 );
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;
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;
820 oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1];
821 oo_interp_points[shipp - Ships][1] = new_pos;
823 multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info);
826 pobjp->phys_info.vel = new_phys_info.vel;
827 pobjp->phys_info.desired_vel = new_phys_info.vel;
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;
839 // ---------------------------------------------------------------------------------------------------------------
840 // ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE!
841 // ---------------------------------------------------------------------------------------------------------------
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;
848 float shield_0, shield_1, shield_2, shield_3;
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);
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;
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;
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] );
875 // fill in the subsystem data
877 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
880 val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
881 subsysp->current_hits = val;
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;
889 // if we've reached max subsystems for some reason, bail out
890 if(subsys_count >= n_subsystems){
895 // recalculate all ship subsystems
896 ship_recalc_subsys_strength( shipp );
901 ushort target_signature;
906 GET_DATA( target_signature );
908 if(shipp->ai_index >= 0){
909 Ai_info[shipp->ai_index].mode = umode;
910 Ai_info[shipp->ai_index].submode = submode;
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;
917 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
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;
927 // support ship extra info
929 GET_DATA(support_extra);
932 int ai_flags, ai_mode, ai_submode;
937 GET_DATA(ai_submode);
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;
946 object *objp = multi_get_network_object( dock_sig );
948 Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp);
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);
960 // maybe turn them off
961 if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
962 afterburners_stop(pobjp);
966 // primary info (only clients care about this)
967 if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
969 if(oo_flags & OO_PRIMARY_BANK){
970 shipp->weapons.current_primary_bank = 1;
972 shipp->weapons.current_primary_bank = 0;
976 shipp->flags &= ~SF_PRIMARY_LINKED;
977 if(oo_flags & OO_PRIMARY_LINKED){
978 shipp->flags |= SF_PRIMARY_LINKED;
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;
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;
994 // update the sequence #
995 shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
997 // flag the object as just updated
998 // pobjp->flags |= OF_JUST_UPDATED;
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)
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];
1012 // reset the timestamp appropriately
1014 // base it upon range
1017 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1021 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1025 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1029 // base it upon range
1032 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1036 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1040 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
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);
1052 // reset the timestamp appropriately for the passed in object
1053 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1055 Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
1058 // reset the timestamp appropriately for the passed in object
1059 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1061 Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
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)
1072 float eye_dot, dist;
1078 ushort cur_pos_chksum = 0;
1079 ushort cur_orient_chksum = 0;
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)){
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;
1095 // stamp hasn't popped yet
1096 if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){
1100 // if we're supposed to update this guy
1102 // get the ship pointer
1103 shipp = &Ships[obj->instance];
1105 // get ship info pointer
1107 if(shipp->ship_info_index >= 0){
1108 sip = &Ship_info[shipp->ship_info_index];
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;
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){
1123 } else if(dist < OO_MIDRANGE_DIST){
1124 range = OO_MIDRANGE;
1129 // reset the timestamp for the next update for this guy
1130 multi_oo_reset_timestamp(pl, obj, range, in_cone);
1133 oo_flags = OO_POS_NEW | OO_ORIENT_NEW;
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;
1143 if(shipp->flags & SF_PRIMARY_LINKED){
1144 oo_flags |= OO_PRIMARY_LINKED;
1147 // trigger down or not
1148 if(shipp->flags & SF_TRIGGER_DOWN){
1149 oo_flags |= OO_TRIGGER_DOWN;
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);
1157 // reset the timestamp
1158 multi_oo_reset_status_timestamp(obj, player_index);
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;
1165 // reset the timestamp
1166 multi_oo_reset_subsys_timestamp(obj, player_index);
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);
1175 // add info which is contingent upon being "in front"
1177 oo_flags |= OO_ORIENT_NEW;
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));
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)
1189 if(oo_flags & OO_POS_NEW){
1190 multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE);
1193 oo_flags &= ~(OO_POS_NEW);
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)
1198 if(oo_flags & OO_ORIENT_NEW){
1199 multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE);
1202 oo_flags &= ~(OO_ORIENT_NEW);
1204 shipp->np_updates[player_index].pos_chksum = cur_pos_chksum;
1205 shipp->np_updates[player_index].orient_chksum = cur_orient_chksum;
1207 // pack stuff only if we have to
1208 int packed = multi_oo_pack_data(pl, obj, oo_flags ,data);
1210 // increment sequence #
1211 Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++;
1217 // process all other objects for this player
1218 void multi_oo_process_all(net_player *pl)
1220 ubyte data[MAX_PACKET_SIZE];
1221 ubyte data_add[MAX_PACKET_SIZE];
1224 int packet_size = 0;
1230 // if the player has an invalid objnum..
1231 if(pl->player->objnum < 0){
1235 // get the player's object
1236 pobj = &Objects[pl->player->objnum];
1240 // build the list of ships to check against
1241 multi_oo_build_ship_list(pl);
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)){
1246 BUILD_HEADER(OBJECT_UPDATE);
1248 // get a pointer to the object
1249 targ_obj = &Objects[pl->s_info.target_objnum];
1251 // run through the maybe_update function
1252 add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
1254 // copy in any relevant data
1257 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1260 memcpy(data + packet_size, data_add, add_size);
1261 packet_size += add_size;
1264 // just build the header for the rest of the function
1265 BUILD_HEADER(OBJECT_UPDATE);
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"));
1279 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1281 // maybe send some info
1282 add_size = multi_oo_maybe_update(pl, moveup, data_add);
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){
1287 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1290 multi_io_send(pl, data, packet_size);
1291 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1294 BUILD_HEADER(OBJECT_UPDATE);
1299 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1303 memcpy(data + packet_size,data_add,add_size);
1304 packet_size += add_size;
1311 // if we have anything more than 3 byte in the packet, send the last one off
1312 if(packet_size > 3){
1314 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1317 multi_io_send(pl, data, packet_size);
1318 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1322 // process all object update details for this frame
1323 void multi_oo_process()
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]);
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 );
1343 // process incoming object update data
1344 void multi_oo_process_update(ubyte *data, header *hinfo)
1348 int offset = HEADER_LENGTH;
1349 net_player *pl = NULL;
1351 // if this is processed on the server, its a client object update packet
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];
1362 // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
1369 while(stop == 0xff){
1371 offset += multi_oo_unpack_data(pl, data + offset);
1378 // initialize all object update timestamps (call whenever entering gameplay state)
1379 void multi_oo_gameplay_init()
1381 int cur, s_idx, idx;
1385 int num_ships = ship_get_num_ships();
1390 split = 3000 / num_ships;
1395 // server should setup initial update timestamps
1396 // stagger initial updates over 3 seconds or so
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];
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;
1413 oo_arrive_time_count[shipp - Ships] = 0;
1414 oo_interp_count[shipp - Ships] = 0;
1416 // increment the time
1421 // reset datarate stamp now
1423 for(idx=0; idx<MAX_PLAYERS; idx++){
1424 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1428 // send control info for a client (which is basically a "reverse" object update)
1429 void multi_oo_send_control_info()
1431 ubyte data[MAX_PACKET_SIZE], stop;
1432 ubyte data_add[MAX_PACKET_SIZE];
1435 int packet_size = 0;
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)){
1443 BUILD_HEADER(OBJECT_UPDATE);
1445 // pos and orient always
1446 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
1448 // pack the appropriate info into the data
1449 add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1451 // copy in any relevant data
1454 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1458 memcpy(data + packet_size, data_add, add_size);
1459 packet_size += add_size;
1462 // add the final stop byte
1464 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1467 // increment sequence #
1468 Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
1470 // send to the server
1471 if(Netgame.server != NULL){
1472 multi_io_send(Net_player, data, packet_size);
1476 // display any oo info on the hud
1477 void multi_oo_display()
1484 // ---------------------------------------------------------------------------------------------------
1485 // DATARATE DEFINES/VARS
1488 // low object update datarate limit
1489 #define OO_LIMIT_LOW 1800
1490 #define OO_LIMIT_MED 3400
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
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
1501 // update timestamp for server datarate checking
1502 #define RATE_UPDATE_TIME 1250 // in ms
1503 int OO_server_rate_stamp = -1;
1505 // bandwidth granularity
1509 dc_get_arg(ARG_INT);
1510 OO_gran = Dc_arg_int;
1513 // process datarate limiting stuff for the server
1514 void multi_oo_server_process();
1516 // process datarate limiting stuff for the client
1517 void multi_oo_client_process();
1519 // update the server datarate
1520 void multi_oo_update_server_rate();
1523 // ---------------------------------------------------------------------------------------------------
1524 // DATARATE FUNCTIONS
1527 // process all object update datarate details
1528 void multi_oo_rate_process()
1530 // if I have no valid player, drop out here
1531 if(Net_player == NULL){
1535 // if we're not in mission, don't do anything
1536 if(!(Game_mode & GM_IN_MISSION)){
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();
1544 // otherwise process client-side stuff
1546 multi_oo_client_process();
1550 // process datarate limiting stuff for the server
1551 void multi_oo_server_process()
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;
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);
1571 // update the server datarate
1572 multi_oo_update_server_rate();
1574 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1578 // process datarate limiting stuff for the client
1579 void multi_oo_client_process()
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);
1589 // datarate limiting system for server -------------------------------------
1591 // initialize the rate limiting system for all players
1592 void multi_oo_rate_init_all()
1596 // if I don't have a net_player, bail here
1597 if(Net_player == NULL){
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]);
1610 OO_server_rate_stamp = -1;
1612 // if i'm the client, initialize my control info datarate stuff
1614 Multi_cirate_stamp = -1;
1615 Multi_cirate_can_send = 1;
1619 // initialize the rate limiting for the passed in player
1620 void multi_oo_rate_init(net_player *pl)
1622 // reinitialize his datarate timestamp
1623 pl->s_info.rate_stamp = -1;
1624 pl->s_info.rate_bytes = 0;
1627 // if the given net-player has exceeded his datarate limit
1628 int multi_oo_rate_exceeded(net_player *pl)
1632 // check against the guy's object update level
1633 switch(pl->p_info.options.obj_update_level){
1635 case OBJ_UPDATE_LOW:
1636 // the low object update limit
1637 rate_compare = OO_LIMIT_LOW;
1640 // medium update level
1641 case OBJ_UPDATE_MEDIUM:
1642 // the low object update limit
1643 rate_compare = OO_LIMIT_MED;
1646 // high update level - super high datarate (no capping, just intelligent updating)
1647 case OBJ_UPDATE_HIGH:
1648 rate_compare = 100000000;
1651 // LAN - no rate max
1652 case OBJ_UPDATE_LAN:
1658 rate_compare = OO_LIMIT_LOW;
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;
1667 // compare his bytes sent against the allowable amount
1668 if(pl->s_info.rate_bytes >= rate_compare){
1672 // we're allowed to send
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()
1679 // if we're allowed to send
1680 if(Multi_cirate_can_send){
1681 Multi_cirate_can_send = 0;
1688 // dynamically update the server capped bandwidth rate
1689 void multi_oo_update_server_rate()
1691 int num_connections;
1694 if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1698 // get the # of connections
1699 num_connections = multi_num_connections();
1700 if(!(Game_mode & GM_STANDALONE_SERVER)){
1703 // make sure we always pretend there's at least one guy available
1704 if(num_connections <= 0){
1705 num_connections = 1;
1708 // set the data rate
1709 switch(Net_player->p_info.options.obj_update_level){
1711 case OBJ_UPDATE_LAN:
1712 // set to something super big so we don't limit anything
1713 OO_server_rate = 500000000;
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;
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;
1729 case OBJ_UPDATE_LOW:
1730 // set the rate to be the "low" update level
1731 OO_server_rate = OO_LIMIT_LOW;
1739 // set the individual client level
1740 OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
1743 // reset all sequencing info (obsolete for new object update stuff)
1744 void multi_oo_reset_sequencing()
1748 // is this object one which needs to go through the interpolation
1749 int multi_oo_is_interp_object(object *objp)
1751 // if not multiplayer, skip it
1752 if(!(Game_mode & GM_MULTIPLAYER)){
1756 // if its not a ship, skip it
1757 if(objp->type != OBJ_SHIP){
1761 // other bogus cases
1762 if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
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){
1775 // servers only interpolate other player ships
1776 if(!(objp->flags & OF_PLAYER_SHIP)){
1780 // here we know its a player ship - is it mine?
1781 if(objp == Player_obj){
1790 void multi_oo_interp(object *objp)
1792 // make sure its a valid ship
1793 Assert(Game_mode & GM_MULTIPLAYER);
1794 if(objp->type != OBJ_SHIP){
1797 if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1801 // increment his approx "next" time
1802 oo_arrive_time_next[objp->instance] += flFrametime;
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);
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)){
1815 // store the magnitude of his velocity
1816 // float vel_mag = vm_vec_mag(&objp->phys_info.vel);
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];
1821 // gr_set_color_fast(&Color_bright);
1822 // gr_printf(100, 10, "%f\n", t);
1824 // we've overshot. hmm. just keep the sim running I guess
1826 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
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);
1840 // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
1842 // run the sim for rotation
1843 physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1845 // blend velocity vectors together with an average weight
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);
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);
1857 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
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);
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);
1871 // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1872 physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1876 float oo_error = 0.8f;
1879 dc_get_arg(ARG_FLOAT);
1880 oo_error = Dc_arg_float;
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)
1886 // vector da, db, dc;
1888 physics_info p_copy;
1889 vector *pts[3] = {&a, &b, &c};
1890 // vector *d_pts[3] = {&da, &db, &dc};
1892 // average time between packets
1893 float avg_diff = oo_arrive_time_avg_diff[ship_index];
1895 // would this cause us to rubber-band?
1896 vector v_norm = cur_phys_info->vel;
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;
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];
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);
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);
1925 // get the spline for our "bad" movement
1927 a = oo_interp_points[ship_index][0];
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);
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);
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.
1945 #include "alphacolors.h"
1947 void oo_update_time()
1951 int display_oo_bez = 0;
1954 display_oo_bez = !display_oo_bez;
1956 dc_printf("Showing interp splines");
1958 dc_printf("Not showing interp splines");
1967 gr_set_color_fast(&Color_bright);
1969 for(idx=0; idx<MAX_SHIPS; idx++){
1971 if(Ships[idx].objnum < 0){
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]);
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