mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 17:48:46 -10:00
Does this by refactoring how the timing is calculated. It also removes the Next Arrival Time Array, which does not actually do a good job of calculating it, anyway. We can now rely on the other oo_arrive_time arrays exclusively by subtracting from current time. Squashed Commits: 1. Use function to pull FrametimeOverall to keep from breaking Fred. 2. Remove unsed parameter from multi_oo_calc_interp_splines 3. And also fix the corresponding declarations and functions calls.
2049 lines
58 KiB
C++
2049 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 <algorithm>
|
|
|
|
#include "network/multi_obj.h"
|
|
#include "globalincs/globals.h"
|
|
#include "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 "object/objectshield.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
|
|
|
|
// interp stuff
|
|
int oo_interp_count[MAX_SHIPS];
|
|
vec3d oo_interp_points[MAX_SHIPS][2];
|
|
bez_spline oo_interp_splines[MAX_SHIPS];
|
|
void multi_oo_calc_interp_splines(int ship_index, vec3d *cur_pos, 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;
|
|
|
|
bool multi_oo_sort_func(const short &index1, const short &index2)
|
|
{
|
|
object *obj1, *obj2;
|
|
float dist1, dist2;
|
|
float dot1, dot2;
|
|
vec3d v1, v2;
|
|
vec3d vn1, vn2;
|
|
|
|
// 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 false;
|
|
}
|
|
|
|
// 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_dot(&OO_player_obj->orient.vec.fvec, &vn1);
|
|
dot2 = vm_vec_dot(&OO_player_obj->orient.vec.fvec, &vn2);
|
|
|
|
// objects in front take precedence
|
|
if((dot1 < 0.0f) && (dot2 >= 0.0f)){
|
|
return false;
|
|
} else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
|
|
return true;
|
|
}
|
|
|
|
// otherwise go by distance
|
|
return (dist1 < dist2);
|
|
}
|
|
|
|
// 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[Object::Object_Flags::Should_be_dead]){
|
|
continue;
|
|
}
|
|
|
|
// don't send info for dying ships
|
|
if (Ships[Objects[moveup->objnum].instance].flags[Ship::Ship_Flags::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 < static_cast<int>(Ship_info.size())) && (Ship_info[Ships[Objects[moveup->objnum].instance].ship_info_index].flags[Ship::Info_Flags::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 sort the thing here
|
|
OO_player_obj = player_obj;
|
|
if (OO_sort) {
|
|
std::sort(OO_ship_index, OO_ship_index + ship_index, 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[AI::AI_Flags::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[Ship::Ship_Flags::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[Ship::Ship_Flags::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 );
|
|
}
|
|
}
|
|
|
|
// 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) { std::uint8_t upercent; if(v < 0.0f){v = 0.0f;} upercent = (v * 255.0f) <= 255.0f ? (std::uint8_t)(v * 255.0f) : (std::uint8_t)255; memcpy(data + packet_size + header_bytes, &upercent, sizeof(std::uint8_t)); packet_size++; }
|
|
#define PACK_BYTE(v) { memcpy( data + packet_size + header_bytes, &v, 1 ); packet_size += 1; }
|
|
#define PACK_SHORT(v) { std::int16_t swap = INTEL_SHORT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(std::int16_t) ); packet_size += sizeof(std::int16_t); }
|
|
#define PACK_USHORT(v) { std::uint16_t swap = INTEL_SHORT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(std::uint16_t) ); packet_size += sizeof(std::uint16_t); }
|
|
#define PACK_INT(v) { std::int32_t swap = INTEL_INT(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(std::int32_t) ); packet_size += sizeof(std::int32_t); }
|
|
#define PACK_ULONG(v) { std::uint64_t swap = INTEL_LONG(v); memcpy( data + packet_size + header_bytes, &swap, sizeof(std::uint64_t) ); packet_size += sizeof(std::uint64_t); }
|
|
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 = shield_get_max_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 &= ~OO_AFTERBURNER_NEW;
|
|
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[Ship::Info_Flags::Support]) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
|
|
ushort dock_sig;
|
|
|
|
// flag
|
|
support_extra = 1;
|
|
PACK_BYTE( support_extra );
|
|
PACK_ULONG( Ai_info[shipp->ai_index].ai_flags.to_u64() );
|
|
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.set(Ship::Ship_Flags::Trigger_down);
|
|
} else {
|
|
shipp->flags.remove(Ship::Ship_Flags::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.remove(Ship::Ship_Flags::Primary_linked);
|
|
if(in_flags & OOC_PRIMARY_LINKED){
|
|
shipp->flags.set(Ship::Ship_Flags::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;
|
|
Ai_info[shipp->ai_index].ai_flags.set(AI::AI_Flags::Seek_lock, (in_flags & OOC_TARGET_SEEK_LOCK) != 0);
|
|
}
|
|
|
|
// 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 >= static_cast<int>(Ship_info.size()))) {
|
|
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;
|
|
}
|
|
|
|
// 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(SHIP_INDEX(shipp), &pobjp->pos, &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 = shield_get_max_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[Ship::Subsystem_Flags::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_mode, ai_submode;
|
|
std::uint64_t ai_flags;
|
|
|
|
// flag
|
|
GET_ULONG(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.from_u64(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.remove(Ship::Ship_Flags::Primary_linked);
|
|
if(oo_flags & OO_PRIMARY_LINKED){
|
|
shipp->flags.set(Ship::Ship_Flags::Primary_linked);
|
|
}
|
|
|
|
// trigger down or not - server doesn't care about this. he'll get it from clients anyway
|
|
shipp->flags.remove(Ship::Ship_Flags::Trigger_down);
|
|
if(oo_flags & OO_TRIGGER_DOWN){
|
|
shipp->flags.set(Ship::Ship_Flags::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->is_fighter_bomber())){
|
|
// primary bank 0 or 1
|
|
if(shipp->weapons.current_primary_bank > 0){
|
|
oo_flags |= OO_PRIMARY_BANK;
|
|
}
|
|
|
|
// linked or not
|
|
if(shipp->flags[Ship::Ship_Flags::Primary_linked]){
|
|
oo_flags |= OO_PRIMARY_LINKED;
|
|
}
|
|
|
|
// trigger down or not
|
|
if(shipp->flags[Ship::Ship_Flags::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[Object::Object_Flags::Player_ship]) && !(Objects[Net_players[idx].m_player->objnum].flags[Object::Object_Flags::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[Ship::Ship_Flags::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() - 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[Object::Object_Flags::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;
|
|
}
|
|
|
|
// Now that we know we have a valid ship, do stream weapon firing for this ship before we do anything else that makes us abort
|
|
Assert(objp != Player_obj);
|
|
if (objp != Player_obj) {
|
|
ship_fire_primary(objp, 1, 0);
|
|
}
|
|
|
|
// if this ship doesn't have enough data points yet, pretend it's a normal ship and skip it
|
|
if(oo_interp_count[objp->instance] < 2){
|
|
physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
|
|
return;
|
|
}
|
|
|
|
extern fix game_get_overall_frametime();
|
|
|
|
// Cyborg17 - Here's the new timing calculation: we subtract the last packet's arrival time oo_arrive_time[objp->instance][oo_arrive_time_count[objp->instance] - 1]
|
|
// from the current frame time (f2fl(game_get_overall_frametime()) to see how long it's been, and then we divide by the average difference in time. This gives us a
|
|
// percent that tells us, for example, "35% of the time has elapsed until the next packet."
|
|
float t = (f2fl(game_get_overall_frametime()) - oo_arrive_time[objp->instance][oo_arrive_time_count[objp->instance] - 1]) / 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
|
|
// Cyborg17 - I've adjusted this percentage up to 125% because we should at least have a little wiggle room for slightly late packets.
|
|
// For example, if I have 4 previous packets all arrive with the same timing and this one is the tiniest bit slower than them, having the cutoff
|
|
// just at average would not let us consider those packets.
|
|
if(t > 1.25f){
|
|
physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
|
|
return;
|
|
// in case they are slightly late, we'll just use the last good point.
|
|
} else if (t > 1.0f) {
|
|
t = 1.0f;
|
|
}
|
|
|
|
// Cyborg17 - we are no longer blending the two curves. I'm not sure *how*, but they were somehow making the interpolation look
|
|
// less erratic when the timing was messed up in the first place. Now we take the *good* curve at its word.
|
|
|
|
float u = 0.5f + (t * 0.5f);
|
|
vec3d interp_point;
|
|
oo_interp_splines[objp->instance].bez_get_point(&interp_point, u);
|
|
vm_vec_scale(&interp_point, t);
|
|
//vm_vec_scale(&p_bad, 1.0f - t);
|
|
objp->pos = interp_point;
|
|
|
|
// set new velocity
|
|
// vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
|
|
|
|
// run the sim for rotation
|
|
// TODO: add a bezier for rot_vel. It was working really well previously, but now it looks slightly choppy.
|
|
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, 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_dot(&v_dir, &v_norm) < 0.0f){
|
|
*new_pos = *cur_pos;
|
|
}
|
|
}
|
|
|
|
// 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].bez_set_points(3, pts);
|
|
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
*/
|
|
}
|