mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
There are a lot of changes here that should probably be examined, namely the Debug Console (which had conflicts) and Yarn's "any resolution" mainhalls code (Which also had conflicts) but for the most part this should be fairly clean. git-svn-id: svn://svn.icculus.org/fs2open/branches/antipodes@10912 387891d4-d844-0410-90c0-e4c51a9137d3
2060 lines
58 KiB
C++
2060 lines
58 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "network/multi_obj.h"
|
|
#include "globalincs/globals.h"
|
|
#include "freespace2/freespace.h"
|
|
#include "io/timer.h"
|
|
#include "io/key.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "network/multimsgs.h"
|
|
#include "network/multiutil.h"
|
|
#include "network/multi_options.h"
|
|
#include "network/multi_rate.h"
|
|
#include "network/multi.h"
|
|
#include "object/object.h"
|
|
#include "ship/ship.h"
|
|
#include "playerman/player.h"
|
|
#include "math/spline.h"
|
|
#include "physics/physics.h"
|
|
#include "ship/afterburner.h"
|
|
#include "cfile/cfile.h"
|
|
#include "debugconsole/console.h"
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// OBJECT UPDATE DEFINES/VARS
|
|
//
|
|
|
|
// test stuff
|
|
float oo_arrive_time[MAX_SHIPS][5]; // the last 5 arrival times for each ship
|
|
int oo_arrive_time_count[MAX_SHIPS]; // size of the arrival queue
|
|
float oo_arrive_time_avg_diff[MAX_SHIPS]; // the average time between arrivals
|
|
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
|
|
|
|
// interp stuff
|
|
int oo_interp_count[MAX_SHIPS];
|
|
vec3d oo_interp_points[MAX_SHIPS][2];
|
|
bez_spline oo_interp_splines[MAX_SHIPS][2];
|
|
void multi_oo_calc_interp_splines(int ship_index, vec3d *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vec3d *new_pos, matrix *new_orient, physics_info *new_phys_info);
|
|
|
|
// HACK!!!
|
|
bool Multi_oo_afterburn_hack = false;
|
|
|
|
// how much data we're willing to put into a given oo packet
|
|
#define OO_MAX_SIZE 480
|
|
|
|
// tolerance for bashing position
|
|
#define OO_POS_UPDATE_TOLERANCE 100.0f
|
|
|
|
// new improved - more compacted info type
|
|
#define OO_POS_NEW (1<<0) //
|
|
#define OO_ORIENT_NEW (1<<1) //
|
|
#define OO_HULL_NEW (1<<2) // Hull AND shields
|
|
#define OO_AFTERBURNER_NEW (1<<3) //
|
|
#define OO_SUBSYSTEMS_AND_AI_NEW (1<<4) //
|
|
#define OO_PRIMARY_BANK (1<<5) // if this is set, fighter has selected bank one
|
|
#define OO_PRIMARY_LINKED (1<<6) // if this is set, banks are linked
|
|
#define OO_TRIGGER_DOWN (1<<7) // if this is set, trigger is DOWN
|
|
|
|
#define OO_VIEW_CONE_DOT (0.1f)
|
|
#define OO_VIEW_DIFF_TOL (0.15f) // if the dotproducts differ this far between frames, he's coming into view
|
|
|
|
// no timestamp should ever have sat for longer than this.
|
|
#define OO_MAX_TIMESTAMP 2500
|
|
|
|
// distance class
|
|
#define OO_NEAR 0
|
|
#define OO_NEAR_DIST (200.0f)
|
|
#define OO_MIDRANGE 1
|
|
#define OO_MIDRANGE_DIST (600.0f)
|
|
#define OO_FAR 2
|
|
#define OO_FAR_DIST (1400.0f)
|
|
|
|
// how often we should send full hull/shield updates
|
|
#define OO_HULL_SHIELD_TIME 600
|
|
#define OO_SUBSYS_TIME 1000
|
|
|
|
// timestamp values for object update times based on client's update level.
|
|
int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
100, // 15x a second
|
|
100, // 15x a second
|
|
66, // 30x a second
|
|
66,
|
|
};
|
|
|
|
// for near ships
|
|
int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
150, // low update
|
|
100, // medium update
|
|
66, // high update
|
|
66,
|
|
};
|
|
|
|
// for medium ships
|
|
int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
250, // low update
|
|
180, // medium update
|
|
120, // high update
|
|
66,
|
|
};
|
|
|
|
// for far ships
|
|
int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
750, // low update
|
|
350, // medium update
|
|
150, // high update
|
|
66,
|
|
};
|
|
|
|
// for near ships
|
|
int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
300, // low update
|
|
200, // medium update
|
|
100, // high update
|
|
66,
|
|
};
|
|
|
|
// for medium ships
|
|
int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
800, // low update
|
|
600, // medium update
|
|
300, // high update
|
|
66,
|
|
};
|
|
|
|
// for far ships
|
|
int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
|
|
{
|
|
2500, // low update
|
|
1500, // medium update
|
|
400, // high update
|
|
66,
|
|
};
|
|
|
|
// ship index list for possibly sorting ships based upon distance, etc
|
|
short OO_ship_index[MAX_SHIPS];
|
|
|
|
int OO_update_index = -1; // index into OO_update_records for displaying update record info
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// OBJECT UPDATE FUNCTIONS
|
|
//
|
|
|
|
object *OO_player_obj;
|
|
int OO_sort = 1;
|
|
|
|
int multi_oo_sort_func(const void *ship1, const void *ship2)
|
|
{
|
|
object *obj1, *obj2;
|
|
short index1, index2;
|
|
float dist1, dist2;
|
|
float dot1, dot2;
|
|
vec3d v1, v2;
|
|
vec3d vn1, vn2;
|
|
|
|
// get the 2 indices
|
|
memcpy(&index1, ship1, sizeof(short));
|
|
memcpy(&index2, ship2, sizeof(short));
|
|
|
|
// if the indices are bogus, or the objnums are bogus, return ">"
|
|
if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
|
|
return 1;
|
|
}
|
|
|
|
// get the 2 objects
|
|
obj1 = &Objects[Ships[index1].objnum];
|
|
obj2 = &Objects[Ships[index2].objnum];
|
|
|
|
// get the distance and dot product to the player obj for both
|
|
vm_vec_sub(&v1, &OO_player_obj->pos, &obj1->pos);
|
|
dist1 = vm_vec_copy_normalize(&vn1, &v1);
|
|
vm_vec_sub(&v2, &OO_player_obj->pos, &obj2->pos);
|
|
dist2 = vm_vec_copy_normalize(&vn2, &v2);
|
|
dot1 = vm_vec_dotprod(&OO_player_obj->orient.vec.fvec, &vn1);
|
|
dot2 = vm_vec_dotprod(&OO_player_obj->orient.vec.fvec, &vn2);
|
|
|
|
// objects in front take precedence
|
|
if((dot1 < 0.0f) && (dot2 >= 0.0f)){
|
|
return 1;
|
|
} else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
|
|
return -1;
|
|
}
|
|
|
|
// otherwise go by distance
|
|
return (dist1 <= dist2) ? -1 : 1;
|
|
}
|
|
|
|
// build the list of ship indices to use when updating for this player
|
|
void multi_oo_build_ship_list(net_player *pl)
|
|
{
|
|
int ship_index;
|
|
int idx;
|
|
ship_obj *moveup;
|
|
object *player_obj;
|
|
|
|
// set all indices to be -1
|
|
for(idx = 0;idx<MAX_SHIPS; idx++){
|
|
OO_ship_index[idx] = -1;
|
|
}
|
|
|
|
// get the player object
|
|
if(pl->m_player->objnum < 0){
|
|
return;
|
|
}
|
|
player_obj = &Objects[pl->m_player->objnum];
|
|
|
|
// go through all other relevant objects
|
|
ship_index = 0;
|
|
for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
|
|
// if it is an invalid ship object, skip it
|
|
if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
|
|
continue;
|
|
}
|
|
|
|
// if we're a standalone server, don't send any data regarding its pseudo-ship
|
|
if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
|
|
continue;
|
|
}
|
|
|
|
// must be a ship, a weapon, and _not_ an observer
|
|
if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
|
|
continue;
|
|
}
|
|
|
|
// don't send info for dying ships
|
|
if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
|
|
continue;
|
|
}
|
|
|
|
// never update the knossos device
|
|
if ((Ships[Objects[moveup->objnum].instance].ship_info_index >= 0) && (Ships[Objects[moveup->objnum].instance].ship_info_index < Num_ship_classes) && (Ship_info[Ships[Objects[moveup->objnum].instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE)){
|
|
continue;
|
|
}
|
|
|
|
// don't send him info for himself
|
|
if ( &Objects[moveup->objnum] == player_obj ){
|
|
continue;
|
|
}
|
|
|
|
// don't send info for his targeted ship here, since its always done first
|
|
if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
|
|
continue;
|
|
}
|
|
|
|
// add the ship
|
|
if(ship_index < MAX_SHIPS){
|
|
OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
|
|
}
|
|
}
|
|
|
|
// maybe qsort the thing here
|
|
OO_player_obj = player_obj;
|
|
if(OO_sort){
|
|
qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
|
|
}
|
|
}
|
|
|
|
// pack information for a client (myself), return bytes added
|
|
int multi_oo_pack_client_data(ubyte *data)
|
|
{
|
|
ubyte out_flags;
|
|
ushort tnet_signature;
|
|
char t_subsys, l_subsys;
|
|
int packet_size = 0;
|
|
|
|
// get our firing stuff
|
|
out_flags = Net_player->s_info.accum_buttons;
|
|
|
|
// zero these values for now
|
|
Net_player->s_info.accum_buttons = 0;
|
|
|
|
// add any necessary targeting flags
|
|
if ( Player_ai->current_target_is_locked ){
|
|
out_flags |= OOC_TARGET_LOCKED;
|
|
}
|
|
if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){
|
|
out_flags |= OOC_TARGET_SEEK_LOCK;
|
|
}
|
|
if ( Player->locking_on_center ){
|
|
out_flags |= OOC_LOCKING_ON_CENTER;
|
|
}
|
|
if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){
|
|
out_flags |= OOC_TRIGGER_DOWN;
|
|
}
|
|
|
|
if ( (Player_obj != NULL) && Player_obj->phys_info.flags & PF_AFTERBURNER_ON){
|
|
out_flags |= OOC_AFTERBURNER_ON;
|
|
}
|
|
|
|
// send my bank info
|
|
if(Player_ship != NULL){
|
|
if(Player_ship->weapons.current_primary_bank > 0){
|
|
out_flags |= OOC_PRIMARY_BANK;
|
|
}
|
|
|
|
// linked or not
|
|
if(Player_ship->flags & SF_PRIMARY_LINKED){
|
|
out_flags |= OOC_PRIMARY_LINKED;
|
|
}
|
|
}
|
|
|
|
// copy the final flags in
|
|
ADD_DATA( out_flags );
|
|
|
|
// client targeting information
|
|
t_subsys = -1;
|
|
l_subsys = -1;
|
|
|
|
// if nothing targeted
|
|
if(Player_ai->target_objnum == -1){
|
|
tnet_signature = 0;
|
|
}
|
|
// if something is targeted
|
|
else {
|
|
// target net signature
|
|
tnet_signature = Objects[Player_ai->target_objnum].net_signature;
|
|
|
|
// targeted subsys index
|
|
if(Player_ai->targeted_subsys != NULL){
|
|
t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
|
|
}
|
|
|
|
// locked targeted subsys index
|
|
if(Player->locking_subsys != NULL){
|
|
l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
|
|
}
|
|
}
|
|
|
|
// add them all
|
|
ADD_USHORT( tnet_signature );
|
|
ADD_DATA( t_subsys );
|
|
ADD_DATA( l_subsys );
|
|
|
|
return packet_size;
|
|
}
|
|
|
|
// pack the appropriate info into the data
|
|
#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++; }
|
|
#define PACK_BYTE(v) { memcpy( data + packet_size + header_bytes, &v, 1 ); packet_size += 1; }
|
|
#define PACK_USHORT(v) { short swap = INTEL_SHORT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(short) ); packet_size += sizeof(short); }
|
|
#define PACK_SHORT(v) { ushort swap = INTEL_SHORT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(ushort) ); packet_size += sizeof(ushort); }
|
|
#define PACK_INT(v) { int swap = INTEL_INT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(int) ); packet_size += sizeof(int); }
|
|
int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
|
|
{
|
|
ubyte data[255];
|
|
ubyte data_size = 0;
|
|
char percent;
|
|
ship *shipp;
|
|
ship_info *sip;
|
|
ubyte ret;
|
|
float temp;
|
|
int header_bytes;
|
|
int packet_size = 0;
|
|
|
|
// make sure we have a valid ship
|
|
Assert(objp->type == OBJ_SHIP);
|
|
if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
|
|
shipp = &Ships[objp->instance];
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
// invalid player
|
|
if(pl == NULL){
|
|
return 0;
|
|
}
|
|
|
|
// no flags -> do nothing
|
|
if(oo_flags == 0){
|
|
return 0;
|
|
}
|
|
|
|
// if i'm the client, make sure I only send certain things
|
|
if(!MULTIPLAYER_MASTER){
|
|
Assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW));
|
|
Assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW)));
|
|
}
|
|
// server
|
|
else {
|
|
// Assert(oo_flags & OO_POS_NEW);
|
|
}
|
|
|
|
// header sizes
|
|
if(MULTIPLAYER_MASTER){
|
|
header_bytes = 5;
|
|
} else {
|
|
header_bytes = 2;
|
|
}
|
|
|
|
// if we're a client (and therefore sending control info), pack client-specific info
|
|
if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);
|
|
}
|
|
|
|
// position, velocity
|
|
if ( oo_flags & OO_POS_NEW ) {
|
|
ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
|
|
packet_size += ret;
|
|
|
|
// global records
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
|
|
|
|
ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
|
|
packet_size += ret;
|
|
|
|
// global records
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);
|
|
}
|
|
|
|
// orientation
|
|
if(oo_flags & OO_ORIENT_NEW){
|
|
ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
|
|
// Assert(ret == OO_ORIENT_RET_SIZE);
|
|
packet_size += ret;
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
|
|
|
|
ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
|
|
packet_size += ret;
|
|
|
|
// global records
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);
|
|
}
|
|
|
|
// forward thrust
|
|
percent = (char)(objp->phys_info.forward_thrust * 100.0f);
|
|
Assert( percent <= 100 );
|
|
|
|
PACK_BYTE( percent );
|
|
|
|
// global records
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1);
|
|
|
|
// hull info
|
|
if ( oo_flags & OO_HULL_NEW ){
|
|
// add the hull value for this guy
|
|
temp = get_hull_pct(objp);
|
|
if ( (temp < 0.004f) && (temp > 0.0f) ) {
|
|
temp = 0.004f; // 0.004 is the lowest positive value we can have before we zero out when packing
|
|
}
|
|
PACK_PERCENT(temp);
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1);
|
|
|
|
float quad = get_max_shield_quad(objp);
|
|
|
|
for (int i = 0; i < objp->n_quadrants; i++) {
|
|
temp = (objp->shield_quadrant[i] / quad);
|
|
PACK_PERCENT(temp);
|
|
}
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "shl", objp->n_quadrants);
|
|
}
|
|
|
|
// subsystem info
|
|
if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){
|
|
ubyte ns;
|
|
ship_subsys *subsysp;
|
|
|
|
// just in case we have some kind of invalid data (should've been taken care of earlier in this function)
|
|
if(shipp->ship_info_index < 0){
|
|
ns = 0;
|
|
PACK_BYTE( ns );
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
|
|
}
|
|
// add the # of subsystems, and their data
|
|
else {
|
|
ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
|
|
PACK_BYTE( ns );
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
|
|
|
|
// now the subsystems.
|
|
for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
|
|
temp = (float)subsysp->current_hits / (float)subsysp->max_hits;
|
|
PACK_PERCENT(temp);
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
|
|
}
|
|
}
|
|
|
|
// ai mode info
|
|
ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
|
|
short submode = (short)(Ai_info[shipp->ai_index].submode);
|
|
ushort target_signature;
|
|
|
|
target_signature = 0;
|
|
if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
|
|
target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
|
|
}
|
|
|
|
PACK_BYTE( umode );
|
|
PACK_SHORT( submode );
|
|
PACK_USHORT( target_signature );
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5);
|
|
|
|
// primary weapon energy
|
|
temp = shipp->weapon_energy / sip->max_weapon_reserve;
|
|
PACK_PERCENT(temp);
|
|
}
|
|
|
|
// afterburner info
|
|
oo_flags &= ~PF_AFTERBURNER_ON;
|
|
if(objp->phys_info.flags & PF_AFTERBURNER_ON){
|
|
oo_flags |= OO_AFTERBURNER_NEW;
|
|
}
|
|
|
|
// if this ship is a support ship, send some extra info
|
|
ubyte support_extra = 0;
|
|
if(MULTIPLAYER_MASTER && (sip->flags & SIF_SUPPORT) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
|
|
ushort dock_sig;
|
|
|
|
// flag
|
|
support_extra = 1;
|
|
PACK_BYTE( support_extra );
|
|
PACK_INT( Ai_info[shipp->ai_index].ai_flags );
|
|
PACK_INT( Ai_info[shipp->ai_index].mode );
|
|
PACK_INT( Ai_info[shipp->ai_index].submode );
|
|
|
|
if((Ai_info[shipp->ai_index].support_ship_objnum < 0) || (Ai_info[shipp->ai_index].support_ship_objnum >= MAX_OBJECTS)){
|
|
dock_sig = 0;
|
|
} else {
|
|
dock_sig = Objects[Ai_info[shipp->ai_index].support_ship_objnum].net_signature;
|
|
}
|
|
|
|
PACK_USHORT( dock_sig );
|
|
} else {
|
|
support_extra = 0;
|
|
PACK_BYTE( support_extra );
|
|
}
|
|
|
|
// make sure we have a valid chunk of data
|
|
// Clients: must be able to accomodate the data_size and shipp->np_updates[NET_PLAYER_NUM(pl)].seq before the data itself
|
|
// Server: TODO
|
|
Assert(packet_size < 255-1);
|
|
if(packet_size >= 255-1){
|
|
return 0;
|
|
}
|
|
data_size = (ubyte)packet_size;
|
|
|
|
// add the object's net signature, type and oo_flags
|
|
packet_size = 0;
|
|
// don't add for clients
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "sig", 2);
|
|
ADD_USHORT( objp->net_signature );
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1);
|
|
ADD_DATA( oo_flags );
|
|
}
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1);
|
|
ADD_DATA( data_size );
|
|
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1);
|
|
ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq );
|
|
|
|
packet_size += data_size;
|
|
|
|
// copy to the outgoing data
|
|
memcpy(data_out, data, packet_size);
|
|
|
|
return packet_size;
|
|
}
|
|
|
|
// unpack information for a client , return bytes processed
|
|
int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
|
|
{
|
|
ubyte in_flags;
|
|
ship *shipp = NULL;
|
|
object *objp = NULL;
|
|
int offset = 0;
|
|
|
|
if (pl == NULL)
|
|
Error(LOCATION, "Invalid net_player pointer passed to multi_oo_unpack_client\n");
|
|
|
|
memcpy(&in_flags, data, sizeof(ubyte));
|
|
offset++;
|
|
|
|
// get the player ship and object
|
|
if((pl->m_player->objnum >= 0) && (Objects[pl->m_player->objnum].type == OBJ_SHIP) && (Objects[pl->m_player->objnum].instance >= 0)){
|
|
objp = &Objects[pl->m_player->objnum];
|
|
shipp = &Ships[objp->instance];
|
|
}
|
|
|
|
// if we have a valid netplayer pointer
|
|
if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){
|
|
// primary fired
|
|
pl->m_player->ci.fire_primary_count = 0;
|
|
|
|
// secondary fired
|
|
pl->m_player->ci.fire_secondary_count = 0;
|
|
if ( in_flags & OOC_FIRE_SECONDARY ){
|
|
pl->m_player->ci.fire_secondary_count = 1;
|
|
}
|
|
|
|
// countermeasure fired
|
|
pl->m_player->ci.fire_countermeasure_count = 0;
|
|
|
|
// set up aspect locking information
|
|
pl->m_player->locking_on_center = 0;
|
|
if ( in_flags & OOC_LOCKING_ON_CENTER ){
|
|
pl->m_player->locking_on_center = 1;
|
|
}
|
|
|
|
// trigger down, bank info
|
|
if(shipp != NULL){
|
|
if(in_flags & OOC_TRIGGER_DOWN){
|
|
shipp->flags |= SF_TRIGGER_DOWN;
|
|
} else {
|
|
shipp->flags &= ~SF_TRIGGER_DOWN;
|
|
}
|
|
|
|
if(in_flags & OOC_PRIMARY_BANK){
|
|
shipp->weapons.current_primary_bank = 1;
|
|
} else {
|
|
shipp->weapons.current_primary_bank = 0;
|
|
}
|
|
|
|
// linked or not
|
|
shipp->flags &= ~SF_PRIMARY_LINKED;
|
|
if(in_flags & OOC_PRIMARY_LINKED){
|
|
shipp->flags |= SF_PRIMARY_LINKED;
|
|
}
|
|
}
|
|
|
|
// other locking information
|
|
if((shipp != NULL) && (shipp->ai_index != -1)){
|
|
Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
|
|
if ( in_flags & OOC_TARGET_SEEK_LOCK ) {
|
|
Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
|
|
} else {
|
|
Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
|
|
}
|
|
}
|
|
|
|
// afterburner status
|
|
if ( (objp != NULL) && (in_flags & OOC_AFTERBURNER_ON) ) {
|
|
Multi_oo_afterburn_hack = true;
|
|
}
|
|
}
|
|
|
|
// client targeting information
|
|
ushort tnet_sig;
|
|
char t_subsys,l_subsys;
|
|
object *tobj;
|
|
|
|
// get the data
|
|
GET_USHORT(tnet_sig);
|
|
GET_DATA(t_subsys);
|
|
GET_DATA(l_subsys);
|
|
|
|
// try and find the targeted object
|
|
tobj = NULL;
|
|
if(tnet_sig != 0){
|
|
tobj = multi_get_network_object( tnet_sig );
|
|
}
|
|
// maybe fill in targeted object values
|
|
if((tobj != NULL) && (pl != NULL) && (pl->m_player->objnum != -1)){
|
|
// assign the target object
|
|
if(Objects[pl->m_player->objnum].type == OBJ_SHIP){
|
|
Ai_info[Ships[Objects[pl->m_player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
|
|
}
|
|
pl->s_info.target_objnum = OBJ_INDEX(tobj);
|
|
|
|
// assign subsystems if possible
|
|
if(Objects[pl->m_player->objnum].type == OBJ_SHIP){
|
|
Ai_info[Ships[Objects[pl->m_player->objnum].instance].ai_index].targeted_subsys = NULL;
|
|
if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
|
|
Ai_info[Ships[Objects[pl->m_player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
|
|
}
|
|
}
|
|
|
|
pl->m_player->locking_subsys = NULL;
|
|
if(Objects[pl->m_player->objnum].type == OBJ_SHIP){
|
|
if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
|
|
pl->m_player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
|
|
}
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
// unpack the object data, return bytes processed
|
|
#define UNPACK_PERCENT(v) { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
|
|
int multi_oo_unpack_data(net_player *pl, ubyte *data)
|
|
{
|
|
int offset = 0;
|
|
object *pobjp;
|
|
ushort net_sig = 0;
|
|
ubyte data_size, oo_flags;
|
|
ubyte seq_num;
|
|
char percent;
|
|
float fpct;
|
|
ship *shipp;
|
|
ship_info *sip;
|
|
|
|
// add the object's net signature, type and oo_flags
|
|
if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
GET_USHORT( net_sig );
|
|
GET_DATA( oo_flags );
|
|
}
|
|
// clients always pos and orient stuff only
|
|
else {
|
|
oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
|
|
}
|
|
GET_DATA( data_size );
|
|
GET_DATA( seq_num );
|
|
|
|
// try and find the object
|
|
if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
pobjp = multi_get_network_object(net_sig);
|
|
} else {
|
|
if((pl != NULL) && (pl->m_player->objnum != -1)){
|
|
pobjp = &Objects[pl->m_player->objnum];
|
|
} else {
|
|
pobjp = NULL;
|
|
}
|
|
}
|
|
|
|
// if we can't find the object, set pointer to bogus object to continue reading the data
|
|
// ignore out of sequence packets here as well
|
|
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_classes)){
|
|
offset += data_size;
|
|
return offset;
|
|
}
|
|
|
|
// ship pointer
|
|
shipp = &Ships[pobjp->instance];
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------
|
|
// CRITICAL OBJECT UPDATE SHIZ
|
|
// ---------------------------------------------------------------------------------------------------------------
|
|
|
|
// if the packet is out of order
|
|
if(seq_num < shipp->np_updates[NET_PLAYER_NUM(pl)].seq){
|
|
// non-wraparound case
|
|
if((shipp->np_updates[NET_PLAYER_NUM(pl)].seq - seq_num) <= 100){
|
|
offset += data_size;
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
// make sure the ab hack is reset before we read in new info
|
|
Multi_oo_afterburn_hack = false;
|
|
|
|
// if this is from a player, read his button info
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
offset += multi_oo_unpack_client_data(pl, data + offset);
|
|
}
|
|
|
|
// new info
|
|
vec3d new_pos = pobjp->pos;
|
|
physics_info new_phys_info = pobjp->phys_info;
|
|
matrix new_orient = pobjp->orient;
|
|
|
|
// position
|
|
if ( oo_flags & OO_POS_NEW ) {
|
|
// AVERAGE TIME BETWEEN PACKETS FOR THIS SHIP
|
|
// store this latest time stamp
|
|
if(oo_arrive_time_count[shipp - Ships] == 5){
|
|
memmove(&oo_arrive_time[shipp - Ships][0], &oo_arrive_time[shipp - Ships][1], sizeof(float) * 4);
|
|
oo_arrive_time[shipp - Ships][4] = f2fl(Missiontime);
|
|
} else {
|
|
oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime);
|
|
}
|
|
// if we've got 5 elements calculate the average
|
|
if(oo_arrive_time_count[shipp - Ships] == 5){
|
|
int idx;
|
|
oo_arrive_time_avg_diff[shipp - Ships] = 0.0f;
|
|
for(idx=0; idx<4; idx++){
|
|
oo_arrive_time_avg_diff[shipp - Ships] += oo_arrive_time[shipp - Ships][idx + 1] - oo_arrive_time[shipp - Ships][idx];
|
|
}
|
|
oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f;
|
|
}
|
|
// next expected arrival time
|
|
oo_arrive_time_next[shipp - Ships] = 0.0f;
|
|
|
|
// int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos );
|
|
int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos );
|
|
offset += r1;
|
|
|
|
// int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
|
|
int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &new_pos, &new_phys_info );
|
|
offset += r3;
|
|
|
|
// bash desired vel to be velocity
|
|
// pobjp->phys_info.desired_vel = pobjp->phys_info.vel;
|
|
}
|
|
|
|
// orientation
|
|
if ( oo_flags & OO_ORIENT_NEW ) {
|
|
// int r2 = multi_pack_unpack_orient( 0, data + offset, &pobjp->orient );
|
|
int r2 = multi_pack_unpack_orient( 0, data + offset, &new_orient );
|
|
offset += r2;
|
|
|
|
// int r5 = multi_pack_unpack_rotvel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
|
|
int r5 = multi_pack_unpack_rotvel( 0, data + offset, &new_orient, &new_pos, &new_phys_info );
|
|
offset += r5;
|
|
|
|
// bash desired rotvel to be 0
|
|
// pobjp->phys_info.desired_rotvel = vmd_zero_vector;
|
|
}
|
|
|
|
// forward thrust
|
|
percent = (char)(pobjp->phys_info.forward_thrust * 100.0f);
|
|
Assert( percent <= 100 );
|
|
GET_DATA(percent);
|
|
|
|
// now stuff all this new info
|
|
if(oo_flags & OO_POS_NEW){
|
|
// if we're past the position update tolerance, bash.
|
|
// this should cause our 2 interpolation splines to be exactly the same. so we'll see a jump,
|
|
// but it should be nice and smooth immediately afterwards
|
|
if(vm_vec_dist(&new_pos, &pobjp->pos) > OO_POS_UPDATE_TOLERANCE){
|
|
pobjp->pos = new_pos;
|
|
}
|
|
|
|
// recalc any interpolation info
|
|
if(oo_interp_count[shipp - Ships] < 2){
|
|
oo_interp_points[shipp - Ships][oo_interp_count[shipp - Ships]++] = new_pos;
|
|
} else {
|
|
oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1];
|
|
oo_interp_points[shipp - Ships][1] = new_pos;
|
|
|
|
multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info);
|
|
}
|
|
|
|
pobjp->phys_info.vel = new_phys_info.vel;
|
|
pobjp->phys_info.desired_vel = new_phys_info.vel;
|
|
}
|
|
|
|
// we'll just sim rotation straight. it works fine.
|
|
if(oo_flags & OO_ORIENT_NEW){
|
|
pobjp->orient = new_orient;
|
|
pobjp->phys_info.rotvel = new_phys_info.rotvel;
|
|
// pobjp->phys_info.desired_rotvel = vmd_zero_vector;
|
|
pobjp->phys_info.desired_rotvel = new_phys_info.rotvel;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------
|
|
// ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE!
|
|
// ---------------------------------------------------------------------------------------------------------------
|
|
|
|
// hull info
|
|
if ( oo_flags & OO_HULL_NEW ){
|
|
UNPACK_PERCENT(fpct);
|
|
pobjp->hull_strength = fpct * Ships[pobjp->instance].ship_max_hull_strength;
|
|
|
|
float quad = get_max_shield_quad(pobjp);
|
|
|
|
for (int i = 0; i < pobjp->n_quadrants; i++) {
|
|
UNPACK_PERCENT(fpct);
|
|
pobjp->shield_quadrant[i] = fpct * quad;
|
|
}
|
|
}
|
|
|
|
if ( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ) {
|
|
ubyte n_subsystems, subsys_count;
|
|
float subsystem_percent[MAX_MODEL_SUBSYSTEMS];
|
|
ship_subsys *subsysp;
|
|
float val;
|
|
int i;
|
|
|
|
// get the data for the subsystems
|
|
GET_DATA( n_subsystems );
|
|
for ( i = 0; i < n_subsystems; i++ ){
|
|
UNPACK_PERCENT( subsystem_percent[i] );
|
|
}
|
|
|
|
// fill in the subsystem data
|
|
subsys_count = 0;
|
|
for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
|
|
int subsys_type;
|
|
|
|
val = subsystem_percent[subsys_count] * subsysp->max_hits;
|
|
subsysp->current_hits = val;
|
|
|
|
// add the value just generated (it was zero'ed above) into the array of generic system types
|
|
subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
|
|
Assert ( subsys_type < SUBSYSTEM_MAX );
|
|
if (!(subsysp->flags & SSF_NO_AGGREGATE)) {
|
|
shipp->subsys_info[subsys_type].aggregate_current_hits += val;
|
|
}
|
|
subsys_count++;
|
|
|
|
// if we've reached max subsystems for some reason, bail out
|
|
if(subsys_count >= n_subsystems){
|
|
break;
|
|
}
|
|
}
|
|
|
|
// recalculate all ship subsystems
|
|
ship_recalc_subsys_strength( shipp );
|
|
|
|
// ai mode info
|
|
ubyte umode;
|
|
short submode;
|
|
ushort target_signature;
|
|
object *target_objp;
|
|
|
|
GET_DATA( umode );
|
|
GET_SHORT( submode );
|
|
GET_USHORT( target_signature );
|
|
|
|
if(shipp->ai_index >= 0){
|
|
Ai_info[shipp->ai_index].mode = umode;
|
|
Ai_info[shipp->ai_index].submode = submode;
|
|
|
|
// set this guys target objnum
|
|
target_objp = multi_get_network_object( target_signature );
|
|
if ( target_objp == NULL ){
|
|
Ai_info[shipp->ai_index].target_objnum = -1;
|
|
} else {
|
|
Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
|
|
}
|
|
}
|
|
|
|
// primary weapon energy
|
|
float weapon_energy_pct;
|
|
UNPACK_PERCENT(weapon_energy_pct);
|
|
shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct;
|
|
}
|
|
|
|
// support ship extra info
|
|
ubyte support_extra;
|
|
GET_DATA(support_extra);
|
|
if(support_extra){
|
|
ushort dock_sig;
|
|
int ai_flags, ai_mode, ai_submode;
|
|
|
|
// flag
|
|
GET_INT(ai_flags);
|
|
GET_INT(ai_mode);
|
|
GET_INT(ai_submode);
|
|
GET_USHORT(dock_sig);
|
|
|
|
// valid ship?
|
|
if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
|
|
Ai_info[shipp->ai_index].ai_flags = ai_flags;
|
|
Ai_info[shipp->ai_index].mode = ai_mode;
|
|
Ai_info[shipp->ai_index].submode = ai_submode;
|
|
|
|
object *objp = multi_get_network_object( dock_sig );
|
|
if(objp != NULL){
|
|
Ai_info[shipp->ai_index].support_ship_objnum = OBJ_INDEX(objp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// afterburner info
|
|
if ( (oo_flags & OO_AFTERBURNER_NEW) || Multi_oo_afterburn_hack ) {
|
|
// maybe turn them on
|
|
if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){
|
|
afterburners_start(pobjp);
|
|
}
|
|
|
|
// make sure the ab hack is reset before we read in new info
|
|
Multi_oo_afterburn_hack = false;
|
|
} else {
|
|
// maybe turn them off
|
|
if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
|
|
afterburners_stop(pobjp);
|
|
}
|
|
}
|
|
|
|
// primary info (only clients care about this)
|
|
if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
|
|
// what bank
|
|
if(oo_flags & OO_PRIMARY_BANK){
|
|
shipp->weapons.current_primary_bank = 1;
|
|
} else {
|
|
shipp->weapons.current_primary_bank = 0;
|
|
}
|
|
|
|
// linked or not
|
|
shipp->flags &= ~SF_PRIMARY_LINKED;
|
|
if(oo_flags & OO_PRIMARY_LINKED){
|
|
shipp->flags |= SF_PRIMARY_LINKED;
|
|
}
|
|
|
|
// trigger down or not - server doesn't care about this. he'll get it from clients anyway
|
|
shipp->flags &= ~SF_TRIGGER_DOWN;
|
|
if(oo_flags & OO_TRIGGER_DOWN){
|
|
shipp->flags |= SF_TRIGGER_DOWN;
|
|
}
|
|
}
|
|
|
|
// if we're the multiplayer server, set eye position and orient
|
|
if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){
|
|
pl->s_info.eye_pos = pobjp->pos;
|
|
pl->s_info.eye_orient = pobjp->orient;
|
|
}
|
|
|
|
// update the sequence #
|
|
shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
|
|
|
|
// flag the object as just updated
|
|
// pobjp->flags |= OF_JUST_UPDATED;
|
|
|
|
return offset;
|
|
}
|
|
|
|
// reset the timestamp appropriately for the passed in object
|
|
void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
|
|
{
|
|
int stamp = 0;
|
|
|
|
// if this is the guy's target,
|
|
if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
|
|
stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
|
|
} else {
|
|
// reset the timestamp appropriately
|
|
if(in_cone){
|
|
// base it upon range
|
|
switch(range){
|
|
case OO_NEAR:
|
|
stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
|
|
case OO_MIDRANGE:
|
|
stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
|
|
case OO_FAR:
|
|
stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
}
|
|
} else {
|
|
// base it upon range
|
|
switch(range){
|
|
case OO_NEAR:
|
|
stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
|
|
case OO_MIDRANGE:
|
|
stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
|
|
case OO_FAR:
|
|
stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// reset the timestamp for this object
|
|
if(objp->type == OBJ_SHIP){
|
|
Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp);
|
|
}
|
|
}
|
|
|
|
// reset the timestamp appropriately for the passed in object
|
|
void multi_oo_reset_status_timestamp(object *objp, int player_index)
|
|
{
|
|
Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
|
|
}
|
|
|
|
// reset the timestamp appropriately for the passed in object
|
|
void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
|
|
{
|
|
Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
|
|
}
|
|
|
|
// determine what needs to get sent for this player regarding the passed object, and when
|
|
int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data)
|
|
{
|
|
ubyte oo_flags;
|
|
int stamp;
|
|
int player_index;
|
|
vec3d player_eye;
|
|
vec3d obj_dot;
|
|
float eye_dot, dist;
|
|
int in_cone;
|
|
int range;
|
|
ship *shipp;
|
|
ship_info *sip;
|
|
ushort cur_pos_chksum = 0;
|
|
ushort cur_orient_chksum = 0;
|
|
|
|
// if the timestamp has elapsed for this guy, send stuff
|
|
player_index = NET_PLAYER_INDEX(pl);
|
|
if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
|
|
return 0;
|
|
}
|
|
|
|
// determine what the timestamp is for this object
|
|
if(obj->type == OBJ_SHIP){
|
|
stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
// stamp hasn't popped yet
|
|
if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){
|
|
return 0;
|
|
}
|
|
|
|
// if we're supposed to update this guy
|
|
|
|
// get the ship pointer
|
|
shipp = &Ships[obj->instance];
|
|
|
|
// get ship info pointer
|
|
sip = NULL;
|
|
if(shipp->ship_info_index >= 0){
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
}
|
|
|
|
// check dot products
|
|
player_eye = pl->s_info.eye_orient.vec.fvec;
|
|
vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
|
|
in_cone = 0;
|
|
if (!(IS_VEC_NULL(&obj_dot))) {
|
|
vm_vec_normalize(&obj_dot);
|
|
eye_dot = vm_vec_dot(&obj_dot, &player_eye);
|
|
in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;
|
|
}
|
|
|
|
// determine distance (near, medium, far)
|
|
vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
|
|
dist = vm_vec_mag(&obj_dot);
|
|
if(dist < OO_NEAR_DIST){
|
|
range = OO_NEAR;
|
|
} else if(dist < OO_MIDRANGE_DIST){
|
|
range = OO_MIDRANGE;
|
|
} else {
|
|
range = OO_FAR;
|
|
}
|
|
|
|
// reset the timestamp for the next update for this guy
|
|
multi_oo_reset_timestamp(pl, obj, range, in_cone);
|
|
|
|
// base oo_flags
|
|
oo_flags = OO_POS_NEW | OO_ORIENT_NEW;
|
|
|
|
// if its a small ship, add weapon link info
|
|
if((sip != NULL) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))){
|
|
// primary bank 0 or 1
|
|
if(shipp->weapons.current_primary_bank > 0){
|
|
oo_flags |= OO_PRIMARY_BANK;
|
|
}
|
|
|
|
// linked or not
|
|
if(shipp->flags & SF_PRIMARY_LINKED){
|
|
oo_flags |= OO_PRIMARY_LINKED;
|
|
}
|
|
|
|
// trigger down or not
|
|
if(shipp->flags & SF_TRIGGER_DOWN){
|
|
oo_flags |= OO_TRIGGER_DOWN;
|
|
}
|
|
}
|
|
|
|
// if the object's hull/shield timestamp has expired
|
|
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)){
|
|
oo_flags |= (OO_HULL_NEW);
|
|
|
|
// reset the timestamp
|
|
multi_oo_reset_status_timestamp(obj, player_index);
|
|
}
|
|
|
|
// if the object's hull/shield timestamp has expired
|
|
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)){
|
|
oo_flags |= OO_SUBSYSTEMS_AND_AI_NEW;
|
|
|
|
// reset the timestamp
|
|
multi_oo_reset_subsys_timestamp(obj, player_index);
|
|
}
|
|
|
|
// add info for a targeted object
|
|
if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
|
|
oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW);
|
|
}
|
|
// all other cases
|
|
else {
|
|
// add info which is contingent upon being "in front"
|
|
if(in_cone){
|
|
oo_flags |= OO_ORIENT_NEW;
|
|
}
|
|
}
|
|
|
|
// get current position and orient checksums
|
|
cur_pos_chksum = cf_add_chksum_short(cur_pos_chksum, (ubyte*)(&obj->pos), sizeof(vec3d));
|
|
cur_orient_chksum = cf_add_chksum_short(cur_orient_chksum, (ubyte*)(&obj->orient), sizeof(matrix));
|
|
|
|
// if position or orientation haven't changed
|
|
if((shipp->np_updates[player_index].pos_chksum != 0) && (shipp->np_updates[player_index].pos_chksum == cur_pos_chksum)){
|
|
// if we otherwise would have been sending it, keep track of it (debug only)
|
|
#ifndef NDEBUG
|
|
if(oo_flags & OO_POS_NEW){
|
|
multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE);
|
|
}
|
|
#endif
|
|
oo_flags &= ~(OO_POS_NEW);
|
|
}
|
|
if((shipp->np_updates[player_index].orient_chksum != 0) && (shipp->np_updates[player_index].orient_chksum == cur_orient_chksum)){
|
|
// if we otherwise would have been sending it, keep track of it (debug only)
|
|
#ifndef NDEBUG
|
|
if(oo_flags & OO_ORIENT_NEW){
|
|
multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE);
|
|
}
|
|
#endif
|
|
oo_flags &= ~(OO_ORIENT_NEW);
|
|
}
|
|
shipp->np_updates[player_index].pos_chksum = cur_pos_chksum;
|
|
shipp->np_updates[player_index].orient_chksum = cur_orient_chksum;
|
|
|
|
// pack stuff only if we have to
|
|
int packed = multi_oo_pack_data(pl, obj, oo_flags ,data);
|
|
|
|
// increment sequence #
|
|
Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++;
|
|
|
|
// bytes packed
|
|
return packed;
|
|
}
|
|
|
|
// process all other objects for this player
|
|
void multi_oo_process_all(net_player *pl)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte data_add[MAX_PACKET_SIZE];
|
|
ubyte stop;
|
|
int add_size;
|
|
int packet_size = 0;
|
|
int idx;
|
|
|
|
object *moveup;
|
|
|
|
// if the player has an invalid objnum..
|
|
if(pl->m_player->objnum < 0){
|
|
return;
|
|
}
|
|
|
|
object *targ_obj;
|
|
|
|
// build the list of ships to check against
|
|
multi_oo_build_ship_list(pl);
|
|
|
|
// do nothing if he has no object targeted, or if he has a weapon targeted
|
|
if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
|
|
// build the header
|
|
BUILD_HEADER(OBJECT_UPDATE);
|
|
|
|
// get a pointer to the object
|
|
targ_obj = &Objects[pl->s_info.target_objnum];
|
|
|
|
// run through the maybe_update function
|
|
add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
|
|
|
|
// copy in any relevant data
|
|
if(add_size){
|
|
stop = 0xff;
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
memcpy(data + packet_size, data_add, add_size);
|
|
packet_size += add_size;
|
|
}
|
|
} else {
|
|
// just build the header for the rest of the function
|
|
BUILD_HEADER(OBJECT_UPDATE);
|
|
}
|
|
|
|
idx = 0;
|
|
// rely on logical-AND shortcut evaluation to prevent array out-of-bounds read of OO_ship_index[idx]
|
|
while((idx < MAX_SHIPS) && (OO_ship_index[idx] >= 0)){
|
|
// if this guy is over his datarate limit, do nothing
|
|
if(multi_oo_rate_exceeded(pl)){
|
|
nprintf(("Network","Capping client\n"));
|
|
idx++;
|
|
|
|
continue;
|
|
}
|
|
|
|
// get the object
|
|
moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
|
|
|
|
// maybe send some info
|
|
add_size = multi_oo_maybe_update(pl, moveup, data_add);
|
|
|
|
// if this data is too much for the packet, send off what we currently have and start over
|
|
if(packet_size + add_size > OO_MAX_SIZE){
|
|
stop = 0x00;
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
multi_io_send(pl, data, packet_size);
|
|
pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
|
|
|
|
packet_size = 0;
|
|
BUILD_HEADER(OBJECT_UPDATE);
|
|
}
|
|
|
|
if(add_size){
|
|
stop = 0xff;
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
// copy in the data
|
|
memcpy(data + packet_size,data_add,add_size);
|
|
packet_size += add_size;
|
|
}
|
|
|
|
// next ship
|
|
idx++;
|
|
}
|
|
|
|
// if we have anything more than 3 byte in the packet, send the last one off
|
|
if(packet_size > 3){
|
|
stop = 0x00;
|
|
multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
multi_io_send(pl, data, packet_size);
|
|
pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
|
|
}
|
|
}
|
|
|
|
// process all object update details for this frame
|
|
void multi_oo_process()
|
|
{
|
|
int idx;
|
|
|
|
// process each player
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) /*&& !MULTI_OBSERVER(Net_players[idx])*/ ){
|
|
// now process the rest of the objects
|
|
multi_oo_process_all(&Net_players[idx]);
|
|
|
|
// do firing stuff for this player
|
|
if((Net_players[idx].m_player != NULL) && (Net_players[idx].m_player->objnum >= 0) && !(Net_players[idx].flags & NETINFO_FLAG_LIMBO) && !(Net_players[idx].flags & NETINFO_FLAG_RESPAWNING)){
|
|
if((Objects[Net_players[idx].m_player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].m_player->objnum].flags & OF_SHOULD_BE_DEAD)){
|
|
obj_player_fire_stuff( &Objects[Net_players[idx].m_player->objnum], Net_players[idx].m_player->ci );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// process incoming object update data
|
|
void multi_oo_process_update(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte stop;
|
|
int player_index;
|
|
int offset = HEADER_LENGTH;
|
|
net_player *pl = NULL;
|
|
|
|
// determine what player this came from
|
|
player_index = find_player_id(hinfo->id);
|
|
if(player_index != -1){
|
|
pl = &Net_players[player_index];
|
|
}
|
|
// otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
|
|
else {
|
|
pl = Net_player;
|
|
}
|
|
|
|
GET_DATA(stop);
|
|
|
|
while(stop == 0xff){
|
|
// process the data
|
|
offset += multi_oo_unpack_data(pl, data + offset);
|
|
|
|
GET_DATA(stop);
|
|
}
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
// initialize all object update timestamps (call whenever entering gameplay state)
|
|
void multi_oo_gameplay_init()
|
|
{
|
|
int cur, s_idx, idx;
|
|
//ship_obj *so;
|
|
ship *shipp;
|
|
/*
|
|
int num_ships = ship_get_num_ships();
|
|
|
|
if(num_ships <= 0){
|
|
return;
|
|
}
|
|
split = 3000 / num_ships;
|
|
*/
|
|
//split = 1;
|
|
//split = 150;
|
|
|
|
// server should setup initial update timestamps
|
|
// stagger initial updates over 3 seconds or so
|
|
cur = 0;
|
|
//for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
|
//if(Objects[so->objnum].type == OBJ_SHIP){
|
|
for(s_idx=0; s_idx<MAX_SHIPS; s_idx++){
|
|
shipp = &Ships[s_idx];
|
|
|
|
// update the timestamps
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
shipp->np_updates[idx].update_stamp = timestamp(cur);
|
|
shipp->np_updates[idx].status_update_stamp = timestamp(cur);
|
|
shipp->np_updates[idx].subsys_update_stamp = timestamp(cur);
|
|
shipp->np_updates[idx].seq = 0;
|
|
shipp->np_updates[idx].pos_chksum = 0;
|
|
shipp->np_updates[idx].orient_chksum = 0;
|
|
}
|
|
|
|
oo_arrive_time_count[shipp - Ships] = 0;
|
|
oo_interp_count[shipp - Ships] = 0;
|
|
|
|
// increment the time
|
|
// cur += split;
|
|
}
|
|
//}
|
|
|
|
// reset datarate stamp now
|
|
extern int OO_gran;
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
|
|
}
|
|
}
|
|
|
|
// send control info for a client (which is basically a "reverse" object update)
|
|
void multi_oo_send_control_info()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], stop;
|
|
ubyte data_add[MAX_PACKET_SIZE];
|
|
ubyte oo_flags;
|
|
int add_size;
|
|
int packet_size = 0;
|
|
|
|
// if I'm dying or my object type is not a ship, bail here
|
|
if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
|
|
return;
|
|
}
|
|
|
|
// build the header
|
|
BUILD_HEADER(OBJECT_UPDATE);
|
|
|
|
// pos and orient always
|
|
oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
|
|
|
|
// pack the appropriate info into the data
|
|
add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
|
|
|
|
// copy in any relevant data
|
|
if(add_size){
|
|
stop = 0xff;
|
|
multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
|
|
|
|
ADD_DATA(stop);
|
|
|
|
memcpy(data + packet_size, data_add, add_size);
|
|
packet_size += add_size;
|
|
}
|
|
|
|
// add the final stop byte
|
|
stop = 0x0;
|
|
multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
// increment sequence #
|
|
Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
|
|
|
|
// send to the server
|
|
if(Netgame.server != NULL){
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// Sends a packet from the server to the client, syncing the player's position/orientation to the
|
|
// Server's. Allows for use of certain SEXPs in multiplayer.
|
|
void multi_oo_send_changed_object(object *changedobj)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], stop;
|
|
ubyte data_add[MAX_PACKET_SIZE];
|
|
ubyte oo_flags;
|
|
int add_size;
|
|
int packet_size = 0;
|
|
int idx = 0;
|
|
#ifndef NDEBUG
|
|
nprintf(("Network","Attempting to affect player object.\n"));
|
|
#endif
|
|
for (; idx < MAX_PLAYERS; idx++)
|
|
{
|
|
if( changedobj == &(Objects[Net_players[idx].m_player->objnum]) ) {
|
|
break;
|
|
}
|
|
}
|
|
#ifndef NDEBUG
|
|
nprintf(("Network","Index for changed object found: [%d].\n",idx));
|
|
#endif
|
|
if( idx >= MAX_PLAYERS ) {
|
|
return;
|
|
}
|
|
// build the header
|
|
BUILD_HEADER(OBJECT_UPDATE);
|
|
|
|
// pos and orient always
|
|
oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
|
|
|
|
// pack the appropriate info into the data
|
|
add_size = multi_oo_pack_data(&Net_players[idx], changedobj, oo_flags, data_add);
|
|
|
|
// copy in any relevant data
|
|
if(add_size){
|
|
stop = 0xff;
|
|
multi_rate_add(idx, "stp", 1);
|
|
|
|
ADD_DATA(stop);
|
|
|
|
memcpy(data + packet_size, data_add, add_size);
|
|
packet_size += add_size;
|
|
}
|
|
|
|
// add the final stop byte
|
|
stop = 0x0;
|
|
multi_rate_add(idx, "stp", 1);
|
|
ADD_DATA(stop);
|
|
|
|
// increment sequence #
|
|
// Player_ship->np_updates[idx].seq++;
|
|
|
|
multi_io_send(&Net_players[idx], data, packet_size);
|
|
}
|
|
|
|
|
|
// display any oo info on the hud
|
|
void multi_oo_display()
|
|
{
|
|
#ifndef NDEBUG
|
|
#endif
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// DATARATE DEFINES/VARS
|
|
//
|
|
|
|
// low object update datarate limit
|
|
#define OO_LIMIT_LOW 1800
|
|
#define OO_LIMIT_MED 3400
|
|
#define OO_LIMIT_HIGH 100000000
|
|
|
|
// timestamp for sending control info (movement only - we'll send button info all the time)
|
|
#define OO_CIRATE 85 // 15x a second
|
|
int Multi_cirate_stamp = -1; // timestamp for waiting on control info time
|
|
int Multi_cirate_can_send = 1; // if we can send control info this frame
|
|
|
|
// global max rates
|
|
int OO_server_rate = -1; // max _total_ bandwidth to send to all clients
|
|
int OO_client_rate = -1; // max bandwidth to go to an individual client
|
|
|
|
// update timestamp for server datarate checking
|
|
#define RATE_UPDATE_TIME 1250 // in ms
|
|
int OO_server_rate_stamp = -1;
|
|
|
|
// bandwidth granularity
|
|
int OO_gran = 1;
|
|
DCF(oog, "Sets bandwidth granularity (Multiplayer)")
|
|
{
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf("Usage: oog <OO_gran>\n");
|
|
dc_printf("Sets bandwidth granularity\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf("Current Granularity is '%i' (default is 1)", OO_gran);
|
|
return;
|
|
}
|
|
|
|
dc_stuff_int(&OO_gran);
|
|
dc_printf("Ganularity set to %i", OO_gran);
|
|
}
|
|
|
|
// process datarate limiting stuff for the server
|
|
void multi_oo_server_process();
|
|
|
|
// process datarate limiting stuff for the client
|
|
void multi_oo_client_process();
|
|
|
|
// update the server datarate
|
|
void multi_oo_update_server_rate();
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// DATARATE FUNCTIONS
|
|
//
|
|
|
|
// process all object update datarate details
|
|
void multi_oo_rate_process()
|
|
{
|
|
// if I have no valid player, drop out here
|
|
if(Net_player == NULL){
|
|
return;
|
|
}
|
|
|
|
// if we're not in mission, don't do anything
|
|
if(!(Game_mode & GM_IN_MISSION)){
|
|
return;
|
|
}
|
|
|
|
// if I'm the server of a game, process server stuff
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_oo_server_process();
|
|
}
|
|
// otherwise process client-side stuff
|
|
else {
|
|
multi_oo_client_process();
|
|
}
|
|
}
|
|
|
|
// process datarate limiting stuff for the server
|
|
void multi_oo_server_process()
|
|
{
|
|
int idx;
|
|
|
|
// go through all players
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
|
|
// if his timestamp is -1 or has expired, reset it and zero his rate byte count
|
|
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)) ){
|
|
Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
|
|
Net_players[idx].s_info.rate_bytes = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine if we should be updating the server datarate
|
|
if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){
|
|
// reset the timestamp
|
|
OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
|
|
|
|
// update the server datarate
|
|
multi_oo_update_server_rate();
|
|
|
|
// nprintf(("Network","UPDATING SERVER DATARATE\n"));
|
|
}
|
|
}
|
|
|
|
// process datarate limiting stuff for the client
|
|
void multi_oo_client_process()
|
|
{
|
|
// if the timestamp is -1 or has elapsed, reset it
|
|
if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){
|
|
Multi_cirate_can_send = 1;
|
|
Multi_cirate_stamp = timestamp(OO_CIRATE);
|
|
}
|
|
}
|
|
|
|
|
|
// datarate limiting system for server -------------------------------------
|
|
|
|
// initialize the rate limiting system for all players
|
|
void multi_oo_rate_init_all()
|
|
{
|
|
int idx;
|
|
|
|
// if I don't have a net_player, bail here
|
|
if(Net_player == NULL){
|
|
return;
|
|
}
|
|
|
|
// if I'm the server of the game
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// go through all players
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx])){
|
|
multi_oo_rate_init(&Net_players[idx]);
|
|
}
|
|
}
|
|
|
|
OO_server_rate_stamp = -1;
|
|
}
|
|
// if i'm the client, initialize my control info datarate stuff
|
|
else {
|
|
Multi_cirate_stamp = -1;
|
|
Multi_cirate_can_send = 1;
|
|
}
|
|
}
|
|
|
|
// initialize the rate limiting for the passed in player
|
|
void multi_oo_rate_init(net_player *pl)
|
|
{
|
|
// reinitialize his datarate timestamp
|
|
pl->s_info.rate_stamp = -1;
|
|
pl->s_info.rate_bytes = 0;
|
|
}
|
|
|
|
// if the given net-player has exceeded his datarate limit
|
|
int multi_oo_rate_exceeded(net_player *pl)
|
|
{
|
|
int rate_compare;
|
|
|
|
// check against the guy's object update level
|
|
switch(pl->p_info.options.obj_update_level){
|
|
// low update level
|
|
case OBJ_UPDATE_LOW:
|
|
// the low object update limit
|
|
rate_compare = OO_LIMIT_LOW;
|
|
break;
|
|
|
|
// medium update level
|
|
case OBJ_UPDATE_MEDIUM:
|
|
// the low object update limit
|
|
rate_compare = OO_LIMIT_MED;
|
|
break;
|
|
|
|
// high update level - super high datarate (no capping, just intelligent updating)
|
|
case OBJ_UPDATE_HIGH:
|
|
rate_compare = OO_LIMIT_HIGH;
|
|
break;
|
|
|
|
// LAN - no rate max
|
|
case OBJ_UPDATE_LAN:
|
|
return 0;
|
|
|
|
// default level
|
|
default:
|
|
Int3();
|
|
rate_compare = OO_LIMIT_LOW;
|
|
break;
|
|
}
|
|
|
|
// if the server global rate PER CLIENT (OO_client_rate) is actually lower
|
|
if(OO_client_rate < rate_compare){
|
|
rate_compare = OO_client_rate;
|
|
}
|
|
|
|
// compare his bytes sent against the allowable amount
|
|
if(pl->s_info.rate_bytes >= rate_compare){
|
|
return 1;
|
|
}
|
|
|
|
// we're allowed to send
|
|
return 0;
|
|
}
|
|
|
|
// if it is ok for me to send a control info (will be ~N times a second)
|
|
int multi_oo_cirate_can_send()
|
|
{
|
|
// if we're allowed to send
|
|
if(Multi_cirate_can_send){
|
|
Multi_cirate_can_send = 0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// dynamically update the server capped bandwidth rate
|
|
void multi_oo_update_server_rate()
|
|
{
|
|
int num_connections;
|
|
|
|
// bail conditions
|
|
if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
return;
|
|
}
|
|
|
|
// get the # of connections
|
|
num_connections = multi_num_connections();
|
|
if(!(Game_mode & GM_STANDALONE_SERVER)){
|
|
num_connections--;
|
|
}
|
|
// make sure we always pretend there's at least one guy available
|
|
if(num_connections <= 0){
|
|
num_connections = 1;
|
|
}
|
|
|
|
// set the data rate
|
|
switch(Net_player->p_info.options.obj_update_level){
|
|
// LAN update level
|
|
case OBJ_UPDATE_LAN:
|
|
// set to something super big so we don't limit anything
|
|
OO_server_rate = 500000000;
|
|
break;
|
|
|
|
// high update level
|
|
case OBJ_UPDATE_HIGH:
|
|
// set to 0 so we don't limit anything
|
|
OO_server_rate = Multi_options_g.datarate_cap;
|
|
break;
|
|
|
|
// medium update level
|
|
case OBJ_UPDATE_MEDIUM:
|
|
// set the rate to be "medium" update level
|
|
OO_server_rate = OO_LIMIT_MED;
|
|
break;
|
|
|
|
// low update level
|
|
case OBJ_UPDATE_LOW:
|
|
// set the rate to be the "low" update level
|
|
OO_server_rate = OO_LIMIT_LOW;
|
|
break;
|
|
|
|
default:
|
|
Int3();
|
|
return;
|
|
}
|
|
|
|
// set the individual client level
|
|
OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
|
|
}
|
|
|
|
// reset all sequencing info (obsolete for new object update stuff)
|
|
void multi_oo_reset_sequencing()
|
|
{
|
|
}
|
|
|
|
// is this object one which needs to go through the interpolation
|
|
int multi_oo_is_interp_object(object *objp)
|
|
{
|
|
// if not multiplayer, skip it
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return 0;
|
|
}
|
|
|
|
// if its not a ship, skip it
|
|
if(objp->type != OBJ_SHIP){
|
|
return 0;
|
|
}
|
|
|
|
// other bogus cases
|
|
if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return 0;
|
|
}
|
|
|
|
// if I'm a client and this is not me, I need to interp it
|
|
if(!MULTIPLAYER_MASTER){
|
|
if(objp != Player_obj){
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// servers only interpolate other player ships
|
|
if(!(objp->flags & OF_PLAYER_SHIP)){
|
|
return 0;
|
|
}
|
|
|
|
// here we know its a player ship - is it mine?
|
|
if(objp == Player_obj){
|
|
return 0;
|
|
}
|
|
|
|
// interp it
|
|
return 1;
|
|
}
|
|
|
|
// interp
|
|
void multi_oo_interp(object *objp)
|
|
{
|
|
// make sure its a valid ship
|
|
Assert(Game_mode & GM_MULTIPLAYER);
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
// increment his approx "next" time
|
|
oo_arrive_time_next[objp->instance] += flFrametime;
|
|
|
|
// do stream weapon firing for this ship
|
|
Assert(objp != Player_obj);
|
|
if(objp != Player_obj){
|
|
ship_fire_primary(objp, 1, 0);
|
|
}
|
|
|
|
// if this ship doesn't have enough data points yet, skip it
|
|
if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){
|
|
return;
|
|
}
|
|
|
|
// store the magnitude of his velocity
|
|
// float vel_mag = vm_vec_mag(&objp->phys_info.vel);
|
|
|
|
// determine how far along we are (0.0 to 1.0) until we should be getting the next packet
|
|
float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance];
|
|
|
|
// gr_set_color_fast(&Color_bright);
|
|
// gr_printf(100, 10, "%f\n", t);
|
|
|
|
// we've overshot. hmm. just keep the sim running I guess
|
|
if(t > 1.0f){
|
|
physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
|
|
return;
|
|
}
|
|
|
|
// otherwise, blend the two curves together to get the new point
|
|
float u = 0.5f + (t * 0.5f);
|
|
vec3d p_bad, p_good;
|
|
oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u);
|
|
oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u);
|
|
vm_vec_scale(&p_good, t);
|
|
vm_vec_scale(&p_bad, 1.0f - t);
|
|
vm_vec_add(&objp->pos, &p_bad, &p_good);
|
|
|
|
// set new velocity
|
|
// vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
|
|
|
|
// run the sim for rotation
|
|
physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
|
|
|
|
// blend velocity vectors together with an average weight
|
|
/*
|
|
vec3d v_bad, v_good;
|
|
oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0);
|
|
oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0);
|
|
|
|
// t -= 1.0f;
|
|
vm_vec_scale(&v_good, t);
|
|
vm_vec_scale(&v_bad, 1.0f - t);
|
|
vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good);
|
|
|
|
// run the sim
|
|
physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
|
|
*/
|
|
|
|
/*
|
|
vec3d v_bad, v_good;
|
|
oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0);
|
|
oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0);
|
|
|
|
// t -= 1.0f;
|
|
vm_vec_scale(&v_good, t);
|
|
vm_vec_scale(&v_bad, 1.0f - t);
|
|
vm_vec_avg(&objp->pos, &v_bad, &v_good);
|
|
|
|
// run the sim
|
|
// physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
|
|
physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
|
|
*/
|
|
}
|
|
|
|
float oo_error = 0.8f;
|
|
DCF(oo_error, "Sets error factor for flight path prediction physics (Multiplayer)")
|
|
{
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf("Usage: oo_error <value>\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf("oo_error is currently %f", oo_error);
|
|
return;
|
|
}
|
|
|
|
dc_stuff_float(&oo_error);
|
|
|
|
dc_printf("oo_error set to %f", oo_error);
|
|
}
|
|
|
|
void multi_oo_calc_interp_splines(int ship_index, vec3d *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vec3d *new_pos, matrix *new_orient, physics_info *new_phys_info)
|
|
{
|
|
vec3d a, b, c;
|
|
matrix m_copy;
|
|
physics_info p_copy;
|
|
vec3d *pts[3] = {&a, &b, &c};
|
|
|
|
// average time between packets
|
|
float avg_diff = oo_arrive_time_avg_diff[ship_index];
|
|
|
|
// would this cause us to rubber-band?
|
|
vec3d v_norm = cur_phys_info->vel;
|
|
vec3d v_dir;
|
|
vm_vec_sub(&v_dir, new_pos, cur_pos);
|
|
if(!IS_VEC_NULL_SQ_SAFE(&v_norm) && !IS_VEC_NULL_SQ_SAFE(&v_dir)){
|
|
vm_vec_normalize(&v_dir);
|
|
vm_vec_normalize(&v_norm);
|
|
if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){
|
|
*new_pos = *cur_pos;
|
|
}
|
|
}
|
|
|
|
// get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether
|
|
a = oo_interp_points[ship_index][0];
|
|
b = *cur_pos;
|
|
c = *cur_pos;
|
|
m_copy = *cur_orient;
|
|
p_copy = *cur_phys_info;
|
|
physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error); // next point, assuming we followed our current path
|
|
oo_interp_splines[ship_index][0].bez_set_points(3, pts);
|
|
|
|
// get the spline representing where this new point tells us we'd be heading
|
|
a = oo_interp_points[ship_index][0]; //-V519
|
|
b = oo_interp_points[ship_index][1]; //-V519
|
|
c = oo_interp_points[ship_index][1];
|
|
m_copy = *new_orient;
|
|
p_copy = *new_phys_info;
|
|
physics_sim(&c, &m_copy, &p_copy, avg_diff); // next point, given this new info
|
|
oo_interp_splines[ship_index][1].bez_set_points(3, pts);
|
|
|
|
// now we've got a spline representing our "new" path and where we would've gone had we been perfect before
|
|
// we'll modify our velocity to move along a blend of these splines.
|
|
}
|
|
|
|
void oo_update_time()
|
|
{
|
|
}
|
|
|
|
int display_oo_bez = 0;
|
|
DCF(bez, "Toggles rendering of player ship trajectory interpolation splines (Multiplayer) *disabled*")
|
|
{
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf("Rendering of interpolation splines is '%s'", display_oo_bez ? "ON" : "OFF");
|
|
return;
|
|
}
|
|
|
|
display_oo_bez = !display_oo_bez;
|
|
|
|
dc_printf("%showing interp splines", display_oo_bez ? "S" : "Not s");
|
|
}
|
|
|
|
void oo_display()
|
|
{
|
|
/* int idx;
|
|
|
|
|
|
gr_set_color_fast(&Color_bright);
|
|
|
|
for(idx=0; idx<MAX_SHIPS; idx++){
|
|
// invalid ship
|
|
if(Ships[idx].objnum < 0){
|
|
continue;
|
|
}
|
|
|
|
// time between updates
|
|
if( (oo_arrive_time_count[idx] == 5) && (idx != (Player_ship - Ships)) ){
|
|
gr_printf(20, 40, "avg time between updates : %f", oo_arrive_time_avg_diff[idx]);
|
|
}
|
|
|
|
// interpolation splines
|
|
if( (oo_interp_count[idx] == 2) && (display_oo_bez) ){
|
|
oo_interp_splines[idx][0].bez_render(10, &Color_bright_red); // bad path
|
|
oo_interp_splines[idx][1].bez_render(10, &Color_bright_green); // good path
|
|
}
|
|
}
|
|
*/
|
|
}
|