2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
13 #include "multimsgs.h"
14 #include "multiutil.h"
15 #include "multi_obj.h"
16 #include "multi_rate.h"
22 #include "alphacolors.h"
23 #include "afterburner.h"
25 // ---------------------------------------------------------------------------------------------------
26 // OBJECT UPDATE DEFINES/VARS
30 float oo_arrive_time[MAX_SHIPS][5]; // the last 5 arrival times for each ship
31 int oo_arrive_time_count[MAX_SHIPS]; // size of the arrival queue
32 float oo_arrive_time_avg_diff[MAX_SHIPS]; // the average time between arrivals
33 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
36 int oo_interp_count[MAX_SHIPS];
37 vector oo_interp_points[MAX_SHIPS][2];
38 bez_spline oo_interp_splines[MAX_SHIPS][2];
39 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);
41 // how much data we're willing to put into a given oo packet
42 #define OO_MAX_SIZE 480
44 // tolerance for bashing position
45 #define OO_POS_UPDATE_TOLERANCE 100.0f
47 // new improved - more compacted info type
48 #define OO_POS_NEW (1<<0) //
49 #define OO_ORIENT_NEW (1<<1) //
50 #define OO_HULL_NEW (1<<2) // Hull AND shields
51 #define OO_AFTERBURNER_NEW (1<<3) //
52 #define OO_SUBSYSTEMS_AND_AI_NEW (1<<4) //
53 #define OO_PRIMARY_BANK (1<<5) // if this is set, fighter has selected bank one
54 #define OO_PRIMARY_LINKED (1<<6) // if this is set, banks are linked
55 #define OO_TRIGGER_DOWN (1<<7) // if this is set, trigger is DOWN
57 #define OO_VIEW_CONE_DOT (0.1f)
58 #define OO_VIEW_DIFF_TOL (0.15f) // if the dotproducts differ this far between frames, he's coming into view
60 // no timestamp should ever have sat for longer than this.
61 #define OO_MAX_TIMESTAMP 2500
65 #define OO_NEAR_DIST (200.0f)
67 #define OO_MIDRANGE_DIST (600.0f)
69 #define OO_FAR_DIST (1400.0f)
71 // how often we should send full hull/shield updates
72 #define OO_HULL_SHIELD_TIME 600
73 #define OO_SUBSYS_TIME 1000
75 // timestamp values for object update times based on client's update level.
76 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] =
85 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
94 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
103 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
106 350, // medium update
112 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
115 200, // medium update
121 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
124 600, // medium update
130 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
133 1500, // medium update
138 // ship index list for possibly sorting ships based upon distance, etc
139 short OO_ship_index[MAX_SHIPS];
141 int OO_update_index = -1; // index into OO_update_records for displaying update record info
143 // ---------------------------------------------------------------------------------------------------
144 // OBJECT UPDATE FUNCTIONS
147 object *OO_player_obj;
150 int multi_oo_sort_func(const void *ship1, const void *ship2)
153 short index1, index2;
160 memcpy(&index1, ship1, sizeof(short));
161 memcpy(&index2, ship2, sizeof(short));
163 // if the indices are bogus, or the objnums are bogus, return ">"
164 if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
169 obj1 = &Objects[Ships[index1].objnum];
170 obj2 = &Objects[Ships[index2].objnum];
172 // get the distance and dot product to the player obj for both
173 vm_vec_sub(&v1, &OO_player_obj->pos, &obj1->pos);
174 dist1 = vm_vec_copy_normalize(&vn1, &v1);
175 vm_vec_sub(&v2, &OO_player_obj->pos, &obj2->pos);
176 dist2 = vm_vec_copy_normalize(&vn2, &v2);
177 dot1 = vm_vec_dotprod(&OO_player_obj->orient.v.fvec, &vn1);
178 dot2 = vm_vec_dotprod(&OO_player_obj->orient.v.fvec, &vn2);
180 // objects in front take precedence
181 if((dot1 < 0.0f) && (dot2 >= 0.0f)){
183 } else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
187 // otherwise go by distance
188 return (dist1 <= dist2) ? -1 : 1;
191 // build the list of ship indices to use when updating for this player
192 void multi_oo_build_ship_list(net_player *pl)
199 // set all indices to be -1
200 for(idx = 0;idx<MAX_SHIPS; idx++){
201 OO_ship_index[idx] = -1;
204 // get the player object
205 if(pl->player->objnum < 0){
208 player_obj = &Objects[pl->player->objnum];
210 // go through all other relevant objects
212 for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
213 // if it is an invalid ship object, skip it
214 if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
218 // if we're a standalone server, don't send any data regarding its pseudo-ship
219 if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
223 // must be a ship, a weapon, and _not_ an observer
224 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
228 // don't send info for dying ships
229 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
233 // never update the knossos device
234 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)){
238 // don't send him info for himself
239 if ( &Objects[moveup->objnum] == player_obj ){
243 // don't send info for his targeted ship here, since its always done first
244 if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
249 if(ship_index < MAX_SHIPS){
250 OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
254 // maybe qsort the thing here
255 OO_player_obj = player_obj;
257 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
261 // pack information for a client (myself), return bytes added
262 int multi_oo_pack_client_data(ubyte *data)
265 ushort tnet_signature;
266 char t_subsys, l_subsys;
269 // get our firing stuff
270 out_flags = Net_player->s_info.accum_buttons;
272 // zero these values for now
273 Net_player->s_info.accum_buttons = 0;
275 // add any necessary targeting flags
276 if ( Player_ai->current_target_is_locked ){
277 out_flags |= OOC_TARGET_LOCKED;
279 if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){
280 out_flags |= OOC_TARGET_SEEK_LOCK;
282 if ( Player->locking_on_center ){
283 out_flags |= OOC_LOCKING_ON_CENTER;
285 if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){
286 out_flags |= OOC_TRIGGER_DOWN;
290 if(Player_ship != NULL){
291 if(Player_ship->weapons.current_primary_bank > 0){
292 out_flags |= OOC_PRIMARY_BANK;
296 if(Player_ship->flags & SF_PRIMARY_LINKED){
297 out_flags |= OOC_PRIMARY_LINKED;
301 // copy the final flags in
302 memcpy(data, &out_flags, sizeof(ubyte));
305 // client targeting information
309 // if nothing targeted
310 if(Player_ai->target_objnum == -1){
313 // if something is targeted
315 // target net signature
316 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
318 // targeted subsys index
319 if(Player_ai->targeted_subsys != NULL){
320 t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
323 // locked targeted subsys index
324 if(Player->locking_subsys != NULL){
325 l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
330 memcpy(data + packet_size, &tnet_signature, sizeof(ushort));
331 packet_size += sizeof(ushort);
332 memcpy(data + packet_size, &t_subsys, sizeof(char));
333 packet_size += sizeof(char);
334 memcpy(data + packet_size, &l_subsys, sizeof(char));
335 packet_size += sizeof(char);
340 // pack the appropriate info into the data
341 #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++; }
342 int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
354 // make sure we have a valid ship
355 SDL_assert(objp->type == OBJ_SHIP);
356 if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
357 shipp = &Ships[objp->instance];
358 sip = &Ship_info[shipp->ship_info_index];
368 // no flags -> do nothing
374 // if i'm the client, make sure I only send certain things
375 if(!MULTIPLAYER_MASTER){
376 SDL_assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW));
377 SDL_assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW)));
381 // SDL_assert(oo_flags & OO_POS_NEW);
385 if(MULTIPLAYER_MASTER){
391 // if we're a client (and therefore sending control info), pack client-specific info
392 if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
393 packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);
396 // position, velocity
397 if ( oo_flags & OO_POS_NEW ) {
398 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
402 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
404 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
408 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
412 if(oo_flags & OO_ORIENT_NEW){
413 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
414 // SDL_assert(ret == OO_ORIENT_RET_SIZE);
416 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
418 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
422 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
426 percent = (char)(objp->phys_info.forward_thrust * 100.0f);
427 SDL_assert( percent <= 100 );
429 memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
433 multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1);
436 if ( oo_flags & OO_HULL_NEW ){
437 // add the hull value for this guy
438 temp = (objp->hull_strength / Ship_info[shipp->ship_info_index].initial_hull_strength);
440 multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1);
442 // pack 2 shield values into each byte
445 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
449 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
453 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
457 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));
460 multi_rate_add(NET_PLAYER_NUM(pl), "shl", 4);
464 if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){
466 ship_subsys *subsysp;
468 // just in case we have some kind of invalid data (should've been taken care of earlier in this function)
469 if(shipp->ship_info_index < 0){
471 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
474 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
476 // add the # of subsystems, and their data
478 ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
479 memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
481 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
483 // now the subsystems.
484 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
485 temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits;
488 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
493 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
494 short submode = (short)(Ai_info[shipp->ai_index].submode);
495 ushort target_signature;
497 target_signature = 0;
498 if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
499 target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
502 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
504 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
506 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));
509 multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5);
511 // primary weapon energy
512 temp = shipp->weapon_energy / sip->max_weapon_reserve;
517 oo_flags &= ~PF_AFTERBURNER_ON;
518 if(objp->phys_info.flags & PF_AFTERBURNER_ON){
519 oo_flags |= OO_AFTERBURNER_NEW;
522 // if this ship is a support ship, send some extra info
523 ubyte support_extra = 0;
524 if(MULTIPLAYER_MASTER && (sip->flags & SIF_SUPPORT) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
529 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
531 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].ai_flags, sizeof(int));
533 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].mode, sizeof(int));
535 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].submode, sizeof(int));
537 if((Ai_info[shipp->ai_index].dock_objnum < 0) || (Ai_info[shipp->ai_index].dock_objnum >= MAX_OBJECTS)){
540 dock_sig = Objects[Ai_info[shipp->ai_index].dock_objnum].net_signature;
542 memcpy(data + packet_size + header_bytes, &dock_sig, sizeof(ushort));
546 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
550 // make sure we have a valid chunk of data
551 SDL_assert(packet_size < 255);
552 if(packet_size >= 255){
555 data_size = (ubyte)packet_size;
557 // add the object's net signature, type and oo_flags
559 // don't add for clients
560 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
561 multi_rate_add(NET_PLAYER_NUM(pl), "sig", 2);
562 ADD_USHORT( objp->net_signature );
564 multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1);
565 ADD_DATA( oo_flags );
567 multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1);
568 ADD_DATA( data_size );
570 multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1);
571 ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq );
573 packet_size += data_size;
575 // copy to the outgoing data
576 memcpy(data_out, data, packet_size);
581 // unpack information for a client , return bytes processed
582 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
588 memcpy(&in_flags, data, sizeof(ubyte));
591 // get the player ship
593 if((pl->player->objnum >= 0) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Objects[pl->player->objnum].instance >= 0)){
594 shipp = &Ships[Objects[pl->player->objnum].instance];
597 // if we have a valid netplayer pointer
598 if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){
600 pl->player->ci.fire_primary_count = 0;
603 pl->player->ci.fire_secondary_count = 0;
604 if ( in_flags & OOC_FIRE_SECONDARY ){
605 pl->player->ci.fire_secondary_count = 1;
608 // countermeasure fired
609 pl->player->ci.fire_countermeasure_count = 0;
611 // set up aspect locking information
612 pl->player->locking_on_center = 0;
613 if ( in_flags & OOC_LOCKING_ON_CENTER ){
614 pl->player->locking_on_center = 1;
617 // trigger down, bank info
619 if(in_flags & OOC_TRIGGER_DOWN){
620 shipp->flags |= SF_TRIGGER_DOWN;
622 shipp->flags &= ~SF_TRIGGER_DOWN;
625 if(in_flags & OOC_PRIMARY_BANK){
626 shipp->weapons.current_primary_bank = 1;
628 shipp->weapons.current_primary_bank = 0;
632 shipp->flags &= ~SF_PRIMARY_LINKED;
633 if(in_flags & OOC_PRIMARY_LINKED){
634 shipp->flags |= SF_PRIMARY_LINKED;
638 // other locking information
639 if((shipp != NULL) && (shipp->ai_index != -1)){
640 Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
641 if ( in_flags & OOC_TARGET_SEEK_LOCK ) {
642 Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
644 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
649 // client targeting information
651 char t_subsys,l_subsys;
655 GET_USHORT(tnet_sig);
659 // try and find the targeted object
662 tobj = multi_get_network_object( tnet_sig );
664 // maybe fill in targeted object values
665 if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){
666 // assign the target object
667 if(Objects[pl->player->objnum].type == OBJ_SHIP){
668 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
670 pl->s_info.target_objnum = OBJ_INDEX(tobj);
672 // assign subsystems if possible
673 if(Objects[pl->player->objnum].type == OBJ_SHIP){
674 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL;
675 if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
676 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
680 pl->player->locking_subsys = NULL;
681 if(Objects[pl->player->objnum].type == OBJ_SHIP){
682 if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
683 pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
691 // unpack the object data, return bytes processed
692 #define UNPACK_PERCENT(v) { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
693 int multi_oo_unpack_data(net_player *pl, ubyte *data)
698 ubyte data_size, oo_flags;
705 // add the object's net signature, type and oo_flags
706 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
707 GET_USHORT( net_sig );
708 GET_DATA( oo_flags );
710 // clients always pos and orient stuff only
712 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
714 GET_DATA( data_size );
717 // try and find the object
718 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
719 pobjp = multi_get_network_object(net_sig);
721 if((pl != NULL) && (pl->player->objnum != -1)){
722 pobjp = &Objects[pl->player->objnum];
728 // if we can't find the object, set pointer to bogus object to continue reading the data
729 // ignore out of sequence packets here as well
730 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)){
736 shipp = &Ships[pobjp->instance];
737 sip = &Ship_info[shipp->ship_info_index];
739 // ---------------------------------------------------------------------------------------------------------------
740 // CRITICAL OBJECT UPDATE SHIZ
741 // ---------------------------------------------------------------------------------------------------------------
743 // if the packet is out of order
744 if(seq_num < shipp->np_updates[NET_PLAYER_NUM(pl)].seq){
745 // non-wraparound case
746 if((shipp->np_updates[NET_PLAYER_NUM(pl)].seq - seq_num) <= 100){
752 // if this is from a player, read his button info
753 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
754 offset += multi_oo_unpack_client_data(pl, data + offset);
758 vector new_pos = pobjp->pos;
759 physics_info new_phys_info = pobjp->phys_info;
760 matrix new_orient = pobjp->orient;
763 if ( oo_flags & OO_POS_NEW ) {
764 // AVERAGE TIME BETWEEN PACKETS FOR THIS SHIP
765 // store this latest time stamp
766 if(oo_arrive_time_count[shipp - Ships] == 5){
767 memmove(&oo_arrive_time[shipp - Ships][0], &oo_arrive_time[shipp - Ships][1], sizeof(float) * 4);
768 oo_arrive_time[shipp - Ships][4] = f2fl(Missiontime);
770 oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime);
772 // if we've got 5 elements calculate the average
773 if(oo_arrive_time_count[shipp - Ships] == 5){
775 oo_arrive_time_avg_diff[shipp - Ships] = 0.0f;
776 for(idx=0; idx<4; idx++){
777 oo_arrive_time_avg_diff[shipp - Ships] += oo_arrive_time[shipp - Ships][idx + 1] - oo_arrive_time[shipp - Ships][idx];
779 oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f;
781 // next expected arrival time
782 oo_arrive_time_next[shipp - Ships] = 0.0f;
784 // int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos );
785 int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos );
788 // int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
789 int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &new_pos, &new_phys_info );
792 // bash desired vel to be velocity
793 // pobjp->phys_info.desired_vel = pobjp->phys_info.vel;
797 if ( oo_flags & OO_ORIENT_NEW ) {
798 // int r2 = multi_pack_unpack_orient( 0, data + offset, &pobjp->orient );
799 int r2 = multi_pack_unpack_orient( 0, data + offset, &new_orient );
802 // int r5 = multi_pack_unpack_rotvel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
803 int r5 = multi_pack_unpack_rotvel( 0, data + offset, &new_orient, &new_pos, &new_phys_info );
806 // bash desired rotvel to be 0
807 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
811 percent = (char)(pobjp->phys_info.forward_thrust * 100.0f);
812 SDL_assert( percent <= 100 );
815 // now stuff all this new info
816 if(oo_flags & OO_POS_NEW){
817 // if we're past the position update tolerance, bash.
818 // this should cause our 2 interpolation splines to be exactly the same. so we'll see a jump,
819 // but it should be nice and smooth immediately afterwards
820 if(vm_vec_dist(&new_pos, &pobjp->pos) > OO_POS_UPDATE_TOLERANCE){
821 pobjp->pos = new_pos;
824 // recalc any interpolation info
825 if(oo_interp_count[shipp - Ships] < 2){
826 oo_interp_points[shipp - Ships][oo_interp_count[shipp - Ships]++] = new_pos;
828 oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1];
829 oo_interp_points[shipp - Ships][1] = new_pos;
831 multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info);
834 pobjp->phys_info.vel = new_phys_info.vel;
835 pobjp->phys_info.desired_vel = new_phys_info.vel;
838 // we'll just sim rotation straight. it works fine.
839 if(oo_flags & OO_ORIENT_NEW){
840 pobjp->orient = new_orient;
841 pobjp->phys_info.rotvel = new_phys_info.rotvel;
842 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
843 pobjp->phys_info.desired_rotvel = new_phys_info.rotvel;
847 // ---------------------------------------------------------------------------------------------------------------
848 // ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE!
849 // ---------------------------------------------------------------------------------------------------------------
852 if ( oo_flags & OO_HULL_NEW ){
853 UNPACK_PERCENT(fpct);
854 pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength;
856 float shield_0, shield_1, shield_2, shield_3;
858 // unpack the 4 quadrants
859 UNPACK_PERCENT(shield_0);
860 UNPACK_PERCENT(shield_1);
861 UNPACK_PERCENT(shield_2);
862 UNPACK_PERCENT(shield_3);
864 pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
865 pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
866 pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
867 pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
870 if ( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ) {
871 ubyte n_subsystems, subsys_count;
872 float subsystem_percent[MAX_MODEL_SUBSYSTEMS];
873 ship_subsys *subsysp;
877 SDL_zero(subsystem_percent);
879 // get the data for the subsystems
880 GET_DATA( n_subsystems );
881 for ( i = 0; i < n_subsystems; i++ ){
882 UNPACK_PERCENT( subsystem_percent[i] );
885 // fill in the subsystem data
887 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
890 val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
891 subsysp->current_hits = val;
893 // add the value just generated (it was zero'ed above) into the array of generic system types
894 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
895 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
896 shipp->subsys_info[subsys_type].current_hits += val;
899 // if we've reached max subsystems for some reason, bail out
900 if(subsys_count >= n_subsystems){
905 // recalculate all ship subsystems
906 ship_recalc_subsys_strength( shipp );
911 ushort target_signature;
915 GET_SHORT( submode );
916 GET_USHORT( target_signature );
918 if(shipp->ai_index >= 0){
919 Ai_info[shipp->ai_index].mode = umode;
920 Ai_info[shipp->ai_index].submode = submode;
922 // set this guys target objnum
923 target_objp = multi_get_network_object( target_signature );
924 if ( target_objp == NULL ){
925 Ai_info[shipp->ai_index].target_objnum = -1;
927 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
931 // primary weapon energy
932 float weapon_energy_pct;
933 UNPACK_PERCENT(weapon_energy_pct);
934 shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct;
937 // support ship extra info
939 GET_DATA(support_extra);
942 int ai_flags, ai_mode, ai_submode;
948 GET_USHORT(dock_sig);
951 if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
952 Ai_info[shipp->ai_index].ai_flags = ai_flags;
953 Ai_info[shipp->ai_index].mode = ai_mode;
954 Ai_info[shipp->ai_index].submode = ai_submode;
956 object *objp = multi_get_network_object( dock_sig );
958 Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp);
964 if(oo_flags & OO_AFTERBURNER_NEW){
965 // maybe turn them on
966 if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){
967 afterburners_start(pobjp);
970 // maybe turn them off
971 if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
972 afterburners_stop(pobjp);
976 // primary info (only clients care about this)
977 if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
979 if(oo_flags & OO_PRIMARY_BANK){
980 shipp->weapons.current_primary_bank = 1;
982 shipp->weapons.current_primary_bank = 0;
986 shipp->flags &= ~SF_PRIMARY_LINKED;
987 if(oo_flags & OO_PRIMARY_LINKED){
988 shipp->flags |= SF_PRIMARY_LINKED;
991 // trigger down or not - server doesn't care about this. he'll get it from clients anyway
992 shipp->flags &= ~SF_TRIGGER_DOWN;
993 if(oo_flags & OO_TRIGGER_DOWN){
994 shipp->flags |= SF_TRIGGER_DOWN;
998 // if we're the multiplayer server, set eye position and orient
999 if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){
1000 pl->s_info.eye_pos = pobjp->pos;
1001 pl->s_info.eye_orient = pobjp->orient;
1004 // update the sequence #
1005 shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
1007 // flag the object as just updated
1008 // pobjp->flags |= OF_JUST_UPDATED;
1013 // reset the timestamp appropriately for the passed in object
1014 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1018 // if this is the guy's target,
1019 if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1020 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1022 // reset the timestamp appropriately
1024 // base it upon range
1027 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1031 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1035 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1039 // base it upon range
1042 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1046 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1050 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1056 // reset the timestamp for this object
1057 if(objp->type == OBJ_SHIP){
1058 Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp);
1062 // reset the timestamp appropriately for the passed in object
1063 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1065 Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
1068 // reset the timestamp appropriately for the passed in object
1069 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1071 Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
1074 // determine what needs to get sent for this player regarding the passed object, and when
1075 int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data)
1082 float eye_dot, dist;
1087 ushort cur_pos_chksum = 0;
1088 ushort cur_orient_chksum = 0;
1090 // if the timestamp has elapsed for this guy, send stuff
1091 player_index = NET_PLAYER_INDEX(pl);
1092 if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1096 // determine what the timestamp is for this object
1097 if(obj->type == OBJ_SHIP){
1098 stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp;
1103 // stamp hasn't popped yet
1104 if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){
1108 // if we're supposed to update this guy
1110 // get the ship pointer
1111 shipp = &Ships[obj->instance];
1113 // get ship info pointer
1115 if(shipp->ship_info_index >= 0){
1116 sip = &Ship_info[shipp->ship_info_index];
1119 // check dot products
1120 player_eye = pl->s_info.eye_orient.v.fvec;
1121 vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1122 vm_vec_normalize(&obj_dot);
1123 eye_dot = vm_vec_dot(&obj_dot, &player_eye);
1124 in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;
1126 // determine distance (near, medium, far)
1127 vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1128 dist = vm_vec_mag(&obj_dot);
1129 if(dist < OO_NEAR_DIST){
1131 } else if(dist < OO_MIDRANGE_DIST){
1132 range = OO_MIDRANGE;
1137 // reset the timestamp for the next update for this guy
1138 multi_oo_reset_timestamp(pl, obj, range, in_cone);
1141 oo_flags = OO_POS_NEW | OO_ORIENT_NEW;
1143 // if its a small ship, add weapon link info
1144 if((sip != NULL) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_STEALTH))){
1145 // primary bank 0 or 1
1146 if(shipp->weapons.current_primary_bank > 0){
1147 oo_flags |= OO_PRIMARY_BANK;
1151 if(shipp->flags & SF_PRIMARY_LINKED){
1152 oo_flags |= OO_PRIMARY_LINKED;
1155 // trigger down or not
1156 if(shipp->flags & SF_TRIGGER_DOWN){
1157 oo_flags |= OO_TRIGGER_DOWN;
1161 // if the object's hull/shield timestamp has expired
1162 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)){
1163 oo_flags |= (OO_HULL_NEW);
1165 // reset the timestamp
1166 multi_oo_reset_status_timestamp(obj, player_index);
1169 // if the object's hull/shield timestamp has expired
1170 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)){
1171 oo_flags |= OO_SUBSYSTEMS_AND_AI_NEW;
1173 // reset the timestamp
1174 multi_oo_reset_subsys_timestamp(obj, player_index);
1177 // add info for a targeted object
1178 if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
1179 oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW);
1183 // add info which is contingent upon being "in front"
1185 oo_flags |= OO_ORIENT_NEW;
1189 // get current position and orient checksums
1190 cur_pos_chksum = cf_add_chksum_short(cur_pos_chksum, (char*)(&obj->pos), sizeof(vector));
1191 cur_orient_chksum = cf_add_chksum_short(cur_orient_chksum, (char*)(&obj->orient), sizeof(matrix));
1193 // if position or orientation haven't changed
1194 if((shipp->np_updates[player_index].pos_chksum != 0) && (shipp->np_updates[player_index].pos_chksum == cur_pos_chksum)){
1195 // if we otherwise would have been sending it, keep track of it (debug only)
1197 if(oo_flags & OO_POS_NEW){
1198 multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE);
1201 oo_flags &= ~(OO_POS_NEW);
1203 if((shipp->np_updates[player_index].orient_chksum != 0) && (shipp->np_updates[player_index].orient_chksum == cur_orient_chksum)){
1204 // if we otherwise would have been sending it, keep track of it (debug only)
1206 if(oo_flags & OO_ORIENT_NEW){
1207 multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE);
1210 oo_flags &= ~(OO_ORIENT_NEW);
1212 shipp->np_updates[player_index].pos_chksum = cur_pos_chksum;
1213 shipp->np_updates[player_index].orient_chksum = cur_orient_chksum;
1215 // pack stuff only if we have to
1216 int packed = multi_oo_pack_data(pl, obj, oo_flags ,data);
1218 // increment sequence #
1219 Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++;
1225 // process all other objects for this player
1226 void multi_oo_process_all(net_player *pl)
1228 ubyte data[MAX_PACKET_SIZE];
1229 ubyte data_add[MAX_PACKET_SIZE];
1232 int packet_size = 0;
1237 // if the player has an invalid objnum..
1238 if(pl->player->objnum < 0){
1244 // build the list of ships to check against
1245 multi_oo_build_ship_list(pl);
1247 // do nothing if he has no object targeted, or if he has a weapon targeted
1248 if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1250 BUILD_HEADER(OBJECT_UPDATE);
1252 // get a pointer to the object
1253 targ_obj = &Objects[pl->s_info.target_objnum];
1255 // run through the maybe_update function
1256 add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
1258 // copy in any relevant data
1261 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1264 memcpy(data + packet_size, data_add, add_size);
1265 packet_size += add_size;
1268 // just build the header for the rest of the function
1269 BUILD_HEADER(OBJECT_UPDATE);
1273 while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1274 // if this guy is over his datarate limit, do nothing
1275 if(multi_oo_rate_exceeded(pl)){
1276 nprintf(("Network","Capping client\n"));
1283 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1285 // maybe send some info
1286 add_size = multi_oo_maybe_update(pl, moveup, data_add);
1288 // if this data is too much for the packet, send off what we currently have and start over
1289 if(packet_size + add_size > OO_MAX_SIZE){
1291 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1294 multi_io_send(pl, data, packet_size);
1295 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1297 BUILD_HEADER(OBJECT_UPDATE);
1302 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1306 memcpy(data + packet_size,data_add,add_size);
1307 packet_size += add_size;
1314 // if we have anything more than 3 byte in the packet, send the last one off
1315 if(packet_size > 3){
1317 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1320 multi_io_send(pl, data, packet_size);
1321 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1325 // process all object update details for this frame
1326 void multi_oo_process()
1330 // process each player
1331 for(idx=0; idx<MAX_PLAYERS; idx++){
1332 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) /*&& !MULTI_OBSERVER(Net_players[idx])*/ ){
1333 // now process the rest of the objects
1334 multi_oo_process_all(&Net_players[idx]);
1336 // do firing stuff for this player
1337 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)){
1338 if((Objects[Net_players[idx].player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].player->objnum].flags & OF_SHOULD_BE_DEAD)){
1339 obj_player_fire_stuff( &Objects[Net_players[idx].player->objnum], Net_players[idx].player->ci );
1346 // process incoming object update data
1347 void multi_oo_process_update(ubyte *data, header *hinfo)
1351 int offset = HEADER_LENGTH;
1352 net_player *pl = NULL;
1354 // if this is processed on the server, its a client object update packet
1355 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1356 // determine what player this came from
1357 player_index = find_player_id(hinfo->id);
1358 if(player_index != -1){
1359 pl = &Net_players[player_index];
1364 // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
1371 while(stop == 0xff){
1373 offset += multi_oo_unpack_data(pl, data + offset);
1380 // initialize all object update timestamps (call whenever entering gameplay state)
1381 void multi_oo_gameplay_init()
1383 int cur, s_idx, idx;
1387 int num_ships = ship_get_num_ships();
1392 split = 3000 / num_ships;
1397 // server should setup initial update timestamps
1398 // stagger initial updates over 3 seconds or so
1400 //for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1401 //if(Objects[so->objnum].type == OBJ_SHIP){
1402 for(s_idx=0; s_idx<MAX_SHIPS; s_idx++){
1403 shipp = &Ships[s_idx];
1405 // update the timestamps
1406 for(idx=0;idx<MAX_PLAYERS;idx++){
1407 shipp->np_updates[idx].update_stamp = timestamp(cur);
1408 shipp->np_updates[idx].status_update_stamp = timestamp(cur);
1409 shipp->np_updates[idx].subsys_update_stamp = timestamp(cur);
1410 shipp->np_updates[idx].seq = 0;
1411 shipp->np_updates[idx].pos_chksum = 0;
1412 shipp->np_updates[idx].orient_chksum = 0;
1415 oo_arrive_time_count[shipp - Ships] = 0;
1416 oo_interp_count[shipp - Ships] = 0;
1418 // increment the time
1423 // reset datarate stamp now
1425 for(idx=0; idx<MAX_PLAYERS; idx++){
1426 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1430 // send control info for a client (which is basically a "reverse" object update)
1431 void multi_oo_send_control_info()
1433 ubyte data[MAX_PACKET_SIZE], stop;
1434 ubyte data_add[MAX_PACKET_SIZE];
1437 int packet_size = 0;
1439 // if I'm dying or my object type is not a ship, bail here
1440 if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1445 BUILD_HEADER(OBJECT_UPDATE);
1447 // pos and orient always
1448 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
1450 // pack the appropriate info into the data
1451 add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1453 // copy in any relevant data
1456 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1460 memcpy(data + packet_size, data_add, add_size);
1461 packet_size += add_size;
1464 // add the final stop byte
1466 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1469 // increment sequence #
1470 Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
1472 // send to the server
1473 if(Netgame.server != NULL){
1474 multi_io_send(Net_player, data, packet_size);
1478 // display any oo info on the hud
1479 void multi_oo_display()
1486 // ---------------------------------------------------------------------------------------------------
1487 // DATARATE DEFINES/VARS
1490 // low object update datarate limit
1491 #define OO_LIMIT_LOW 1800
1492 #define OO_LIMIT_MED 3400
1494 // timestamp for sending control info (movement only - we'll send button info all the time)
1495 #define OO_CIRATE 85 // 15x a second
1496 int Multi_cirate_stamp = -1; // timestamp for waiting on control info time
1497 int Multi_cirate_can_send = 1; // if we can send control info this frame
1500 int OO_server_rate = -1; // max _total_ bandwidth to send to all clients
1501 int OO_client_rate = -1; // max bandwidth to go to an individual client
1503 // update timestamp for server datarate checking
1504 #define RATE_UPDATE_TIME 1250 // in ms
1505 int OO_server_rate_stamp = -1;
1507 // bandwidth granularity
1511 dc_get_arg(ARG_INT);
1512 OO_gran = Dc_arg_int;
1515 // process datarate limiting stuff for the server
1516 void multi_oo_server_process();
1518 // process datarate limiting stuff for the client
1519 void multi_oo_client_process();
1521 // update the server datarate
1522 void multi_oo_update_server_rate();
1525 // ---------------------------------------------------------------------------------------------------
1526 // DATARATE FUNCTIONS
1529 // process all object update datarate details
1530 void multi_oo_rate_process()
1532 // if I have no valid player, drop out here
1533 if(Net_player == NULL){
1537 // if we're not in mission, don't do anything
1538 if(!(Game_mode & GM_IN_MISSION)){
1542 // if I'm the server of a game, process server stuff
1543 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1544 multi_oo_server_process();
1546 // otherwise process client-side stuff
1548 multi_oo_client_process();
1552 // process datarate limiting stuff for the server
1553 void multi_oo_server_process()
1557 // go through all players
1558 for(idx=0;idx<MAX_PLAYERS;idx++){
1559 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1560 // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1561 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)) ){
1562 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1563 Net_players[idx].s_info.rate_bytes = 0;
1568 // determine if we should be updating the server datarate
1569 if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){
1570 // reset the timestamp
1571 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1573 // update the server datarate
1574 multi_oo_update_server_rate();
1576 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1580 // process datarate limiting stuff for the client
1581 void multi_oo_client_process()
1583 // if the timestamp is -1 or has elapsed, reset it
1584 if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){
1585 Multi_cirate_can_send = 1;
1586 Multi_cirate_stamp = timestamp(OO_CIRATE);
1591 // datarate limiting system for server -------------------------------------
1593 // initialize the rate limiting system for all players
1594 void multi_oo_rate_init_all()
1598 // if I don't have a net_player, bail here
1599 if(Net_player == NULL){
1603 // if I'm the server of the game
1604 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1605 // go through all players
1606 for(idx=0;idx<MAX_PLAYERS;idx++){
1607 if(MULTI_CONNECTED(Net_players[idx])){
1608 multi_oo_rate_init(&Net_players[idx]);
1612 OO_server_rate_stamp = -1;
1614 // if i'm the client, initialize my control info datarate stuff
1616 Multi_cirate_stamp = -1;
1617 Multi_cirate_can_send = 1;
1621 // initialize the rate limiting for the passed in player
1622 void multi_oo_rate_init(net_player *pl)
1624 // reinitialize his datarate timestamp
1625 pl->s_info.rate_stamp = -1;
1626 pl->s_info.rate_bytes = 0;
1629 // if the given net-player has exceeded his datarate limit
1630 int multi_oo_rate_exceeded(net_player *pl)
1634 // check against the guy's object update level
1635 switch(pl->p_info.options.obj_update_level){
1637 case OBJ_UPDATE_LOW:
1638 // the low object update limit
1639 rate_compare = OO_LIMIT_LOW;
1642 // medium update level
1643 case OBJ_UPDATE_MEDIUM:
1644 // the low object update limit
1645 rate_compare = OO_LIMIT_MED;
1648 // high update level - super high datarate (no capping, just intelligent updating)
1649 case OBJ_UPDATE_HIGH:
1650 rate_compare = 100000000;
1653 // LAN - no rate max
1654 case OBJ_UPDATE_LAN:
1660 rate_compare = OO_LIMIT_LOW;
1664 // if the server global rate PER CLIENT (OO_client_rate) is actually lower
1665 if(OO_client_rate < rate_compare){
1666 rate_compare = OO_client_rate;
1669 // compare his bytes sent against the allowable amount
1670 if(pl->s_info.rate_bytes >= rate_compare){
1674 // we're allowed to send
1678 // if it is ok for me to send a control info (will be ~N times a second)
1679 int multi_oo_cirate_can_send()
1681 // if we're allowed to send
1682 if(Multi_cirate_can_send){
1683 Multi_cirate_can_send = 0;
1690 // dynamically update the server capped bandwidth rate
1691 void multi_oo_update_server_rate()
1693 int num_connections;
1696 if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1700 // get the # of connections
1701 num_connections = multi_num_connections();
1702 if(!(Game_mode & GM_STANDALONE_SERVER)){
1705 // make sure we always pretend there's at least one guy available
1706 if(num_connections <= 0){
1707 num_connections = 1;
1710 // set the data rate
1711 switch(Net_player->p_info.options.obj_update_level){
1713 case OBJ_UPDATE_LAN:
1714 // set to something super big so we don't limit anything
1715 OO_server_rate = 500000000;
1718 // high update level
1719 case OBJ_UPDATE_HIGH:
1720 // set to 0 so we don't limit anything
1721 OO_server_rate = Multi_options_g.datarate_cap;
1724 // medium update level
1725 case OBJ_UPDATE_MEDIUM:
1726 // set the rate to be "medium" update level
1727 OO_server_rate = OO_LIMIT_MED;
1731 case OBJ_UPDATE_LOW:
1732 // set the rate to be the "low" update level
1733 OO_server_rate = OO_LIMIT_LOW;
1741 // set the individual client level
1742 OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
1745 // reset all sequencing info (obsolete for new object update stuff)
1746 void multi_oo_reset_sequencing()
1750 // is this object one which needs to go through the interpolation
1751 int multi_oo_is_interp_object(object *objp)
1753 // if not multiplayer, skip it
1754 if(!(Game_mode & GM_MULTIPLAYER)){
1758 // if its not a ship, skip it
1759 if(objp->type != OBJ_SHIP){
1763 // other bogus cases
1764 if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1768 // if I'm a client and this is not me, I need to interp it
1769 if(!MULTIPLAYER_MASTER){
1770 if(objp != Player_obj){
1777 // servers only interpolate other player ships
1778 if(!(objp->flags & OF_PLAYER_SHIP)){
1782 // here we know its a player ship - is it mine?
1783 if(objp == Player_obj){
1792 void multi_oo_interp(object *objp)
1794 // make sure its a valid ship
1795 SDL_assert(Game_mode & GM_MULTIPLAYER);
1796 if(objp->type != OBJ_SHIP){
1799 if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1803 // increment his approx "next" time
1804 oo_arrive_time_next[objp->instance] += flFrametime;
1806 // do stream weapon firing for this ship
1807 SDL_assert(objp != Player_obj);
1808 if(objp != Player_obj){
1809 ship_fire_primary(objp, 1, 0);
1812 // if this ship doesn't have enough data points yet, skip it
1813 if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){
1817 // store the magnitude of his velocity
1818 // float vel_mag = vm_vec_mag(&objp->phys_info.vel);
1820 // determine how far along we are (0.0 to 1.0) until we should be getting the next packet
1821 float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance];
1823 // gr_set_color_fast(&Color_bright);
1824 // gr_printf(100, 10, "%f\n", t);
1826 // we've overshot. hmm. just keep the sim running I guess
1828 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1832 // otherwise, blend the two curves together to get the new point
1833 float u = 0.5f + (t * 0.5f);
1834 vector p_bad, p_good;
1835 oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u);
1836 oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u);
1837 vm_vec_scale(&p_good, t);
1838 vm_vec_scale(&p_bad, 1.0f - t);
1839 vm_vec_add(&objp->pos, &p_bad, &p_good);
1842 // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
1844 // run the sim for rotation
1845 physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1847 // blend velocity vectors together with an average weight
1849 vector v_bad, v_good;
1850 oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0);
1851 oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0);
1854 vm_vec_scale(&v_good, t);
1855 vm_vec_scale(&v_bad, 1.0f - t);
1856 vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good);
1859 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1863 vector v_bad, v_good;
1864 oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0);
1865 oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0);
1868 vm_vec_scale(&v_good, t);
1869 vm_vec_scale(&v_bad, 1.0f - t);
1870 vm_vec_avg(&objp->pos, &v_bad, &v_good);
1873 // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1874 physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1878 float oo_error = 0.8f;
1881 dc_get_arg(ARG_FLOAT);
1882 oo_error = Dc_arg_float;
1885 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)
1888 // vector da, db, dc;
1890 physics_info p_copy;
1891 vector *pts[3] = {&a, &b, &c};
1892 // vector *d_pts[3] = {&da, &db, &dc};
1894 // average time between packets
1895 float avg_diff = oo_arrive_time_avg_diff[ship_index];
1897 // would this cause us to rubber-band?
1898 vector v_norm = cur_phys_info->vel;
1900 vm_vec_sub(&v_dir, new_pos, cur_pos);
1901 if(!IS_VEC_NULL(&v_norm) && !IS_VEC_NULL(&v_dir)){
1902 vm_vec_normalize(&v_dir);
1903 vm_vec_normalize(&v_norm);
1904 if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){
1905 *new_pos = *cur_pos;
1909 // get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether
1910 a = oo_interp_points[ship_index][0];
1913 m_copy = *cur_orient;
1914 p_copy = *cur_phys_info;
1915 physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error); // next point, assuming we followed our current path
1916 oo_interp_splines[ship_index][0].bez_set_points(3, pts);
1918 // get the spline representing where this new point tells us we'd be heading
1919 a = oo_interp_points[ship_index][0];
1920 b = oo_interp_points[ship_index][1];
1921 c = oo_interp_points[ship_index][1];
1922 m_copy = *new_orient;
1923 p_copy = *new_phys_info;
1924 physics_sim(&c, &m_copy, &p_copy, avg_diff); // next point, given this new info
1925 oo_interp_splines[ship_index][1].bez_set_points(3, pts);
1927 // get the spline for our "bad" movement
1929 a = oo_interp_points[ship_index][0];
1931 da = oo_interp_vel[ship_index][0];
1932 db = cur_phys_info->vel;
1933 oo_interp_splines[ship_index][0].herm_set_points(2, pts, d_pts);
1935 // get the spline for our "good" movement
1936 a = oo_interp_points[ship_index][0];
1937 b = oo_interp_points[ship_index][1];
1938 da = oo_interp_vel[ship_index][0];
1939 db = oo_interp_vel[ship_index][1];
1940 oo_interp_splines[ship_index][1].herm_set_points(2, pts, d_pts);
1943 // now we've got a spline representing our "new" path and where we would've gone had we been perfect before
1944 // we'll modify our velocity to move along a blend of these splines.
1947 #include "alphacolors.h"
1949 void oo_update_time()
1953 int display_oo_bez = 0;
1956 display_oo_bez = !display_oo_bez;
1958 dc_printf("Showing interp splines");
1960 dc_printf("Not showing interp splines");
1969 gr_set_color_fast(&Color_bright);
1971 for(idx=0; idx<MAX_SHIPS; idx++){
1973 if(Ships[idx].objnum < 0){
1977 // time between updates
1978 if( (oo_arrive_time_count[idx] == 5) && (idx != (Player_ship - Ships)) ){
1979 gr_printf(20, 40, "avg time between updates : %f", oo_arrive_time_avg_diff[idx]);
1982 // interpolation splines
1983 if( (oo_interp_count[idx] == 2) && (display_oo_bez) ){
1984 oo_interp_splines[idx][0].bez_render(10, &Color_bright_red); // bad path
1985 oo_interp_splines[idx][1].bez_render(10, &Color_bright_green); // good path