mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* add new conditions * don't worry about it * implement new conditions * bugfixing * handle pokethrough differently * clang for the clang god * appeasement * to the last i grapple with thee * from hell's heart etc * bugfixing * support hits while dying * fix shield math, cleanup * fix memory error
9291 lines
258 KiB
C++
9291 lines
258 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 <climits>
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "network/multimsgs.h"
|
|
#include "network/multiutil.h"
|
|
#include "network/multiui.h"
|
|
#include "network/multi.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "gamesequence/gamesequence.h"
|
|
#include "hud/hudets.h"
|
|
#include "hud/hudmessage.h"
|
|
#include "hud/hudsquadmsg.h"
|
|
#include "freespace.h"
|
|
#include "io/timer.h"
|
|
#include "mission/missiongoals.h"
|
|
#include "mission/missionlog.h"
|
|
#include "mission/missionmessage.h"
|
|
#include "network/stand_gui.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "ship/shiphit.h"
|
|
#include "render/3d.h"
|
|
#include "playerman/player.h"
|
|
#include "debris/debris.h"
|
|
#include "missionui/missionweaponchoice.h"
|
|
#include "missionui/missionshipchoice.h"
|
|
#include "ship/shipfx.h"
|
|
#include "popup/popup.h"
|
|
#include "network/multi_ingame.h"
|
|
#include "network/multiteamselect.h"
|
|
#include "ai/aigoals.h"
|
|
#include "ai/ai.h"
|
|
#include "network/multi_campaign.h"
|
|
#include "network/multi_team.h"
|
|
#include "network/multi_respawn.h"
|
|
#include "network/multi_observer.h"
|
|
#include "network/multi_obj.h"
|
|
#include "asteroid/asteroid.h"
|
|
#include "network/multi_pmsg.h"
|
|
#include "object/object.h"
|
|
#include "object/objectshield.h"
|
|
#include "stats/medals.h"
|
|
#include "ship/ship.h"
|
|
#include "weapon/weapon.h"
|
|
#include "hud/hudreticle.h"
|
|
#include "network/multi_pause.h"
|
|
#include "network/multi_endgame.h"
|
|
#include "missionui/missiondebrief.h"
|
|
#include "network/multi_log.h"
|
|
#include "weapon/emp.h"
|
|
#include "weapon/muzzleflash.h"
|
|
#include "network/multi_kick.h"
|
|
#include "cmdline/cmdline.h"
|
|
#include "weapon/flak.h"
|
|
#include "weapon/beam.h"
|
|
#include "network/multi_rate.h"
|
|
#include "nebula/neblightning.h"
|
|
#include "hud/hud.h"
|
|
#include "missionui/missionscreencommon.h"
|
|
#include "mission/missionbriefcommon.h"
|
|
#include "network/multi_log.h"
|
|
#include "object/objectdock.h"
|
|
#include "cmeasure/cmeasure.h"
|
|
#include "parse/sexp.h"
|
|
#include "network/multi_fstracker.h"
|
|
#include "network/multi_sw.h"
|
|
#include "network/multi_sexp.h"
|
|
#include "network/multi_mdns.h"
|
|
#include "mission/missiongoals.h"
|
|
#include "network/multi_interpolate.h"
|
|
#include "network/multi_turret_manager.h"
|
|
|
|
// #define _MULTI_SUPER_WACKY_COMPRESSION
|
|
|
|
#ifdef _MULTI_SUPER_WACKY_COMPRESSION
|
|
#define BITS 15
|
|
#define MAX_CODE ( ( 1 << BITS ) - 1 )
|
|
#define TABLE_SIZE 35023L
|
|
#define END_OF_STREAM 256
|
|
#define BUMP_CODE 257
|
|
#define FLUSH_CODE 258
|
|
#define FIRST_CODE 259
|
|
#define UNUSED -1
|
|
|
|
typedef struct {
|
|
int code_value;
|
|
int parent_code;
|
|
char character;
|
|
} DICTIONARY;
|
|
|
|
static DICTIONARY dict[TABLE_SIZE];
|
|
static char decode_stack[TABLE_SIZE];
|
|
static uint next_code;
|
|
static int current_code_bits;
|
|
static uint next_bump_code;
|
|
|
|
typedef struct BitBuf {
|
|
ubyte mask;
|
|
int rack;
|
|
ubyte *data;
|
|
} BitBuf;
|
|
|
|
void output_bits( BitBuf *bitbuf, uint code, int count )
|
|
{
|
|
uint mask;
|
|
|
|
mask = 1L << ( count - 1 );
|
|
while ( mask != 0) {
|
|
if ( mask & code )
|
|
bitbuf->rack |= bitbuf->mask;
|
|
bitbuf->mask >>= 1;
|
|
if ( bitbuf->mask == 0 ) {
|
|
*bitbuf->data++=(ubyte)bitbuf->rack;
|
|
bitbuf->rack = 0;
|
|
bitbuf->mask = 0x80;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
|
|
uint input_bits( BitBuf *bitbuf, int bit_count )
|
|
{
|
|
uint mask;
|
|
uint return_value;
|
|
|
|
mask = 1L << ( bit_count - 1 );
|
|
return_value = 0;
|
|
while ( mask != 0) {
|
|
if ( bitbuf->mask == 0x80 ) {
|
|
bitbuf->rack = *bitbuf->data++;
|
|
if ( bitbuf->rack == EOF )
|
|
return END_OF_STREAM;
|
|
}
|
|
if ( bitbuf->rack & bitbuf->mask )
|
|
return_value |= mask;
|
|
mask >>= 1;
|
|
bitbuf->mask >>= 1;
|
|
if ( bitbuf->mask == 0 )
|
|
bitbuf->mask = 0x80;
|
|
}
|
|
return( return_value );
|
|
}
|
|
|
|
|
|
static void InitializeDictionary()
|
|
{
|
|
uint i;
|
|
|
|
for ( i = 0 ; i < TABLE_SIZE ; i++ )
|
|
dict[i].code_value = UNUSED;
|
|
|
|
next_code = FIRST_CODE;
|
|
current_code_bits = 9;
|
|
next_bump_code = 511;
|
|
|
|
}
|
|
|
|
static uint find_child_node( int parent_code, int child_character )
|
|
{
|
|
uint index;
|
|
int offset;
|
|
|
|
index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
|
|
if ( index == 0 )
|
|
offset = 1;
|
|
else
|
|
offset = TABLE_SIZE - index;
|
|
for ( ; ; ) {
|
|
if ( dict[ index ].code_value == UNUSED )
|
|
return( (uint) index );
|
|
if ( dict[ index ].parent_code == parent_code &&
|
|
dict[ index ].character == (char) child_character )
|
|
return( index );
|
|
if ( (int) index >= offset )
|
|
index -= offset;
|
|
else
|
|
index += TABLE_SIZE - offset;
|
|
}
|
|
}
|
|
|
|
|
|
static uint decode_string( uint count, uint code )
|
|
{
|
|
while ( code > 255 ) {
|
|
decode_stack[ count++ ] = dict[ code ].character;
|
|
code = dict[ code ].parent_code;
|
|
}
|
|
decode_stack[ count++ ] = (char) code;
|
|
return( count );
|
|
}
|
|
|
|
int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
|
|
{
|
|
BitBuf output;
|
|
int character;
|
|
int string_code;
|
|
uint index;
|
|
int i;
|
|
|
|
// Init output bit buffer
|
|
output.rack = 0;
|
|
output.mask = 0x80;
|
|
output.data = outputbuf;
|
|
|
|
InitializeDictionary();
|
|
|
|
string_code = *inputbuf++;
|
|
|
|
for ( i=1 ; i<input_size ; i++ ) {
|
|
character = *inputbuf++;
|
|
index = find_child_node( string_code, character );
|
|
if ( dict[ index ].code_value != - 1 )
|
|
string_code = dict[ index ].code_value;
|
|
else {
|
|
dict[ index ].code_value = next_code++;
|
|
dict[ index ].parent_code = string_code;
|
|
dict[ index ].character = (char) character;
|
|
output_bits( &output, (unsigned long) string_code, current_code_bits );
|
|
string_code = character;
|
|
if ( next_code > MAX_CODE ) {
|
|
output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
|
|
InitializeDictionary();
|
|
} else if ( next_code > next_bump_code ) {
|
|
output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
|
|
current_code_bits++;
|
|
next_bump_code <<= 1;
|
|
next_bump_code |= 1;
|
|
}
|
|
}
|
|
}
|
|
output_bits( &output, (unsigned long) string_code, current_code_bits );
|
|
output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
|
|
|
|
if ( output.mask != 0x80 )
|
|
*output.data++ = (ubyte)output.rack;
|
|
|
|
return output.data-outputbuf;
|
|
}
|
|
|
|
|
|
int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
|
|
{
|
|
BitBuf input;
|
|
uint new_code;
|
|
uint old_code;
|
|
int character;
|
|
uint count;
|
|
uint counter;
|
|
|
|
input.rack = 0;
|
|
input.mask = 0x80;
|
|
input.data = inputbuf;
|
|
|
|
counter = 0;
|
|
for ( ; ; ) {
|
|
InitializeDictionary();
|
|
old_code = (uint) input_bits( &input, current_code_bits );
|
|
if ( old_code == END_OF_STREAM )
|
|
return counter;
|
|
character = old_code;
|
|
outputbuf[counter++] = ( ubyte )old_code;
|
|
for ( ; ; ) {
|
|
new_code = (uint) input_bits( &input, current_code_bits );
|
|
if ( new_code == END_OF_STREAM )
|
|
return counter;
|
|
if ( new_code == FLUSH_CODE )
|
|
break;
|
|
if ( new_code == BUMP_CODE ) {
|
|
current_code_bits++;
|
|
continue;
|
|
}
|
|
if ( new_code >= next_code ) {
|
|
decode_stack[ 0 ] = (char) character;
|
|
count = decode_string( 1, old_code );
|
|
} else {
|
|
count = decode_string( 0, new_code );
|
|
}
|
|
character = decode_stack[ count - 1 ];
|
|
while ( count > 0 )
|
|
outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
|
|
dict[ next_code ].parent_code = old_code;
|
|
dict[ next_code ].character = (char) character;
|
|
next_code++;
|
|
old_code = new_code;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// process a join request packet add
|
|
void add_join_request(ubyte *data, int *size, join_request *jr)
|
|
{
|
|
int packet_size = *size;
|
|
|
|
ADD_STRING(jr->passwd);
|
|
ADD_STRING(jr->callsign);
|
|
ADD_STRING(jr->image_filename);
|
|
ADD_STRING(jr->squad_filename);
|
|
ADD_DATA(jr->player_rank);
|
|
ADD_DATA(jr->flags);
|
|
ADD_INT(jr->tracker_id);
|
|
ADD_INT(jr->player_options.flags);
|
|
ADD_INT(jr->player_options.obj_update_level);
|
|
ADD_DATA(jr->version);
|
|
ADD_DATA(jr->comp_version);
|
|
ADD_STRING(jr->pxo_squad_name);
|
|
|
|
*size = packet_size;
|
|
}
|
|
|
|
// process a join request packet get
|
|
void get_join_request(ubyte *data, int *size, join_request *jr)
|
|
{
|
|
int offset = *size;
|
|
|
|
GET_STRING(jr->passwd);
|
|
GET_STRING(jr->callsign);
|
|
GET_STRING(jr->image_filename);
|
|
GET_STRING(jr->squad_filename);
|
|
GET_DATA(jr->player_rank);
|
|
GET_DATA(jr->flags);
|
|
GET_INT(jr->tracker_id);
|
|
GET_INT(jr->player_options.flags);
|
|
GET_INT(jr->player_options.obj_update_level);
|
|
GET_DATA(jr->version);
|
|
GET_DATA(jr->comp_version);
|
|
GET_STRING(jr->pxo_squad_name);
|
|
|
|
*size = offset;
|
|
}
|
|
|
|
void add_net_addr(ubyte *data, int *size, net_addr *addr)
|
|
{
|
|
int packet_size = *size;
|
|
|
|
ADD_DATA(addr->addr);
|
|
ADD_USHORT(addr->port);
|
|
|
|
*size = packet_size;
|
|
}
|
|
|
|
void get_net_addr(ubyte *data, int *size, net_addr *addr)
|
|
{
|
|
int offset = *size;
|
|
|
|
GET_DATA(addr->addr);
|
|
GET_USHORT(addr->port);
|
|
|
|
*size = offset;
|
|
}
|
|
/*
|
|
void add_vector_data(ubyte *data, int *size, vec3d vec)
|
|
{
|
|
int packet_size = *size;
|
|
|
|
ADD_FLOAT(vec.xyz.x);
|
|
ADD_FLOAT(vec.xyz.y);
|
|
ADD_FLOAT(vec.xyz.z);
|
|
|
|
*size = packet_size;
|
|
}
|
|
|
|
void get_vector_data(ubyte *data, int *size, vec3d vec)
|
|
{
|
|
int offset = *size;
|
|
|
|
GET_FLOAT(vec.xyz.x);
|
|
GET_FLOAT(vec.xyz.y);
|
|
GET_FLOAT(vec.xyz.z);
|
|
|
|
*size = offset;
|
|
}
|
|
*/
|
|
// send the specified data packet to all players
|
|
void multi_io_send(net_player *pl, const ubyte *data, int len)
|
|
{
|
|
// invalid
|
|
if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
|
|
return;
|
|
}
|
|
|
|
// don't do it for single player
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return;
|
|
}
|
|
|
|
// sanity checks
|
|
if(MULTIPLAYER_CLIENT){
|
|
// Assert(pl == Net_player);
|
|
if(pl != Net_player){
|
|
return;
|
|
}
|
|
} else {
|
|
// Assert(pl != Net_player);
|
|
if(pl == Net_player){
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
|
|
if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
|
|
multi_io_send_force(pl);
|
|
pl->s_info.unreliable_buffer_size = 0;
|
|
}
|
|
|
|
Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
|
|
|
|
memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
|
|
pl->s_info.unreliable_buffer_size += len;
|
|
}
|
|
|
|
void multi_io_send_to_all(const ubyte *data, int length, const net_player *ignore)
|
|
{
|
|
int i;
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
// need to check for i > 1, hmmm... and connected. I don't know.
|
|
for (i = 0; i < MAX_PLAYERS; i++ ) {
|
|
if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
|
|
continue;
|
|
}
|
|
|
|
// maybe ignore a player
|
|
if((ignore != NULL) && (&Net_players[i] == ignore)){
|
|
continue;
|
|
}
|
|
|
|
// ingame joiners not waiting to select a ship doesn't get any packets
|
|
if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
|
|
continue;
|
|
}
|
|
|
|
// send it
|
|
multi_io_send(&Net_players[i], data, length);
|
|
}
|
|
}
|
|
|
|
void multi_io_send_force(net_player *pl)
|
|
{
|
|
// invalid
|
|
if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
|
|
return;
|
|
}
|
|
|
|
// don't do it for single player
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return;
|
|
}
|
|
|
|
// send everything in
|
|
if (MULTIPLAYER_MASTER) {
|
|
psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
|
|
|
|
// add the bytes sent to this player
|
|
pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
|
|
} else {
|
|
psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
|
|
}
|
|
pl->s_info.unreliable_buffer_size = 0;
|
|
}
|
|
|
|
// send the data packet to all players via their reliable sockets
|
|
void multi_io_send_reliable(net_player *pl, const ubyte *data, int len)
|
|
{
|
|
// invalid
|
|
if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
|
|
return;
|
|
}
|
|
|
|
// don't do it for single player
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return;
|
|
}
|
|
|
|
// sanity checks
|
|
if(MULTIPLAYER_CLIENT){
|
|
// Assert(pl == Net_player);
|
|
if(pl != Net_player){
|
|
return;
|
|
}
|
|
} else {
|
|
// Assert(pl != Net_player);
|
|
if(pl == Net_player){
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
|
|
if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
|
|
multi_io_send_reliable_force(pl);
|
|
pl->s_info.reliable_buffer_size = 0;
|
|
}
|
|
|
|
Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
|
|
|
|
memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
|
|
pl->s_info.reliable_buffer_size += len;
|
|
}
|
|
|
|
void multi_io_send_to_all_reliable(const ubyte* data, int length, const net_player *ignore)
|
|
{
|
|
int i;
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
// need to check for i > 1, hmmm... and connected. I don't know.
|
|
for (i = 0; i < MAX_PLAYERS; i++ ) {
|
|
if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
|
|
continue;
|
|
}
|
|
|
|
// maybe ignore a player
|
|
if((ignore != NULL) && (&Net_players[i] == ignore)){
|
|
continue;
|
|
}
|
|
|
|
// ingame joiners not waiting to select a ship doesn't get any packets
|
|
if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
|
|
continue;
|
|
}
|
|
|
|
// send it
|
|
multi_io_send_reliable(&Net_players[i], data, length);
|
|
}
|
|
}
|
|
|
|
void multi_io_send_reliable_force(net_player *pl)
|
|
{
|
|
// invalid
|
|
if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
|
|
return;
|
|
}
|
|
|
|
// don't do it for single player
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return;
|
|
}
|
|
|
|
// send everything in
|
|
if(MULTIPLAYER_MASTER) {
|
|
psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
|
|
} else if(Net_player != NULL){
|
|
psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
|
|
}
|
|
pl->s_info.reliable_buffer_size = 0;
|
|
}
|
|
|
|
// send all buffered packets
|
|
void multi_io_send_buffered_packets()
|
|
{
|
|
int idx;
|
|
|
|
// don't do it for single player
|
|
if(!(Game_mode & GM_MULTIPLAYER)){
|
|
return;
|
|
}
|
|
|
|
// server
|
|
if(MULTIPLAYER_MASTER){
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
|
|
// force unreliable data
|
|
if(Net_players[idx].s_info.unreliable_buffer_size > 0){
|
|
multi_io_send_force(&Net_players[idx]);
|
|
Net_players[idx].s_info.unreliable_buffer_size = 0;
|
|
}
|
|
|
|
// force reliable data
|
|
if(Net_players[idx].s_info.reliable_buffer_size > 0){
|
|
multi_io_send_reliable_force(&Net_players[idx]);
|
|
Net_players[idx].s_info.reliable_buffer_size = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// clients
|
|
else if(Net_player != NULL){
|
|
// force unreliable data
|
|
if(Net_player->s_info.unreliable_buffer_size > 0){
|
|
multi_io_send_force(Net_player);
|
|
Net_player->s_info.unreliable_buffer_size = 0;
|
|
}
|
|
|
|
// force reliable data
|
|
if(Net_player->s_info.reliable_buffer_size > 0){
|
|
multi_io_send_reliable_force(Net_player);
|
|
Net_player->s_info.reliable_buffer_size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Game Chat Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_game_chat_packet
|
|
{
|
|
char packet_signature; //0xC3
|
|
short from_player_id;
|
|
int server_msg;
|
|
char mode;
|
|
|
|
// variable record
|
|
if (mode)
|
|
short to_player_id;
|
|
else
|
|
{
|
|
int i;
|
|
char expr[i];
|
|
}
|
|
int j;
|
|
char message[j];
|
|
|
|
}
|
|
*/
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// send a general game chat packet (if msg_mode == MULTI_MSG_TARGET, need to pass in "to", if == MULTI_MSG_EXPR, need to pass in expr)
|
|
void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, ubyte server_msg)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],mode;
|
|
int packet_size,idx;
|
|
bool undeliverable = true;
|
|
|
|
BUILD_HEADER(GAME_CHAT);
|
|
|
|
// add the id
|
|
ADD_SHORT(from->player_id);
|
|
|
|
// add the message mode and if in MSG_TARGET mode, add who the target is
|
|
ADD_DATA(server_msg);
|
|
mode = (ubyte)msg_mode;
|
|
ADD_DATA(mode);
|
|
switch(mode){
|
|
case MULTI_MSG_TARGET:
|
|
Assert(to != NULL);
|
|
ADD_SHORT(to->player_id);
|
|
break;
|
|
case MULTI_MSG_EXPR:
|
|
Assert(expr != NULL);
|
|
ADD_STRING(expr);
|
|
break;
|
|
}
|
|
// add the message itself
|
|
ADD_STRING( msg );
|
|
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
switch(mode){
|
|
// message all players
|
|
case MULTI_MSG_ALL:
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
|
|
multi_io_send_reliable(&Net_players[idx], data, packet_size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// message only friendly players
|
|
case MULTI_MSG_FRIENDLY:
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && (Net_players[idx].p_info.team == from->p_info.team)){
|
|
multi_io_send_reliable(&Net_players[idx], data, packet_size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// message only hostile players
|
|
case MULTI_MSG_HOSTILE:
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && (Net_players[idx].p_info.team != from->p_info.team)){
|
|
multi_io_send_reliable(&Net_players[idx], data, packet_size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// message the player's target
|
|
case MULTI_MSG_TARGET:
|
|
Assert(to != NULL);
|
|
if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
|
|
multi_io_send_reliable(to, data, packet_size);
|
|
}
|
|
break;
|
|
|
|
// message all players who match the expression string
|
|
case MULTI_MSG_EXPR:
|
|
Assert(expr != NULL);
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
|
|
multi_io_send_reliable(&Net_players[idx], data, packet_size);
|
|
undeliverable = false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// if the message can't be delivered, notify the player
|
|
if (undeliverable) {
|
|
switch(mode){
|
|
case MULTI_MSG_EXPR:
|
|
// if the message came from the server
|
|
if (from == Net_player) {
|
|
multi_display_chat_msg ("Unable to send message, player does not exist", 0, 0);
|
|
}
|
|
// otherwise send a message back to the player
|
|
else {
|
|
send_game_chat_packet(Net_player, "Unable to send message, player does not exist", MULTI_MSG_TARGET, from, NULL, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// send to the server, who will take care of routing it
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// process a general game chat packet, if we're the standalone we should rebroadcast
|
|
void process_game_chat_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ubyte mode, server_msg;
|
|
int color_index, player_index, to_player_index, should_display;
|
|
char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
|
|
char expr[255];
|
|
short from, to;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the id of the sender
|
|
GET_SHORT(from);
|
|
|
|
// determine if this is a server message
|
|
GET_DATA(server_msg);
|
|
|
|
// get the mode
|
|
GET_DATA(mode);
|
|
|
|
// if targeting a specific player, get the address
|
|
to = -1;
|
|
switch(mode){
|
|
case MULTI_MSG_TARGET:
|
|
GET_SHORT(to);
|
|
break;
|
|
case MULTI_MSG_EXPR:
|
|
GET_STRING(expr);
|
|
break;
|
|
}
|
|
// get the message itself
|
|
GET_STRING(msg);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get the index of the sending player
|
|
color_index = find_player_index(from);
|
|
player_index = color_index;
|
|
|
|
// if we couldn't find the player - bail
|
|
if(player_index == -1){
|
|
nprintf(("Network","Could not find player for processing game chat packet!\n"));
|
|
return;
|
|
}
|
|
|
|
should_display = 0;
|
|
|
|
// if we're the server, determine what to do with the packet here
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// if he's targeting a specific player, find out who it is
|
|
if(mode == MULTI_MSG_TARGET){
|
|
to_player_index = find_player_index(to);
|
|
} else {
|
|
to_player_index = -1;
|
|
}
|
|
|
|
// if we couldn't find who sent the message or who should be getting the message, the bail
|
|
if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
|
|
return;
|
|
}
|
|
|
|
// determine if _I_ should be seeing the text
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
should_display = 1;
|
|
}
|
|
// check against myself for several specific cases
|
|
else {
|
|
if((mode == MULTI_MSG_ALL) ||
|
|
((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
|
|
((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
|
|
((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
|
|
((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
|
|
should_display = 1;
|
|
}
|
|
}
|
|
|
|
// if we're the server of a game, we need to rebroadcast to all other players
|
|
switch(mode){
|
|
// individual target mission
|
|
case MULTI_MSG_TARGET:
|
|
// if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
|
|
if(to_player_index != MY_NET_PLAYER_NUM){
|
|
send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
|
|
}
|
|
break;
|
|
// expression mode
|
|
case MULTI_MSG_EXPR:
|
|
send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
|
|
break;
|
|
// all other modes
|
|
default :
|
|
send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
|
|
break;
|
|
}
|
|
}
|
|
// if a client receives this packet, its always ok for him to display it
|
|
else {
|
|
should_display = 1;
|
|
}
|
|
|
|
// if we're not on a standalone
|
|
if(should_display){
|
|
if(server_msg == 2){
|
|
HUD_printf("%s", msg);
|
|
} else {
|
|
multi_display_chat_msg(msg, player_index, !server_msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//*********************************************************************************************************
|
|
// Hud Message packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_game_chat_packet
|
|
{
|
|
char packet_signature; //0xC1 HUD_MSG
|
|
int msg_size;
|
|
char msg[msg_size];
|
|
}
|
|
*/
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// broadcast a hud message to all players
|
|
void send_hud_msg_to_all( char* msg )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
// only the server should be sending this packet
|
|
BUILD_HEADER(HUD_MSG);
|
|
|
|
ADD_STRING(msg);
|
|
|
|
multi_io_send_to_all( data, packet_size );
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// process an incoming hud message packet
|
|
void process_hud_message(ubyte* data, header* hinfo)
|
|
{
|
|
int offset;
|
|
char msg_buffer[255];
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_STRING(msg_buffer);
|
|
PACKET_SET_SIZE();
|
|
|
|
// this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
|
|
if(Game_mode & GM_IN_MISSION){
|
|
HUD_printf("%s", msg_buffer);
|
|
}
|
|
}
|
|
|
|
|
|
//*********************************************************************************************************
|
|
// Join Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_game_chat_packet
|
|
{
|
|
char packet_signature; //0xC1 HUD_MSG
|
|
int msg_size;
|
|
char msg[msg_size];
|
|
}
|
|
*/
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// send a join packet request to the specified address (should be a server)
|
|
void send_join_packet(net_addr* addr,join_request *jr)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
// build the header and add the request
|
|
BUILD_HEADER(JOIN);
|
|
add_join_request(data, &packet_size, jr);
|
|
|
|
psnet_send(addr, data, packet_size);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// process an incoming join request packet
|
|
void process_join_packet(ubyte* data, header* hinfo)
|
|
{
|
|
join_request jr;
|
|
int offset;
|
|
int ret_code;
|
|
int host_restr_mode;
|
|
int team0_avail,team1_avail;
|
|
char join_string[255];
|
|
net_addr addr;
|
|
|
|
// only the server of the game should ever receive this packet
|
|
if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
|
|
return;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// read in the request info
|
|
memset(&jr,0,sizeof(join_request));
|
|
|
|
get_join_request(data, &offset, &jr);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// fill in the address information of where this came from
|
|
fill_net_addr(&addr, hinfo->addr, hinfo->port);
|
|
|
|
// determine if we should accept this guy, or return a reason we should reject him
|
|
// see the DENY_* codes in multi.h
|
|
ret_code = multi_eval_join_request(&jr,&addr);
|
|
|
|
// evaluate the return code
|
|
switch(ret_code)
|
|
{
|
|
// he should be accepted
|
|
case -1 :
|
|
break;
|
|
|
|
// we have to query the host because this is a restricted game
|
|
case JOIN_QUERY_RESTRICTED :
|
|
if(!(Game_mode & GM_STANDALONE_SERVER)){
|
|
// notify the host of the event
|
|
snd_play(gamesnd_get_game_sound(GameSounds::CUE_VOICE));
|
|
}
|
|
|
|
// set the query timestamp
|
|
Multi_restr_query_timestamp = ui_timestamp(MULTI_QUERY_RESTR_STAMP);
|
|
Netgame.flags |= NG_FLAG_INGAME_JOINING;
|
|
|
|
// determine what mode we're in
|
|
host_restr_mode = -1;
|
|
memset(join_string,0,255);
|
|
if((Netgame.type_flags & NG_TYPE_TEAM) && Netgame.mode == NG_MODE_RESTRICTED){
|
|
multi_player_ships_available(&team0_avail,&team1_avail);
|
|
|
|
if(team0_avail && team1_avail){
|
|
host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
|
|
sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
|
|
} else if(team0_avail && !team1_avail){
|
|
host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
|
|
sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
|
|
} else if(!team0_avail && team1_avail){
|
|
host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
|
|
sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
|
|
}
|
|
} else if(Netgame.mode == NG_MODE_RESTRICTED){
|
|
host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
|
|
sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
|
|
}
|
|
Assert(host_restr_mode != -1);
|
|
|
|
// store the request info
|
|
memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
|
|
memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
|
|
Multi_join_restr_mode = host_restr_mode;
|
|
|
|
// if i'm the standalone server, I need to send a query to the host
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
|
|
} else {
|
|
HUD_printf("%s", join_string);
|
|
}
|
|
|
|
// NETLOG
|
|
ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
|
|
|
|
return;
|
|
|
|
// he'e being denied for some reason
|
|
default :
|
|
// send him the reason he is being denied
|
|
send_deny_packet(&addr,ret_code);
|
|
Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
|
|
return;
|
|
}
|
|
|
|
// process the rest of the request
|
|
multi_process_valid_join_request(&jr,&addr);
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// New Player Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_new_player_packet
|
|
{
|
|
char packet_signature; // 0xB4 NOTIFY_NEW_PLAYER
|
|
int new_player_num;
|
|
net_addr player_addr;
|
|
short player_id;
|
|
int flags;
|
|
|
|
int i;
|
|
char callsign[i];
|
|
|
|
int j;
|
|
char plyr_image_filename[j];
|
|
|
|
int k;
|
|
char plyr_squad_filename[k];
|
|
|
|
int l;
|
|
char plyr_pxo_squad_name[l];
|
|
}
|
|
*/
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
|
|
void send_new_player_packet(int new_player_num,net_player *target)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], val;
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER( NOTIFY_NEW_PLAYER );
|
|
|
|
// add the new player's info
|
|
val = static_cast<ubyte>(new_player_num);
|
|
ADD_DATA(val);
|
|
// ADD_DATA(Net_players[new_player_num].p_info.addr);
|
|
add_net_addr(data, &packet_size, &Net_players[new_player_num].p_info.addr);
|
|
ADD_SHORT(Net_players[new_player_num].player_id);
|
|
ADD_INT(Net_players[new_player_num].flags);
|
|
ADD_STRING(Net_players[new_player_num].m_player->callsign);
|
|
ADD_STRING(Net_players[new_player_num].m_player->image_filename);
|
|
ADD_STRING(Net_players[new_player_num].m_player->m_squad_filename);
|
|
ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
|
|
|
|
val = (ubyte)Net_players[new_player_num].p_info.team;
|
|
ADD_DATA(val);
|
|
|
|
// broadcast the data
|
|
if(target != NULL){
|
|
multi_io_send_reliable(target, data, packet_size);
|
|
} else {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// process a notification for a new player who has joined the game
|
|
void process_new_player_packet(ubyte* data, header* hinfo)
|
|
{
|
|
int already_in_game = 0;
|
|
int offset, player_num, new_flags;
|
|
net_addr new_addr;
|
|
char new_player_name[CALLSIGN_LEN+2] = "";
|
|
char new_player_image[MAX_FILENAME_LEN+1] = "";
|
|
char new_player_squad[MAX_FILENAME_LEN+1] = "";
|
|
char new_player_pxo_squad[LOGIN_LEN+1] = "";
|
|
char notify_string[256];
|
|
ubyte team, new_player_num;
|
|
short new_id;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the new players information
|
|
GET_DATA(new_player_num);
|
|
// GET_DATA(new_addr);
|
|
get_net_addr(data, &offset, &new_addr);
|
|
GET_SHORT(new_id);
|
|
GET_INT(new_flags);
|
|
GET_STRING(new_player_name);
|
|
GET_STRING(new_player_image);
|
|
GET_STRING(new_player_squad);
|
|
GET_STRING(new_player_pxo_squad);
|
|
GET_DATA(team);
|
|
PACKET_SET_SIZE();
|
|
|
|
player_num = multi_find_open_player_slot();
|
|
Assert(player_num != -1);
|
|
|
|
// note that this new code does not check for duplicate IPs. It merely checks to see if
|
|
// the slot referenced by new_player_num is already occupied by a connected player
|
|
if(MULTI_CONNECTED(Net_players[new_player_num])){
|
|
already_in_game=1;
|
|
}
|
|
|
|
// if he's not alreayd in the game for one reason or another
|
|
if ( !already_in_game ) {
|
|
if ( Game_mode & GM_IN_MISSION ){
|
|
HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
|
|
}
|
|
|
|
// create the player
|
|
if(new_flags & NETINFO_FLAG_OBSERVER){
|
|
multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
|
|
Net_players[new_player_num].flags |= new_flags;
|
|
} else {
|
|
multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
|
|
Net_players[new_player_num].flags |= new_flags;
|
|
}
|
|
|
|
// copy in the filename
|
|
if(new_player_image[0] != '\0'){
|
|
strcpy_s(Net_players[new_player_num].m_player->image_filename, new_player_image);
|
|
} else {
|
|
strcpy_s(Net_players[new_player_num].m_player->image_filename, "");
|
|
}
|
|
// copy his pilot squad filename
|
|
Net_players[new_player_num].m_player->insignia_texture = -1;
|
|
player_set_squad_bitmap(Net_players[new_player_num].m_player, new_player_squad, true);
|
|
|
|
// copy in his pxo squad name
|
|
strcpy_s(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
|
|
|
|
// since we just created the player, set the last_heard_time here.
|
|
Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
|
|
|
|
Net_players[new_player_num].p_info.team = team;
|
|
|
|
Net_players[new_player_num].player_id = new_id;
|
|
|
|
// zero out this players ping
|
|
multi_ping_reset(&Net_players[new_player_num].s_info.ping);
|
|
|
|
// add a chat message
|
|
if(*Net_players[new_player_num].m_player->callsign){
|
|
sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].m_player->callsign);
|
|
multi_display_chat_msg(notify_string,0,0);
|
|
}
|
|
}
|
|
|
|
// NETLOG
|
|
ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].m_player->callsign);
|
|
|
|
// let the current ui screen know someone joined
|
|
switch(gameseq_get_state()){
|
|
case GS_STATE_MULTI_HOST_SETUP :
|
|
multi_create_handle_join(&Net_players[new_player_num]);
|
|
break;
|
|
case GS_STATE_MULTI_CLIENT_SETUP :
|
|
multi_jw_handle_join(&Net_players[new_player_num]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Accept Player Data Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_accept_player_data
|
|
{
|
|
char packet_signature; //ACCEPT_PLAYER_DATA
|
|
ubyte stop;
|
|
int player_num;
|
|
net_addr plyr_addr;
|
|
int player_id;
|
|
|
|
int i;
|
|
char callsign[i];
|
|
|
|
int j;
|
|
char plr_image_filename[j];
|
|
|
|
int k;
|
|
char plr_squad_filename[k]
|
|
|
|
int l
|
|
char plr_pxo_squadname[l];
|
|
|
|
int flags;
|
|
|
|
if (is_ingame)
|
|
int net_signature;
|
|
|
|
|
|
};
|
|
*/
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
#define PLAYER_DATA_SLOP 100
|
|
|
|
void send_accept_player_data( net_player *npp, int is_ingame )
|
|
{
|
|
int packet_size;
|
|
uint8_t i;
|
|
ubyte data[MAX_PACKET_SIZE], stop;
|
|
|
|
BUILD_HEADER(ACCEPT_PLAYER_DATA);
|
|
|
|
// add in the netplayer data for all players
|
|
stop = APD_NEXT;
|
|
for (i=0; i<MAX_PLAYERS; i++) {
|
|
// skip non connected players
|
|
if ( !MULTI_CONNECTED(Net_players[i]) ){
|
|
continue;
|
|
}
|
|
|
|
// skip this new player's entry
|
|
if ( npp->player_id == Net_players[i].player_id ){
|
|
continue;
|
|
}
|
|
|
|
// add the stop byte
|
|
ADD_DATA(stop);
|
|
|
|
// add the player's number
|
|
ADD_DATA(i);
|
|
|
|
// add the player's address
|
|
// ADD_DATA(Net_players[i].p_info.addr);
|
|
add_net_addr(data, &packet_size, &Net_players[i].p_info.addr);
|
|
|
|
// add his id#
|
|
ADD_SHORT(Net_players[i].player_id);
|
|
|
|
// add his callsign
|
|
ADD_STRING(Net_players[i].m_player->callsign);
|
|
|
|
// add his image filename
|
|
ADD_STRING(Net_players[i].m_player->image_filename);
|
|
|
|
// add his squad filename
|
|
ADD_STRING(Net_players[i].m_player->m_squad_filename);
|
|
|
|
// add his PXO squad name
|
|
ADD_STRING(Net_players[i].p_info.pxo_squad_name);
|
|
|
|
// add his flags
|
|
ADD_INT(Net_players[i].flags);
|
|
|
|
// add his object's net sig
|
|
if ( is_ingame ) {
|
|
ADD_USHORT( Objects[Net_players[i].m_player->objnum].net_signature );
|
|
}
|
|
|
|
if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
|
|
stop = APD_END_PACKET;
|
|
ADD_DATA(stop);
|
|
multi_io_send_reliable( npp, data, packet_size );
|
|
BUILD_HEADER(ACCEPT_PLAYER_DATA);
|
|
stop = APD_NEXT;
|
|
}
|
|
|
|
}
|
|
|
|
// add the stop byte
|
|
stop = APD_END_DATA;
|
|
ADD_DATA(stop);
|
|
multi_io_send_reliable(npp, data, packet_size);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// process the player data from the server
|
|
void process_accept_player_data( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, player_slot_num, new_flags;
|
|
char name[CALLSIGN_LEN + 1] = "";
|
|
char image_name[MAX_FILENAME_LEN + 1] = "";
|
|
char squad_name[MAX_FILENAME_LEN + 1] = "";
|
|
char pxo_squad_name[LOGIN_LEN+1] = "";
|
|
short player_id;
|
|
net_addr addr;
|
|
ubyte stop;
|
|
ushort ig_signature;
|
|
uint8_t player_num;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_DATA(stop);
|
|
while ( stop == APD_NEXT ) {
|
|
player_slot_num = multi_find_open_player_slot();
|
|
Assert(player_slot_num != -1);
|
|
|
|
// get the player's number
|
|
GET_DATA(player_num);
|
|
|
|
// add the player's address
|
|
// GET_DATA(addr);
|
|
get_net_addr(data, &offset, &addr);
|
|
|
|
// get the player's id#
|
|
GET_SHORT(player_id);
|
|
|
|
// get his callsign
|
|
GET_STRING(name);
|
|
|
|
// add his image filename
|
|
GET_STRING(image_name);
|
|
|
|
// get his squad logo filename
|
|
GET_STRING(squad_name);
|
|
|
|
// get his PXO squad name
|
|
GET_STRING(pxo_squad_name);
|
|
|
|
// get his flags
|
|
GET_INT(new_flags);
|
|
|
|
if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
|
|
multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num]);
|
|
|
|
} else {
|
|
multi_create_player(player_num, &Players[player_slot_num], name, &addr, -1, player_id);
|
|
}
|
|
|
|
// copy his image filename
|
|
strcpy_s(Net_players[player_num].m_player->image_filename, image_name);
|
|
|
|
// copy his pilot squad filename
|
|
Net_players[player_num].m_player->insignia_texture = -1;
|
|
player_set_squad_bitmap(Net_players[player_num].m_player, squad_name, true);
|
|
|
|
// copy his pxo squad name
|
|
strcpy_s(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
|
|
|
|
// set his player id#
|
|
Net_players[player_num].player_id = player_id;
|
|
|
|
// mark him as being connected
|
|
Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
|
|
Net_players[player_num].flags |= new_flags;
|
|
|
|
// set the server pointer
|
|
if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
|
|
Netgame.server = &Net_players[player_num];
|
|
Netgame.server->last_heard_time = timer_get_fixed_seconds();
|
|
|
|
// also - always set the server address to be where this data came from, NOT from
|
|
// the data in the packet
|
|
fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->port);
|
|
}
|
|
|
|
// set the host pointer
|
|
if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
|
|
Netgame.host = &Net_players[player_num];
|
|
}
|
|
|
|
// read in the player's object net signature and store as his objnum for now
|
|
if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
|
|
GET_USHORT( ig_signature );
|
|
Net_players[player_num].m_player->objnum = ig_signature;
|
|
}
|
|
|
|
// get the stop byte
|
|
GET_DATA(stop);
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
if ( stop == APD_END_DATA ) {
|
|
// if joining a game automatically, set the connect address to NULl so we don't try and
|
|
// do this next time we enter a game
|
|
if (Cmdline_connect_addr != NULL) {
|
|
Cmdline_connect_addr = NULL;
|
|
}
|
|
|
|
// send my stats to the server if I'm not in observer mode
|
|
if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
|
|
send_player_stats_block_packet(Net_player, STATS_ALLTIME);
|
|
}
|
|
|
|
// if i'm being accepted as a host, then move into the host setup state
|
|
if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
|
|
// set my permission bits
|
|
Net_player->flags |= NETINFO_FLAG_GAME_HOST;
|
|
Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
|
|
|
|
gameseq_post_event(GS_EVENT_MULTI_START_GAME);
|
|
}
|
|
|
|
if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
|
|
Net_player->flags |= NETINFO_FLAG_OBSERVER;
|
|
|
|
// since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
|
|
if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
|
|
gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
|
|
}
|
|
}
|
|
|
|
if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
|
|
gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
|
|
}
|
|
|
|
if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
|
|
// flag myself as being an ingame joiner
|
|
Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
|
|
|
|
// move myself into the ingame join mission sync state
|
|
Multi_sync_mode = MULTI_SYNC_INGAME;
|
|
gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
|
|
}
|
|
|
|
// update my options on the server
|
|
multi_options_update_local();
|
|
|
|
// if we're in PXO mode, mark it down in our player struct
|
|
if(MULTI_IS_TRACKER_GAME){
|
|
Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
|
|
Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//*********************************************************************************************************
|
|
// Accept Player Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_accept_packet
|
|
{
|
|
char packet_signature;
|
|
int code;
|
|
|
|
if (code & ACCEPT_INGAME)
|
|
{
|
|
int gm_mis_fname_len;
|
|
char mission_filename[gm_mis_fname_len];
|
|
unsigned char ingame_joining_team;
|
|
if (ingame_joining_team == 1)
|
|
unsigned char ingame_join_team;
|
|
|
|
}
|
|
|
|
int skill_level;
|
|
int player_num;
|
|
short player_id;
|
|
int netgame_type_flags;
|
|
|
|
}
|
|
*/
|
|
// send an accept packet to a client in response to a request to join the game
|
|
void send_accept_packet(int new_player_num, int code, int ingame_join_team)
|
|
{
|
|
int packet_size, i;
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
char notify_string[256];
|
|
|
|
// sanity
|
|
Assert(new_player_num >= 0);
|
|
|
|
// setup his "reliable" socket
|
|
Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
|
|
|
|
// build the packet header
|
|
packet_size = 0;
|
|
BUILD_HEADER(ACCEPT);
|
|
|
|
// add the accept code
|
|
ADD_INT(code);
|
|
|
|
// add code specific accept data
|
|
if (code & ACCEPT_INGAME) {
|
|
// the game filename
|
|
ADD_STRING(Game_current_mission_filename);
|
|
|
|
// if he is joining on a specific team, mark it here
|
|
if(ingame_join_team != -1){
|
|
val = 1;
|
|
ADD_DATA(val);
|
|
val = (ubyte)ingame_join_team;
|
|
ADD_DATA(val);
|
|
} else {
|
|
val = 0;
|
|
ADD_DATA(val);
|
|
}
|
|
}
|
|
|
|
if (code & ACCEPT_OBSERVER) {
|
|
Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
|
|
}
|
|
|
|
if (code & ACCEPT_HOST) {
|
|
Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
|
|
}
|
|
|
|
if (code & ACCEPT_CLIENT) {
|
|
Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
|
|
}
|
|
|
|
// add the current skill level setting on the host
|
|
// sanity check - reset skill level to default before sending if out of range
|
|
if (Game_skill_level < 0 || Game_skill_level >= NUM_SKILL_LEVELS) {
|
|
Warning(LOCATION, "Trying to send packet containing invalid skill level %i! Valid range 0 to %i. Resetting to default.", Game_skill_level, NUM_SKILL_LEVELS);
|
|
Game_skill_level = game_get_default_skill_level();
|
|
}
|
|
ADD_INT(Game_skill_level);
|
|
|
|
// add this guys player num
|
|
val = static_cast<ubyte>(new_player_num);
|
|
ADD_DATA(val);
|
|
|
|
// add his player id
|
|
ADD_SHORT(Net_players[new_player_num].player_id);
|
|
|
|
// add netgame type flags
|
|
ADD_INT(Netgame.type_flags);
|
|
|
|
// actually send the packet
|
|
psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
|
|
|
|
// if he's not an observer, inform all the other players in the game about him
|
|
// inform the other players in the game about this new player
|
|
for (i=0; i<MAX_PLAYERS; i++) {
|
|
// skip unconnected players as well as this new guy himself
|
|
if ( !MULTI_CONNECTED(Net_players[i]) || psnet_same(&Net_players[new_player_num].p_info.addr, &(Net_players[i].p_info.addr)) || (Net_player == &Net_players[i])) {
|
|
continue;
|
|
}
|
|
|
|
// send the new packet
|
|
send_new_player_packet(new_player_num,&Net_players[i]);
|
|
}
|
|
|
|
// add a chat message
|
|
if(*Net_players[new_player_num].m_player->callsign){
|
|
sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].m_player->callsign);
|
|
multi_display_chat_msg(notify_string, 0, 0);
|
|
}
|
|
|
|
// handle any team vs. team details
|
|
if (!(code & ACCEPT_OBSERVER)) {
|
|
multi_team_handle_join(&Net_players[new_player_num]);
|
|
}
|
|
|
|
// NETLOG
|
|
if(Net_players[new_player_num].tracker_player_id >= 0){
|
|
ml_printf(NOX("Server accepted %s (tracker id %d) as new client"), Net_players[new_player_num].m_player->callsign, Net_players[new_player_num].tracker_player_id);
|
|
} else {
|
|
ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].m_player->callsign);
|
|
}
|
|
}
|
|
|
|
// process an accept packet from the server
|
|
extern int Select_default_ship;
|
|
|
|
void process_accept_packet(ubyte* data, header* hinfo)
|
|
{
|
|
int code, offset;
|
|
ubyte val, team = 0, my_player_num;
|
|
short player_id;
|
|
|
|
// get the accept code
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_INT(code);
|
|
|
|
// read in the accept code specific data
|
|
val = 0;
|
|
if (code & ACCEPT_INGAME) {
|
|
// the game filename
|
|
GET_STRING(Game_current_mission_filename);
|
|
mprintf(("Got mission filename %s\n", Game_current_mission_filename));
|
|
Select_default_ship = 0;
|
|
|
|
// determine if I'm being placed on a team
|
|
GET_DATA(val);
|
|
if(val){
|
|
GET_DATA(team);
|
|
}
|
|
}
|
|
|
|
if (code & ACCEPT_OBSERVER) {
|
|
Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
|
|
}
|
|
|
|
if (code & ACCEPT_HOST) {
|
|
Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
|
|
}
|
|
|
|
if (code & ACCEPT_CLIENT) {
|
|
Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
|
|
}
|
|
|
|
// fill in the netgame server address
|
|
fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->port );
|
|
|
|
// get the skill level setting
|
|
GET_INT(Game_skill_level);
|
|
if (Game_skill_level < 0 || Game_skill_level >= NUM_SKILL_LEVELS) {
|
|
Warning(LOCATION, "Received packet containing invalid skill level %i! Valid range 0 to %i. Resetting to default.", Game_skill_level, NUM_SKILL_LEVELS);
|
|
Game_skill_level = game_get_default_skill_level();
|
|
}
|
|
|
|
// get my netplayer number
|
|
GET_DATA(my_player_num);
|
|
|
|
// get my id #
|
|
GET_SHORT(player_id);
|
|
|
|
// get netgame type flags
|
|
GET_INT(Netgame.type_flags);
|
|
|
|
// setup the Net_players structure for myself first
|
|
Net_player = &Net_players[my_player_num];
|
|
Net_player->flags = 0;
|
|
Net_player->tracker_player_id = Multi_tracker_id;
|
|
Net_player->player_id = player_id;
|
|
Net_player->s_info.xfer_handle = -1;
|
|
// stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
|
|
stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
|
|
multi_options_local_load(&Net_player->p_info.options, Net_player);
|
|
Net_player->p_info.team = team;
|
|
|
|
// set accept code in netplayer for this guy
|
|
if ( code & ACCEPT_INGAME ){
|
|
Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
|
|
}
|
|
if ( code & ACCEPT_OBSERVER ){
|
|
Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
|
|
}
|
|
if ( code & ACCEPT_HOST ){
|
|
Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
|
|
}
|
|
if ( code & ACCEPT_CLIENT ){
|
|
Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
|
|
}
|
|
|
|
// if I have hacked data
|
|
if(game_hacked_data()){
|
|
Net_player->flags |= NETINFO_FLAG_HAXOR;
|
|
}
|
|
|
|
// if we're supposed to flush our local data cache, do so now
|
|
if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
|
|
multi_flush_multidata_cache();
|
|
}
|
|
|
|
Net_player->sv_bytes_sent = 0;
|
|
Net_player->sv_last_pl = -1;
|
|
Net_player->cl_bytes_recvd = 0;
|
|
Net_player->cl_last_pl = -1;
|
|
|
|
// intiialize endgame stuff
|
|
multi_endgame_init();
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// make a call to psnet to initialize and try to connect with the server.
|
|
psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
|
|
if ( Net_player->reliable_socket == PSNET_INVALID_SOCKET ) {
|
|
multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Player Leave Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_leave_game_packet
|
|
{
|
|
char packet_signature;
|
|
char kicked_reason;
|
|
short player_id;
|
|
|
|
}
|
|
*/
|
|
// send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
|
|
void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
char val;
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(LEAVE_GAME);
|
|
|
|
// add a flag indicating whether he was kicked or not
|
|
val = (char)kicked_reason;
|
|
ADD_DATA(val);
|
|
|
|
if (player_id < 0) {
|
|
ADD_SHORT(Net_player->player_id);
|
|
|
|
// inform the host that we are leaving the game
|
|
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
// this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
|
|
// so he has to tell everyone that this guy left
|
|
else {
|
|
nprintf(("Network","Sending a leave game packet to all players (server)\n"));
|
|
|
|
// a couple of important checks
|
|
Assert(player_id != Net_player->player_id);
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
|
|
// add the id of the guy to be kicked
|
|
ADD_SHORT(player_id);
|
|
|
|
// broadcast to everyone
|
|
if (target == NULL) {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(target, data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
// process a notification that a player has left the game
|
|
void process_leave_game_packet(ubyte* data, header* hinfo)
|
|
{
|
|
int offset;
|
|
short deader_id;
|
|
int player_num;
|
|
char kicked_reason;
|
|
char str[512];
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get whether he was kicked
|
|
GET_DATA(kicked_reason);
|
|
|
|
// get the address of the guy who is to leave
|
|
GET_SHORT(deader_id);
|
|
PACKET_SET_SIZE();
|
|
|
|
// determine who is dropping and printf out a notification
|
|
player_num = find_player_index(deader_id);
|
|
if (player_num == -1) {
|
|
nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
|
|
return;
|
|
|
|
} else {
|
|
nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].m_player->callsign));
|
|
}
|
|
|
|
// a hook to display that a player was kicked
|
|
if (kicked_reason >= 0){
|
|
// if it was me that was kicked, leave the game
|
|
if((Net_player != NULL) && (Net_player->player_id == deader_id)){
|
|
int notify_code;
|
|
|
|
switch(kicked_reason){
|
|
case KICK_REASON_BAD_XFER:
|
|
notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
|
|
break;
|
|
case KICK_REASON_CANT_XFER:
|
|
notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
|
|
break;
|
|
case KICK_REASON_INGAME_ENDED:
|
|
notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
|
|
break;
|
|
default:
|
|
notify_code = MULTI_END_NOTIFY_KICKED;
|
|
break;
|
|
}
|
|
|
|
multi_quit_game(PROMPT_NONE, notify_code);
|
|
return;
|
|
|
|
// otherwise indicate someone was kicked
|
|
} else {
|
|
nprintf(("Network","%s was kicked\n",Net_players[player_num].m_player->callsign));
|
|
|
|
// display the result
|
|
memset(str, 0, 512);
|
|
multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
|
|
multi_display_chat_msg(str, player_num, 0);
|
|
}
|
|
}
|
|
|
|
// first of all, if we're the master, we should be rebroadcasting this packet
|
|
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
|
|
char msg[255];
|
|
|
|
sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].m_player->callsign );
|
|
|
|
if (!(Game_mode & GM_STANDALONE_SERVER)){
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", msg);
|
|
}
|
|
|
|
send_hud_msg_to_all(msg);
|
|
multi_io_send_to_all_reliable(data, offset);
|
|
}
|
|
|
|
// leave the game if the host and/or master has dropped
|
|
/*
|
|
if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
|
|
nprintf(("Network","Host and/or server has left the game - aborting...\n"));
|
|
|
|
// NETLOG
|
|
ml_string(NOX("Host and/or server has left the game"));
|
|
|
|
// if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
|
|
if (gameseq_get_state() != GS_STATE_DEBRIEF) {
|
|
multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
|
|
}
|
|
|
|
delete_player(player_num);
|
|
} else {
|
|
*/
|
|
delete_player(player_num);
|
|
|
|
// OSAPI GUI stuff (if standalone)
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
// returns true if we should reset the standalone
|
|
if (std_remove_player(&Net_players[player_num])) {
|
|
nprintf(("Network", "Should reset!!\n"));
|
|
return;
|
|
}
|
|
|
|
// update these gui vals
|
|
std_connect_set_host_connect_status();
|
|
std_connect_set_connect_count();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Game active packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_game_active_packet
|
|
{
|
|
char packet_signature;
|
|
ubyte server_version;
|
|
ubyte compat_version;
|
|
int len1;
|
|
char netgame_name[len1];
|
|
int len2;
|
|
char netgame_mission_name[len2];
|
|
int len3;
|
|
char netgame_title[len3];
|
|
ubyte num_players;
|
|
unsigned short flags;
|
|
}
|
|
*/
|
|
// send information about this currently active game to the specified address
|
|
void send_game_active_packet(net_addr* addr)
|
|
{
|
|
int packet_size;
|
|
ushort flags;
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(GAME_ACTIVE);
|
|
|
|
// add the server version and compatible version #
|
|
val = MULTI_FS_SERVER_VERSION;
|
|
ADD_DATA(val);
|
|
val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
|
|
ADD_DATA(val);
|
|
|
|
// use 4-byte version of *_STRING to remain compatible with older builds
|
|
ADD_STRING_32(Netgame.name);
|
|
ADD_STRING_32(Netgame.mission_name);
|
|
ADD_STRING_32(Netgame.title);
|
|
|
|
val = (ubyte)multi_num_players();
|
|
ADD_DATA(val);
|
|
|
|
// add the proper flags
|
|
flags = 0;
|
|
if ( (Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd())) ) {
|
|
flags |= AG_FLAG_PASSWD;
|
|
}
|
|
|
|
// proper netgame type flags
|
|
if(Netgame.type_flags & NG_TYPE_TEAM){
|
|
flags |= AG_FLAG_TEAMS;
|
|
} else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
|
|
flags |= AG_FLAG_DOGFIGHT;
|
|
} else {
|
|
flags |= AG_FLAG_COOP;
|
|
}
|
|
|
|
// proper netgame state flags
|
|
switch(Netgame.game_state){
|
|
case NETGAME_STATE_FORMING:
|
|
flags |= AG_FLAG_FORMING;
|
|
break;
|
|
|
|
case NETGAME_STATE_BRIEFING:
|
|
case NETGAME_STATE_MISSION_SYNC:
|
|
case NETGAME_STATE_HOST_SETUP:
|
|
flags |= AG_FLAG_BRIEFING;
|
|
break;
|
|
|
|
case NETGAME_STATE_IN_MISSION:
|
|
flags |= AG_FLAG_IN_MISSION;
|
|
break;
|
|
|
|
case NETGAME_STATE_PAUSED:
|
|
flags |= AG_FLAG_PAUSE;
|
|
break;
|
|
|
|
case NETGAME_STATE_ENDGAME:
|
|
case NETGAME_STATE_DEBRIEF:
|
|
flags |= AG_FLAG_DEBRIEF;
|
|
break;
|
|
}
|
|
|
|
// if this is a standalone
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
flags |= AG_FLAG_STANDALONE;
|
|
}
|
|
|
|
// if we're in campaign mode
|
|
if(Netgame.campaign_mode == MP_CAMPAIGN){
|
|
flags |= AG_FLAG_CAMPAIGN;
|
|
}
|
|
|
|
// if we're registered on multi tracker
|
|
if (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) {
|
|
flags |= AG_FLAG_TRACKER;
|
|
}
|
|
|
|
// add the data about the connection speed of the host machine
|
|
Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
|
|
flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
|
|
|
|
ADD_USHORT(flags);
|
|
|
|
// send the data
|
|
psnet_send(addr, data, packet_size);
|
|
}
|
|
|
|
// process information about an active game
|
|
void process_game_active_packet(ubyte* data, header* hinfo)
|
|
{
|
|
int offset;
|
|
ubyte val;
|
|
active_game ag;
|
|
int modes_compatible = 1;
|
|
|
|
fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->port);
|
|
|
|
// read this game into a temporary structure
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the server version and compatible version
|
|
GET_DATA(ag.version);
|
|
GET_DATA(ag.comp_version);
|
|
|
|
// use 4-byte version of *_STRING to remain compatible with older builds
|
|
GET_STRING_32(ag.name);
|
|
GET_STRING_32(ag.mission_name);
|
|
GET_STRING_32(ag.title);
|
|
|
|
GET_DATA(val);
|
|
ag.num_players = val;
|
|
GET_USHORT(ag.flags);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
if ( (ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo )
|
|
modes_compatible = 0;
|
|
|
|
if ( !(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo )
|
|
modes_compatible = 0;
|
|
|
|
// if this is a compatible version, and our modes are compatible, register it
|
|
if ( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ) {
|
|
if (gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME) {
|
|
multi_join_maybe_update_selected(&ag);
|
|
}
|
|
|
|
multi_update_active_games(&ag);
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Game Update Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
struct fs2_game_update
|
|
{
|
|
char packet_signature;
|
|
int len1;
|
|
char netgame_name[len1];
|
|
int len2;
|
|
char netgame_mission_name[len2];
|
|
int len3;
|
|
char netgame_title[len3];
|
|
int len4;
|
|
char netgame_campaign_name[len4];
|
|
int campaign_mode;
|
|
int max_players;
|
|
int security;
|
|
unsigned int respawn;
|
|
int flags;
|
|
int type_flags;
|
|
int version_info;
|
|
ubyte debug_flags;
|
|
|
|
// !!!!!! this isn't relying on information earlier in the packet!
|
|
// receiving seems to always assume it's there!
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
int game_state;
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
|
|
// is used to change the current mission, current state, etc.
|
|
void send_netgame_update_packet(net_player *pl, const bool interval)
|
|
{
|
|
int packet_size;
|
|
int idx;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
|
|
packet_size = 0;
|
|
BUILD_HEADER(GAME_UPDATE);
|
|
|
|
// with new mission description field, this becomes way to large
|
|
// so we must add every element piece by piece except the
|
|
ADD_STRING(Netgame.name);
|
|
ADD_STRING(Netgame.mission_name);
|
|
ADD_STRING(Netgame.title);
|
|
ADD_STRING(Netgame.campaign_name);
|
|
ADD_INT(Netgame.campaign_mode);
|
|
ADD_INT(Netgame.max_players);
|
|
ADD_INT(Netgame.security);
|
|
ADD_UINT(Netgame.respawn);
|
|
ADD_INT(Netgame.flags);
|
|
ADD_INT(Netgame.type_flags);
|
|
ADD_INT(Netgame.version_info);
|
|
ADD_DATA(Netgame.debug_flags);
|
|
|
|
// only the server should ever send the netgame state (standalone situation)
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
ADD_INT(Netgame.game_state);
|
|
}
|
|
|
|
// if we're the host on a standalone, send to the standalone and let him rebroadcast
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
if ( pl == NULL ) {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
|
|
send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
|
|
}
|
|
}
|
|
} else {
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
send_netgame_descript_packet( &pl->p_info.addr , 1 );
|
|
}
|
|
} else {
|
|
Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
// host should always send a netgame options update as well
|
|
if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
|
|
multi_options_update_netgame();
|
|
}
|
|
|
|
// update tracker as well
|
|
if ( (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) && !interval ) {
|
|
multi_fs_tracker_update_game(&Netgame);
|
|
}
|
|
|
|
}
|
|
|
|
// process information about the netgame sent from the server/host
|
|
void process_netgame_update_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
int ng_state;
|
|
|
|
Assert(!(Game_mode & GM_STANDALONE_SERVER));
|
|
Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
|
|
// read in the netgame information
|
|
offset = HEADER_LENGTH;
|
|
GET_STRING(Netgame.name);
|
|
GET_STRING(Netgame.mission_name);
|
|
GET_STRING(Netgame.title);
|
|
GET_STRING(Netgame.campaign_name);
|
|
GET_INT(Netgame.campaign_mode);
|
|
GET_INT(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
|
|
GET_INT(Netgame.security);
|
|
GET_UINT(Netgame.respawn);
|
|
|
|
// be sure not to blast the quitting flag because of the "one frame extra" problem
|
|
GET_INT(Netgame.flags);
|
|
GET_INT(Netgame.type_flags);
|
|
GET_INT(Netgame.version_info);
|
|
GET_DATA(Netgame.debug_flags);
|
|
|
|
// netgame state
|
|
GET_INT(ng_state);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// now compare the passed in game state to our current known state. If it has changed, then maybe
|
|
// do something interesting.
|
|
// move from the forming or debriefing state to the mission sync state
|
|
if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
|
|
// if coming from the forming state
|
|
if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
|
|
((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
|
|
// do any special processing for forced state transitions
|
|
multi_handle_state_special();
|
|
|
|
Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
|
|
strcpy_s( Game_current_mission_filename, Netgame.mission_name );
|
|
gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
|
|
}
|
|
// if coming from the debriefing state
|
|
else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
|
|
((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
|
|
|
|
// do any special processing for forced state transitions
|
|
multi_handle_state_special();
|
|
|
|
multi_flush_mission_stuff();
|
|
|
|
Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
|
|
strcpy_s( Game_current_mission_filename, Netgame.mission_name );
|
|
gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
|
|
}
|
|
}
|
|
// move from mission sync to team select
|
|
else if ( ng_state == NETGAME_STATE_BRIEFING ){
|
|
if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
|
|
((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
|
|
|
|
// do any special processing for forced state transitions
|
|
multi_handle_state_special();
|
|
|
|
strcpy_s( Game_current_mission_filename, Netgame.mission_name );
|
|
|
|
extern bool mission_has_fiction();
|
|
extern int mission_has_cmd_brief();
|
|
extern int red_alert_mission();
|
|
|
|
if ( mission_has_fiction() ) {
|
|
gameseq_post_event(GS_EVENT_FICTION_VIEWER);
|
|
} else if ( mission_has_cmd_brief() ) {
|
|
gameseq_post_event(GS_EVENT_CMD_BRIEF);
|
|
} else if ( red_alert_mission() ) {
|
|
gameseq_post_event(GS_EVENT_RED_ALERT);
|
|
} else {
|
|
gameseq_post_event(GS_EVENT_START_BRIEFING);
|
|
}
|
|
}
|
|
}
|
|
// move from the debriefing to the create game screen
|
|
else if ( ng_state == NETGAME_STATE_FORMING ){
|
|
if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
|
|
((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
|
|
// do any special processing for forced state transitions
|
|
multi_handle_state_special();
|
|
|
|
multi_flush_mission_stuff();
|
|
|
|
// move to the proper screen
|
|
if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
|
|
gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
|
|
} else {
|
|
gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
|
|
}
|
|
}
|
|
}
|
|
|
|
Netgame.game_state = ng_state;
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
// Game Update Packet
|
|
//*********************************************************************************************************
|
|
/*
|
|
|
|
*/
|
|
// send a request or a reply for mission description, if code == 0, request, if code == 1, reply
|
|
// Cyborg17 - Now recursive for longer descriptions!
|
|
void send_netgame_descript_packet(net_addr *addr, int code)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
// Get this out of the way, because we don't want to waste our time if the addr is bad.
|
|
if (addr == nullptr) {
|
|
UNREACHABLE("Net address not specified!");
|
|
return;
|
|
}
|
|
|
|
// if we are the client requesting, just send
|
|
if ( !code ) {
|
|
// build the header
|
|
BUILD_HEADER(UPDATE_DESCRIPT);
|
|
|
|
ubyte val = 0;
|
|
ADD_DATA(val);
|
|
|
|
psnet_send(addr, data, packet_size);
|
|
}
|
|
// otherwise, break up description string if necessary and send to client
|
|
else {
|
|
extern SCP_string Multi_netgame_common_description;
|
|
|
|
SCP_string message;
|
|
const size_t MAX_LEN = MAX_PACKET_SIZE - HEADER_LENGTH - 10;
|
|
const size_t desc_len = Multi_netgame_common_description.length();
|
|
size_t desc_offset = 0;
|
|
|
|
ubyte index = 1; // to indicate which message index this is for concatenation - 1, 2, ...
|
|
|
|
do {
|
|
message = Multi_netgame_common_description.substr(desc_offset, std::min(desc_len, MAX_LEN));
|
|
|
|
BUILD_HEADER(UPDATE_DESCRIPT);
|
|
|
|
ADD_DATA(index);
|
|
ADD_STRING_16(message.c_str());
|
|
|
|
psnet_send(addr, data, packet_size);
|
|
|
|
++index;
|
|
desc_offset += message.length();
|
|
} while (desc_offset < desc_len);
|
|
}
|
|
}
|
|
|
|
// process an incoming netgame description packet
|
|
void process_netgame_descript_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, state;
|
|
ubyte code;
|
|
net_addr addr;
|
|
|
|
fill_net_addr(&addr, hinfo->addr, hinfo->port);
|
|
|
|
// read this game into a temporary structure
|
|
offset = HEADER_LENGTH;
|
|
GET_DATA(code);
|
|
|
|
// if this is a request for mission description
|
|
if(code == 0){
|
|
if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
PACKET_SET_SIZE();
|
|
return;
|
|
}
|
|
|
|
// send an update to this guy
|
|
send_netgame_descript_packet(&addr, 1);
|
|
}
|
|
// otherwise 'code' will be an index indicating the message number - 1, 2, ...
|
|
else {
|
|
char message[MAX_PACKET_SIZE];
|
|
|
|
GET_STRING_16(message);
|
|
|
|
// only display if we're in the proper state, since the client doesn't
|
|
// decide when this packet arrives
|
|
state = gameseq_get_state();
|
|
|
|
switch (state) {
|
|
case GS_STATE_MULTI_JOIN_GAME:
|
|
case GS_STATE_MULTI_CLIENT_SETUP:
|
|
case GS_STATE_MULTI_HOST_SETUP:
|
|
multi_mission_desciption_set(message, code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
// broadcast a query for active games. TCP will either request from the MT or from the specified list
|
|
void broadcast_game_query()
|
|
{
|
|
server_item *s_moveup;
|
|
|
|
if (MULTI_IS_TRACKER_GAME) {
|
|
// check with MT
|
|
multi_fs_tracker_send_game_request();
|
|
return;
|
|
} else {
|
|
// go through the server list and query each of those as well
|
|
s_moveup = Game_server_head;
|
|
if (s_moveup != nullptr) {
|
|
do {
|
|
send_server_query(&s_moveup->server_addr);
|
|
s_moveup = s_moveup->next;
|
|
} while (s_moveup != Game_server_head);
|
|
}
|
|
}
|
|
|
|
// send out a broadcast if our options allow us
|
|
if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
|
|
multi_mdns_query();
|
|
}
|
|
}
|
|
|
|
// send an individual query to an address to see if there is an active game
|
|
void send_server_query(net_addr *addr)
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
|
|
// build the header and send the data
|
|
BUILD_HEADER(GAME_QUERY);
|
|
psnet_send(addr, data, packet_size);
|
|
}
|
|
|
|
// process a query from a client looking for active freespace games
|
|
void process_game_query(ubyte* /*data*/, header* hinfo)
|
|
{
|
|
int offset;
|
|
net_addr addr;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// if I am not a server of a game, don't send a reply!!!
|
|
if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
|
|
return;
|
|
}
|
|
|
|
// if the game options are being selected, then ignore the request
|
|
// also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
|
|
if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
|
|
return;
|
|
}
|
|
|
|
// check to be sure that we don't capture our own broadcast message
|
|
fill_net_addr(&addr, hinfo->addr, hinfo->port);
|
|
if ( psnet_is_local_addr(&addr) ) {
|
|
return;
|
|
}
|
|
|
|
// send information about this active game
|
|
send_game_active_packet(&addr);
|
|
}
|
|
|
|
// sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
|
|
void send_netplayer_update_packet( net_player *pl )
|
|
{
|
|
int packet_size,idx;
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
|
|
BUILD_HEADER(NETPLAYER_UPDATE);
|
|
|
|
// if I'm the server of the game, I should send an update for _all_players in the game
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
// only send info for connected players
|
|
if(MULTI_CONNECTED(Net_players[idx])){
|
|
// add a stop byte
|
|
val = 0x0;
|
|
ADD_DATA(val);
|
|
|
|
// add the net player's information
|
|
ADD_SHORT(Net_players[idx].player_id);
|
|
ADD_INT(Net_players[idx].state);
|
|
ADD_INT(Net_players[idx].p_info.ship_class);
|
|
ADD_INT(Net_players[idx].tracker_player_id);
|
|
|
|
//Used to be a check for a CD here --Mjn
|
|
val = 1;
|
|
ADD_DATA(val);
|
|
}
|
|
}
|
|
// add the final stop byte
|
|
val = 0xff;
|
|
ADD_DATA(val);
|
|
|
|
// broadcast the packet
|
|
if(!(Game_mode & GM_IN_MISSION)){
|
|
if ( pl == NULL ) {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
}
|
|
} else {
|
|
if ( pl == NULL ) {
|
|
multi_io_send_to_all(data, packet_size);
|
|
} else {
|
|
multi_io_send(pl, data, packet_size);
|
|
}
|
|
}
|
|
} else {
|
|
// add a stop byte
|
|
val = 0x0;
|
|
ADD_DATA(val);
|
|
|
|
// add my current state in the netgame to this packet
|
|
ADD_SHORT(Net_player->player_id);
|
|
ADD_INT(Net_player->state);
|
|
ADD_INT(Net_player->p_info.ship_class);
|
|
ADD_INT(Multi_tracker_id);
|
|
|
|
// Used to check or a CD here but that has been removed --Mjn
|
|
val = 1;
|
|
ADD_DATA(val);
|
|
|
|
// add a final stop byte
|
|
val = 0xff;
|
|
ADD_DATA(val);
|
|
|
|
// send the packet to the server
|
|
Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
|
|
if(!(Game_mode & GM_IN_MISSION)){
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
} else {
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
// process an incoming netplayer state update. if we're the server, we should rebroadcast
|
|
void process_netplayer_update_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, player_num;
|
|
net_player bogus;
|
|
ubyte stop, has_cd;
|
|
short player_id;
|
|
int new_state;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the first stop byte
|
|
GET_DATA(stop);
|
|
player_num = -1;
|
|
while(stop != 0xff){
|
|
// look the player up
|
|
GET_SHORT(player_id);
|
|
player_num = find_player_index(player_id);
|
|
// if we couldn't find him, read in the bogus data
|
|
if((player_num == -1) || (Net_player == &Net_players[player_num])){
|
|
GET_INT(bogus.state);
|
|
GET_INT(bogus.p_info.ship_class);
|
|
GET_INT(bogus.tracker_player_id);
|
|
|
|
GET_DATA(has_cd);
|
|
}
|
|
// otherwise read in the data correctly
|
|
else {
|
|
GET_INT(new_state);
|
|
GET_INT(Net_players[player_num].p_info.ship_class);
|
|
GET_INT(Net_players[player_num].tracker_player_id);
|
|
GET_DATA(has_cd); //unused
|
|
|
|
// if he's changing state to joined, send a team update
|
|
if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
|
|
multi_team_send_update();
|
|
}
|
|
|
|
// set state
|
|
Net_players[player_num].state = new_state;
|
|
}
|
|
|
|
// get the next stop byte
|
|
GET_DATA(stop);
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
send_netplayer_update_packet(NULL);
|
|
}
|
|
|
|
// if i'm the standalone and this is an update from the host, maybe change some netgame settings
|
|
if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
|
|
switch(Net_players[player_num].state){
|
|
case NETPLAYER_STATE_STD_HOST_SETUP:
|
|
Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
|
|
break;
|
|
|
|
case NETPLAYER_STATE_HOST_SETUP:
|
|
// check for race conditions
|
|
if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
|
|
Netgame.game_state = NETGAME_STATE_FORMING;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define EXTRA_DEATH_VAPORIZED (1<<0)
|
|
#define EXTRA_DEATH_WASHED (1<<1)
|
|
// send a packet indicating a ship has been killed
|
|
void send_ship_kill_packet( const object *objp, const object *other_objp, float percent_killed, int self_destruct )
|
|
{
|
|
int packet_size, model;
|
|
ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
|
|
ushort debris_signature;
|
|
ubyte sd;
|
|
polymodel * pm;
|
|
|
|
// only sendable from the master
|
|
Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
|
|
|
|
// special deaths
|
|
vaporized = Ships[objp->instance].flags[Ship::Ship_Flags::Vaporize];
|
|
|
|
extra_death_info = 0;
|
|
if ( vaporized ) {
|
|
extra_death_info |= EXTRA_DEATH_VAPORIZED;
|
|
}
|
|
|
|
if ( Ships[objp->instance].wash_killed ) {
|
|
extra_death_info |= EXTRA_DEATH_WASHED;
|
|
}
|
|
|
|
// find out the next network signature that will be used for the debris pieces.
|
|
model = Ship_info[Ships[objp->instance].ship_info_index].model_num;
|
|
pm = model_get(model);
|
|
debris_signature = 0;
|
|
if ( pm && !vaporized ) {
|
|
debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
|
|
multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
|
|
Ships[objp->instance].debris_net_sig = debris_signature;
|
|
}
|
|
|
|
BUILD_HEADER(SHIP_KILL);
|
|
ADD_USHORT(objp->net_signature);
|
|
|
|
// ships which are initially killed get the rest of the data sent. self destructed ships and
|
|
if ( other_objp == NULL ) {
|
|
ushort temp;
|
|
|
|
temp = 0;
|
|
ADD_USHORT(temp);
|
|
nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
|
|
} else {
|
|
ADD_USHORT( other_objp->net_signature );
|
|
}
|
|
|
|
ADD_USHORT( debris_signature );
|
|
ADD_FLOAT( percent_killed );
|
|
sd = (ubyte)self_destruct;
|
|
ADD_DATA(sd);
|
|
ADD_DATA( extra_death_info );
|
|
|
|
// if the ship who died is a player, then send some extra info, like who killed him, etc.
|
|
was_player = 0;
|
|
if ( objp->flags[Object::Object_Flags::Player_ship] ) {
|
|
int pnum;
|
|
char temp;
|
|
short temp2;
|
|
|
|
pnum = multi_find_player_by_object( objp );
|
|
if ( pnum != -1 ) {
|
|
was_player = 1;
|
|
ADD_DATA( was_player );
|
|
|
|
Assert(Net_players[pnum].m_player->killer_objtype < CHAR_MAX);
|
|
temp = (char)Net_players[pnum].m_player->killer_objtype;
|
|
ADD_DATA( temp );
|
|
|
|
Assert(Net_players[pnum].m_player->killer_species < CHAR_MAX);
|
|
temp = (char)Net_players[pnum].m_player->killer_species;
|
|
ADD_DATA( temp );
|
|
|
|
Assert(Net_players[pnum].m_player->killer_weapon_index < SHRT_MAX);
|
|
temp2 = (short)Net_players[pnum].m_player->killer_weapon_index;
|
|
ADD_SHORT( temp2 );
|
|
|
|
ADD_STRING( Net_players[pnum].m_player->killer_parent_name );
|
|
} else {
|
|
ADD_DATA( was_player );
|
|
}
|
|
} else {
|
|
ADD_DATA( was_player );
|
|
}
|
|
|
|
// send the packet reliably!!!
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a packet indicating that a ship has been killed
|
|
void process_ship_kill_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort ship_sig, other_sig, debris_sig;
|
|
object *sobjp, *oobjp;
|
|
float percent_killed;
|
|
ubyte was_player, extra_death_info, sd;
|
|
char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = 0;
|
|
short killer_weapon_index = -1;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(ship_sig);
|
|
|
|
GET_USHORT( other_sig );
|
|
GET_USHORT( debris_sig );
|
|
GET_FLOAT( percent_killed );
|
|
GET_DATA( sd );
|
|
GET_DATA( extra_death_info );
|
|
GET_DATA( was_player );
|
|
|
|
|
|
// pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
|
|
if ( was_player != 0 ) {
|
|
GET_DATA( killer_objtype );
|
|
GET_DATA( killer_species );
|
|
GET_SHORT( killer_weapon_index );
|
|
GET_STRING( killer_name );
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
sobjp = multi_get_network_object( ship_sig );
|
|
|
|
// if I am unable to find the ship object which was killed, I have to bail and rely on getting
|
|
// another message from the server that this happened!
|
|
if ( sobjp == NULL ) {
|
|
nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
|
|
return;
|
|
}
|
|
|
|
// set this ship's hull value to 0
|
|
sobjp->hull_strength = 0.0f;
|
|
|
|
// maybe set vaporized
|
|
if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
|
|
Ships[sobjp->instance].flags.set(Ship::Ship_Flags::Vaporize);
|
|
}
|
|
|
|
// maybe set wash_killed
|
|
if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
|
|
Ships[sobjp->instance].wash_killed = 1;
|
|
}
|
|
|
|
oobjp = multi_get_network_object( other_sig );
|
|
|
|
if ( was_player != 0 ) {
|
|
int pnum;
|
|
|
|
pnum = multi_find_player_by_object( sobjp );
|
|
if ( pnum != -1 ) {
|
|
Net_players[pnum].m_player->killer_objtype = killer_objtype;
|
|
Net_players[pnum].m_player->killer_species = killer_species;
|
|
Net_players[pnum].m_player->killer_weapon_index = killer_weapon_index;
|
|
strcpy_s( Net_players[pnum].m_player->killer_parent_name, killer_name );
|
|
}
|
|
}
|
|
|
|
// check to see if I need to respawn myself
|
|
multi_respawn_check(sobjp);
|
|
|
|
// store the debris signature in the arrival distance which will never get used for player ships
|
|
Ships[sobjp->instance].debris_net_sig = debris_sig;
|
|
|
|
// set this bit so that we don't accidentally start switching targets when we die
|
|
if(sobjp == Player_obj){
|
|
Game_mode |= GM_DEAD_DIED;
|
|
}
|
|
|
|
mprintf(("Network Killing off %s\n", Ships[sobjp->instance].ship_name));
|
|
|
|
// do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
|
|
if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
|
|
ship_hit_kill( sobjp, oobjp, nullptr, percent_killed, sd != 0 );
|
|
} else {
|
|
sobjp->flags.set(Object::Object_Flags::Should_be_dead);
|
|
ship_cleanup(sobjp->instance, SHIP_DESTROYED);
|
|
obj_delete( OBJ_INDEX(sobjp) );
|
|
}
|
|
}
|
|
|
|
constexpr int WK_FLAGS_WEAPON_WEAPON = (1<<0);
|
|
|
|
// simply removes a missile from existence on the client.
|
|
void send_missile_kill_packet(object* objp)
|
|
{
|
|
Assert(objp != nullptr);
|
|
Assert(objp->type == OBJ_WEAPON);
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
if (objp == nullptr || objp->type != OBJ_WEAPON || MULTIPLAYER_CLIENT || !(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype & WP_MISSILE)) {
|
|
return;
|
|
}
|
|
|
|
ubyte data[MAX_PACKET_SIZE], flags = 0;
|
|
int packet_size;
|
|
|
|
BUILD_HEADER(MISSILE_KILL);
|
|
|
|
ADD_USHORT(objp->net_signature);
|
|
if (objp->instance > -1) {
|
|
if (Weapons[objp->instance].weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon])
|
|
flags |= WK_FLAGS_WEAPON_WEAPON;
|
|
}
|
|
|
|
ADD_DATA(flags);
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_weapon_kill_packet(ubyte *data, header *hinfo )
|
|
{
|
|
// only masters should receive this packet.
|
|
Assert(MULTIPLAYER_CLIENT);
|
|
|
|
int offset = HEADER_LENGTH;
|
|
ushort missile_net_signature;
|
|
ubyte flags;
|
|
|
|
GET_USHORT(missile_net_signature);
|
|
GET_DATA(flags);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get the object pointer for the missile
|
|
object* missile = multi_get_network_object(missile_net_signature);
|
|
|
|
// make sure it's valid before implementing.
|
|
if (missile == nullptr || missile->type != OBJ_WEAPON || missile->instance < 0 || !(Weapon_info[Weapons[missile->instance].weapon_info_index].subtype & WP_MISSILE)) {
|
|
return;
|
|
}
|
|
|
|
// set any optional packets.
|
|
if (flags & WK_FLAGS_WEAPON_WEAPON) {
|
|
Weapons[missile->instance].weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
}
|
|
|
|
// kill the missile!
|
|
if (Weapon_info[Weapons[missile->instance].weapon_info_index].weapon_hitpoints > 0.0f) {
|
|
missile->hull_strength = -1.0f;
|
|
}
|
|
|
|
weapon_detonate(missile);
|
|
}
|
|
|
|
// send a packet indicating a ship should be created
|
|
void send_ship_create_packet( object *objp, bool is_support )
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte val;
|
|
|
|
// We will pass the ship to create by name.
|
|
BUILD_HEADER(SHIP_CREATE);
|
|
ADD_USHORT(objp->net_signature);
|
|
val = is_support ? 1 : 0;
|
|
ADD_DATA(val);
|
|
if ( is_support ){
|
|
ADD_VECTOR( objp->pos );
|
|
}
|
|
|
|
// broadcast the packet
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a packet indicating a ship should be created
|
|
void process_ship_create_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, objnum;
|
|
ushort signature;
|
|
p_object *objp;
|
|
vec3d pos = ZERO_VECTOR;
|
|
ubyte is_support;
|
|
|
|
Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(signature);
|
|
GET_DATA(is_support);
|
|
if ( is_support ){
|
|
GET_VECTOR( pos );
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
|
|
if ( !is_support ) {
|
|
objp = mission_parse_get_arrival_ship( signature );
|
|
if ( objp != NULL ) {
|
|
objnum = parse_create_object(objp);
|
|
} else {
|
|
nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
|
|
}
|
|
} else {
|
|
Assert( Arriving_support_ship );
|
|
if(Arriving_support_ship == NULL){
|
|
return;
|
|
}
|
|
Arriving_support_ship->pos = pos;
|
|
Arriving_support_ship->net_signature = signature;
|
|
objnum = parse_create_object( Arriving_support_ship );
|
|
Assert( objnum != -1 );
|
|
if(objnum >= 0){
|
|
mission_parse_support_arrived( objnum );
|
|
}
|
|
}
|
|
}
|
|
|
|
// send a packet indicating a wing of ships should be created
|
|
void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
|
|
{
|
|
int packet_size, index, ship_instance;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ushort signature;
|
|
int val;
|
|
|
|
// for creating wing -- we just send the index into the wing array of this wing.
|
|
// all players load the same mission, and so their array's should all match. We also
|
|
// need to send the signature of the first ship that was created. We can find this by
|
|
// looking num_to_create places back in the ship_index field in the wing structure.
|
|
|
|
index = WING_INDEX(wingp);
|
|
ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
|
|
signature = Objects[Ships[ship_instance].objnum].net_signature;
|
|
|
|
BUILD_HEADER( WING_CREATE );
|
|
ADD_INT(index);
|
|
ADD_INT(num_to_create);
|
|
ADD_USHORT(signature);
|
|
ADD_INT(pre_create_count);
|
|
val = wingp->current_wave - 1;
|
|
ADD_INT(val);
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a packet saying that a wing should be created
|
|
void process_wing_create_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, index, num_to_create;
|
|
ushort signature;
|
|
int total_arrived_count, current_wave;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_INT(index);
|
|
GET_INT(num_to_create);
|
|
GET_USHORT(signature);
|
|
GET_INT(total_arrived_count);
|
|
GET_INT(current_wave);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// do a sanity check on the wing to be sure that we are actually working on a valid wing
|
|
if ( (index < 0) || (index >= Num_wings) || (Wings[index].num_waves == -1) ) {
|
|
nprintf(("Network", "Invalid index %d for wing create packet\n", index));
|
|
return;
|
|
}
|
|
if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
|
|
nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
|
|
return;
|
|
}
|
|
|
|
// bash some info
|
|
Wings[index].current_count = 0;
|
|
Wings[index].total_arrived_count = total_arrived_count;
|
|
Wings[index].current_wave = current_wave;
|
|
|
|
// set the network signature that was passed. The client should create ships in the same order
|
|
// as the server -- so all ships should get the same sigs as assigned by the server. We also
|
|
// need to set some timestamps and cues correctly to be sure that these things get created on
|
|
// the clients correctly
|
|
multi_set_network_signature( signature, MULTI_SIG_SHIP );
|
|
parse_wing_create_ships( &Wings[index], num_to_create, true );
|
|
}
|
|
|
|
// packet indicating a ship is departing
|
|
void send_ship_depart_packet( object *objp, int method )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
ushort signature;
|
|
|
|
signature = objp->net_signature;
|
|
|
|
BUILD_HEADER(SHIP_DEPART);
|
|
ADD_USHORT( signature );
|
|
ADD_INT( method );
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a packet indicating a ship is departing
|
|
void process_ship_depart_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
object *objp;
|
|
ushort signature;
|
|
int s_method;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT( signature );
|
|
GET_INT(s_method);
|
|
PACKET_SET_SIZE();
|
|
|
|
// find the object which is departing
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
|
|
return;
|
|
}
|
|
|
|
switch (s_method) {
|
|
case SHIP_DEPARTED_BAY:
|
|
case SHIP_VANISHED:
|
|
if (objp->type == OBJ_SHIP) {
|
|
ship_actually_depart(objp->instance, s_method);
|
|
}
|
|
else {
|
|
nprintf(("network", "Can not process ship depart packed. Object with net signature %d is not a ship!\n", signature ));
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// assume standard warp out
|
|
default:
|
|
// start warping him out
|
|
shipfx_warpout_start( objp );
|
|
}
|
|
}
|
|
|
|
// packet to tell clients cargo of a ship was revealed to all
|
|
void send_cargo_revealed_packet( ship *shipp )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(CARGO_REVEALED);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
|
|
// server sends to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// clients just send to the server
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a cargo revealed packet
|
|
void process_cargo_revealed_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
object *objp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(signature);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get a ship pointer and call the ship function to reveal the cargo
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
|
|
return;
|
|
}
|
|
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
// this will take care of re-routing to all other clients
|
|
ship_do_cargo_revealed( &Ships[objp->instance], 1);
|
|
|
|
// server should rebroadcast
|
|
if(MULTIPLAYER_MASTER){
|
|
send_cargo_revealed_packet(&Ships[objp->instance]);
|
|
}
|
|
}
|
|
|
|
// packet to tell clients cargo of a ship was hidden to all
|
|
void send_cargo_hidden_packet( ship *shipp )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(CARGO_HIDDEN);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
|
|
// server sends to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// clients just send to the server
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a cargo hidden packet
|
|
void process_cargo_hidden_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
object *objp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(signature);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get a ship pointer and call the ship function to hide the cargo
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("Network", "Could not find object with net signature %d for cargo hidden\n", signature ));
|
|
return;
|
|
}
|
|
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
// this will take care of re-routing to all other clients
|
|
ship_do_cargo_hidden( &Ships[objp->instance], 1);
|
|
|
|
// server should rebroadcast
|
|
if(MULTIPLAYER_MASTER){
|
|
send_cargo_hidden_packet(&Ships[objp->instance]);
|
|
}
|
|
}
|
|
|
|
// defines used for secondary fire packet
|
|
#define SFPF_ALLOW_SWARM (1<<7)
|
|
#define SFPF_DUAL_FIRE (1<<6)
|
|
#define SFPF_TARGET_LOCKED (1<<5)
|
|
|
|
// send a packet indicating a secondary weapon was fired
|
|
void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int /*starting_count*/, int num_fired, int allow_swarm )
|
|
{
|
|
int packet_size, net_player_num;
|
|
ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
|
|
object *objp;
|
|
ushort target_net_signature;
|
|
int s_index;
|
|
ai_info *aip;
|
|
|
|
// Assert ( starting_count < UCHAR_MAX );
|
|
|
|
// get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
|
|
// we might send the info to the other player different than the one who fired
|
|
objp = &Objects[shipp->objnum];
|
|
if ( !(objp->flags[Object::Object_Flags::Player_ship]) ) {
|
|
if ( num_fired == 0 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
aip = &Ai_info[shipp->ai_index];
|
|
|
|
current_bank = (ubyte)shipp->weapons.current_secondary_bank;
|
|
Assert( (current_bank < MAX_SHIP_SECONDARY_BANKS) );
|
|
|
|
// build up the header portion
|
|
BUILD_HEADER( SECONDARY_FIRED_AI );
|
|
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
ADD_USHORT( starting_sig );
|
|
|
|
// add a couple of bits for swarm missiles and dual fire secondary weapons
|
|
sinfo = current_bank;
|
|
|
|
if ( allow_swarm ){
|
|
sinfo |= SFPF_ALLOW_SWARM;
|
|
}
|
|
|
|
if ( shipp->flags[Ship::Ship_Flags::Secondary_dual_fire] ){
|
|
sinfo |= SFPF_DUAL_FIRE;
|
|
}
|
|
|
|
if ( aip->current_target_is_locked ){
|
|
sinfo |= SFPF_TARGET_LOCKED;
|
|
}
|
|
|
|
ADD_DATA( sinfo );
|
|
|
|
// add the ship's target and any targeted subsystem
|
|
target_net_signature = 0;
|
|
s_index = -1;
|
|
if ( aip->target_objnum != -1) {
|
|
target_net_signature = Objects[aip->target_objnum].net_signature;
|
|
if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
|
|
s_index = ship_get_subsys_index( aip->targeted_subsys );
|
|
}
|
|
|
|
if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
|
|
Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]);
|
|
}
|
|
|
|
}
|
|
|
|
ADD_USHORT( target_net_signature );
|
|
ADD_SHORT( static_cast<short>(s_index) );
|
|
|
|
// just send this packet to everyone, then bail if an AI ship fired.
|
|
if ( !(objp->flags[Object::Object_Flags::Player_ship]) ) {
|
|
multi_io_send_to_all(data, packet_size);
|
|
return;
|
|
}
|
|
|
|
net_player_num = multi_find_player_by_object( objp );
|
|
|
|
if ( net_player_num < 0 ) {
|
|
// Pass to higher level code to handle
|
|
return;
|
|
}
|
|
|
|
// getting here means a player fired. Send the current packet to all players except the player
|
|
// who fired. If nothing got fired, then don't send to the other players -- we will just send
|
|
// a packet to the player who will find out that he didn't fire anything
|
|
if ( num_fired > 0 ) {
|
|
multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
|
|
}
|
|
|
|
// if I (the master) fired, then return
|
|
if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
|
|
return;
|
|
}
|
|
|
|
// if this is a rollback shot, the firing player does not need this packet.
|
|
if (multi_ship_record_get_rollback_wep_mode()) {
|
|
return;
|
|
}
|
|
|
|
// now build up the packet to send to the player who actually fired.
|
|
BUILD_HEADER( SECONDARY_FIRED_PLR );
|
|
ADD_USHORT(starting_sig);
|
|
ADD_DATA( sinfo );
|
|
|
|
// add the targeting information so that the player's weapons will always home on the correct
|
|
// ship
|
|
ADD_USHORT( target_net_signature );
|
|
ADD_SHORT( static_cast<short>(s_index) );
|
|
|
|
multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
|
|
}
|
|
|
|
/// process a packet indicating a secondary weapon was fired
|
|
void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
|
|
{
|
|
int offset, allow_swarm, target_objnum_save;
|
|
ushort net_signature, starting_sig, target_net_signature;
|
|
ubyte sinfo, current_bank;
|
|
object* objp, *target_objp;
|
|
ship *shipp;
|
|
short t_subsys;
|
|
ai_info *aip;
|
|
ship_subsys *targeted_subsys_save;
|
|
|
|
offset = HEADER_LENGTH; // size of the header
|
|
|
|
// if from_player is false, it means that the secondary weapon info in this packet was
|
|
// fired by an ai object (or another player). from_player == 1 means tha me (the person
|
|
// receiving this packet) fired the secondary weapon
|
|
if ( !from_player ) {
|
|
GET_USHORT( net_signature );
|
|
GET_USHORT( starting_sig );
|
|
GET_DATA( sinfo ); // includes flags and the secondary bank.
|
|
|
|
GET_USHORT( target_net_signature );
|
|
GET_SHORT( t_subsys );
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// find the object (based on network signatures) for the object that fired
|
|
objp = multi_get_network_object( net_signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("Network", "Could not find ship for fire secondary packet!\n"));
|
|
return;
|
|
}
|
|
|
|
// set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
|
|
// of the next fire time of this bank to 0 so we can fire right away
|
|
shipp = &Ships[objp->instance];
|
|
|
|
} else {
|
|
GET_USHORT( starting_sig );
|
|
GET_DATA( sinfo );
|
|
|
|
GET_USHORT( target_net_signature );
|
|
GET_SHORT( t_subsys );
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// get the object and ship
|
|
objp = Player_obj;
|
|
shipp = Player_ship;
|
|
}
|
|
|
|
// check the allow swarm bit
|
|
allow_swarm = 0;
|
|
if ( sinfo & SFPF_ALLOW_SWARM ){
|
|
allow_swarm = 1;
|
|
}
|
|
|
|
// set the dual fire properties of the ship
|
|
shipp->flags.set(Ship::Ship_Flags::Secondary_dual_fire, (sinfo & SFPF_DUAL_FIRE) != 0);
|
|
|
|
// determine whether current target is locked
|
|
Assert( shipp->ai_index != -1 );
|
|
aip = &Ai_info[shipp->ai_index];
|
|
if ( sinfo & SFPF_TARGET_LOCKED ) {
|
|
aip->current_target_is_locked = 1;
|
|
} else {
|
|
aip->current_target_is_locked = 0;
|
|
}
|
|
|
|
// find out the current bank
|
|
current_bank = (ubyte)(sinfo & 0x3);
|
|
Assert( (current_bank < MAX_SHIP_SECONDARY_BANKS) );
|
|
shipp->weapons.current_secondary_bank = current_bank;
|
|
|
|
// make it so we can fire this ship's secondary bank immediately!!!
|
|
shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
|
|
shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
|
|
|
|
// set this ship's target and subsystem information. We will save and restore target and
|
|
// targeted subsystem so that we do not accidentally change targets for this player or
|
|
// any AI ships on his system.
|
|
target_objnum_save = aip->target_objnum;
|
|
targeted_subsys_save = aip->targeted_subsys;
|
|
|
|
// reset these variables for accuracy. They will get reassigned at the end of this fuction
|
|
aip->target_objnum = -1;
|
|
aip->targeted_subsys = NULL;
|
|
|
|
target_objp = multi_get_network_object( target_net_signature );
|
|
if ( target_objp != NULL ) {
|
|
aip->target_objnum = OBJ_INDEX(target_objp);
|
|
|
|
if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
|
|
aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
|
|
}
|
|
}
|
|
|
|
if ( starting_sig != 0 ){
|
|
multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
|
|
} else {
|
|
shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
|
|
}
|
|
|
|
ship_fire_secondary( objp, allow_swarm );
|
|
|
|
// restore targeted object and targeted subsystem
|
|
aip->target_objnum = target_objnum_save;
|
|
aip->targeted_subsys = targeted_subsys_save;
|
|
}
|
|
|
|
// send a packet indicating a countermeasure was fired
|
|
void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
Int3();
|
|
|
|
Assert ( cmeasure_count < UCHAR_MAX );
|
|
BUILD_HEADER(COUNTERMEASURE_FIRED);
|
|
ADD_USHORT( objp->net_signature );
|
|
ADD_INT( rand_val );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
// process a packet indicating a countermeasure was fired
|
|
void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, rand_val;
|
|
ushort signature;
|
|
object *objp;
|
|
|
|
Int3();
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_USHORT( signature );
|
|
GET_INT( rand_val );
|
|
PACKET_SET_SIZE();
|
|
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
|
|
return;
|
|
}
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
// Assert ( objp->type == OBJ_SHIP );
|
|
|
|
// make it so ship can fire right away!
|
|
Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
|
|
if ( objp == Player_obj ){
|
|
nprintf(("network", "firing countermeasure from my ship\n"));
|
|
}
|
|
|
|
ship_launch_countermeasure( objp, rand_val );
|
|
}
|
|
|
|
// send a packet indicating that a turret has been fired
|
|
void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum, float dist_to_target, float target_radius )
|
|
{
|
|
int packet_size;
|
|
ushort pnet_signature;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
object *objp;
|
|
ubyte has_sig = 0;
|
|
ship_subsys *ssp;
|
|
|
|
// sanity
|
|
if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
|
|
return;
|
|
}
|
|
|
|
// local setup -- be sure we are actually passing a weapon!!!!
|
|
objp = &Objects[weapon_objnum];
|
|
Assert ( objp->type == OBJ_WEAPON );
|
|
if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
|
|
has_sig = 1;
|
|
}
|
|
|
|
pnet_signature = Objects[ship_objnum].net_signature;
|
|
|
|
ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index );
|
|
if(ssp == NULL){
|
|
return;
|
|
}
|
|
|
|
// build the fire turret packet.
|
|
BUILD_HEADER(FIRE_TURRET_WEAPON);
|
|
packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.vec.fvec);
|
|
ADD_DATA( has_sig );
|
|
ADD_USHORT( pnet_signature );
|
|
if(has_sig){
|
|
ADD_USHORT( objp->net_signature );
|
|
}
|
|
ADD_SHORT( static_cast<short>(Weapons[objp->instance].weapon_info_index) );
|
|
ADD_SHORT( static_cast<short>(subsys_index) );
|
|
|
|
// where we might accidentally dereference nullptrs, send 0 instead
|
|
constexpr float ZERO_VALUE = 0.0f;
|
|
|
|
if (ssp->submodel_instance_1 != nullptr) {
|
|
ADD_FLOAT( ssp->submodel_instance_1->cur_angle );
|
|
}
|
|
else {
|
|
ADD_FLOAT( ZERO_VALUE );
|
|
}
|
|
|
|
if (ssp->submodel_instance_2 != nullptr) {
|
|
ADD_FLOAT( ssp->submodel_instance_2->cur_angle );
|
|
}
|
|
else {
|
|
ADD_FLOAT(ZERO_VALUE);
|
|
}
|
|
|
|
ADD_FLOAT(dist_to_target);
|
|
|
|
ADD_FLOAT(target_radius);
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
|
|
multi_rate_add(1, "tur", packet_size);
|
|
}
|
|
|
|
// process a packet indicating a turret has been fired
|
|
void process_turret_fired_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, weapon_objnum;
|
|
ushort pnet_signature, wnet_signature;
|
|
vec3d pos, temp;
|
|
matrix orient;
|
|
vec3d o_fvec;
|
|
short turret_index, wid;
|
|
object *objp;
|
|
ship_subsys *ssp;
|
|
ubyte has_sig = 0;
|
|
ship *shipp;
|
|
float angle1, angle2;
|
|
float dist_to_target;
|
|
float target_radius;
|
|
|
|
// get the data for the turret fired packet
|
|
offset = HEADER_LENGTH;
|
|
offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
|
|
GET_DATA( has_sig );
|
|
GET_USHORT( pnet_signature );
|
|
if(has_sig){
|
|
GET_USHORT( wnet_signature );
|
|
} else {
|
|
wnet_signature = 0;
|
|
}
|
|
GET_SHORT( wid );
|
|
GET_SHORT( turret_index );
|
|
GET_FLOAT( angle1 );
|
|
GET_FLOAT( angle2 );
|
|
GET_FLOAT( dist_to_target );
|
|
GET_FLOAT( target_radius );
|
|
PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
|
|
|
|
// if we don't have a valid weapon index then bail
|
|
if ( (wid < 0) || (wid >= weapon_info_size()) ) {
|
|
return;
|
|
}
|
|
|
|
// find the object
|
|
objp = multi_get_network_object( pnet_signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
|
|
return;
|
|
}
|
|
|
|
// if this isn't a ship, do nothing
|
|
if ( objp->type != OBJ_SHIP ){
|
|
return;
|
|
}
|
|
|
|
// make an orientation matrix from the o_fvec
|
|
vm_vector_2_matrix_norm(&orient, &o_fvec, nullptr, nullptr);
|
|
|
|
// find this turret, and set the position of the turret that just fired to be where it fired. Quite a
|
|
// hack, but should be suitable.
|
|
shipp = &Ships[objp->instance];
|
|
ssp = ship_get_indexed_subsys( shipp, turret_index );
|
|
if(ssp == NULL){
|
|
return;
|
|
}
|
|
|
|
// bash the position and orientation of the turret
|
|
// but only if the submodels are not null
|
|
if (ssp->submodel_instance_1 != nullptr) {
|
|
ssp->submodel_instance_1->cur_angle = angle1;
|
|
}
|
|
|
|
if (ssp->submodel_instance_2 != nullptr) {
|
|
ssp->submodel_instance_2->cur_angle = angle2;
|
|
}
|
|
|
|
// get the world position of the weapon
|
|
ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
|
|
|
|
auto launch_curve_data = WeaponLaunchCurveData {
|
|
ssp->system_info->turret_num_firing_points,
|
|
dist_to_target,
|
|
target_radius,
|
|
};
|
|
|
|
// create the weapon object
|
|
if(wnet_signature != 0){
|
|
multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
|
|
}
|
|
|
|
weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), -1, true, false, 0.0f, ssp, launch_curve_data);
|
|
|
|
if (weapon_objnum != -1) {
|
|
if ( Weapon_info[wid].launch_snd.isValid() ) {
|
|
snd_play_3d( gamesnd_get_game_sound(Weapon_info[wid].launch_snd), &pos, &View_position );
|
|
}
|
|
}
|
|
}
|
|
|
|
// send a mission log item packet
|
|
void send_mission_log_packet( log_entry* entry )
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte type;
|
|
int sindex;
|
|
|
|
Assert ( MULTIPLAYER_MASTER );
|
|
|
|
// get the data from the log
|
|
type = (ubyte)entry->type; // do the type casting thing to save on packet space
|
|
sindex = entry->index;
|
|
|
|
BUILD_HEADER(MISSION_LOG_ENTRY);
|
|
ADD_DATA(type);
|
|
ADD_INT(entry->flags);
|
|
ADD_INT(sindex);
|
|
ADD_INT(entry->timestamp); // NOTE: this is a long so careful with swapping in 64-bit platforms - taylor
|
|
ADD_STRING(entry->pname);
|
|
ADD_STRING(entry->sname);
|
|
|
|
// broadcast the packet to all players
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a mission log item packet
|
|
void process_mission_log_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, flags;
|
|
int sindex;
|
|
ubyte type;
|
|
char pname[NAME_LENGTH], sname[NAME_LENGTH];
|
|
fix timestamp;
|
|
|
|
Assert ( MULTIPLAYER_CLIENT );
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_DATA(type);
|
|
GET_INT(flags);
|
|
GET_INT(sindex);
|
|
GET_INT(timestamp); // NOTE: this is a long so careful with swapping in 64-bit platforms - taylor
|
|
GET_STRING(pname);
|
|
GET_STRING(sname);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
mission_log_add_entry_multi( static_cast<LogType>(type), pname, sname, sindex, timestamp, flags );
|
|
}
|
|
|
|
// send a mission message packet
|
|
void send_mission_message_packet( int id, const char *who_from, int priority, int timing, int source, int builtin_type, int multi_target, int multi_team_filter, int delay, int event_num_to_cancel)
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE], up, us, utime;
|
|
|
|
Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
|
|
Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
|
|
Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
|
|
|
|
up = (ubyte) priority;
|
|
us = (ubyte) source;
|
|
utime = (ubyte)timing;
|
|
|
|
BUILD_HEADER(MISSION_MESSAGE);
|
|
ADD_INT(id);
|
|
ADD_STRING(who_from);
|
|
ADD_DATA(up);
|
|
ADD_DATA(utime);
|
|
ADD_DATA(us);
|
|
ADD_INT(builtin_type);
|
|
ADD_INT(multi_team_filter);
|
|
ADD_INT(delay);
|
|
ADD_INT(event_num_to_cancel);
|
|
|
|
if (multi_target == -1){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a mission message packet
|
|
void process_mission_message_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, id, builtin_type, delay = 0;
|
|
ubyte priority, source, utiming;
|
|
char who_from[NAME_LENGTH];
|
|
int multi_team_filter, event_num_to_cancel = -1;
|
|
|
|
Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_INT(id);
|
|
GET_STRING(who_from);
|
|
GET_DATA(priority);
|
|
GET_DATA(utiming);
|
|
GET_DATA(source);
|
|
GET_INT(builtin_type);
|
|
GET_INT(multi_team_filter);
|
|
GET_INT(delay);
|
|
GET_INT(event_num_to_cancel);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// filter out some builtin ones in TvT
|
|
if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)) {
|
|
mprintf(("Builtin message of type %d filtered out in process_mission_message_packet()\n", id));
|
|
return;
|
|
}
|
|
|
|
// maybe filter this out
|
|
if(!message_filter_multi(id)){
|
|
// send the message as if it came from an sexpression
|
|
message_queue_message( id, priority, utiming, who_from, source, 0, delay, builtin_type, event_num_to_cancel );
|
|
}
|
|
}
|
|
|
|
// just send them a pong back as fast as possible
|
|
void process_ping_packet(ubyte * /*data*/, header *hinfo)
|
|
{
|
|
net_addr addr;
|
|
int offset;
|
|
|
|
offset = HEADER_LENGTH;
|
|
PACKET_SET_SIZE();
|
|
|
|
// get the address to return the pong to
|
|
fill_net_addr(&addr, hinfo->addr, hinfo->port);
|
|
|
|
// send the pong
|
|
send_pong(&addr);
|
|
}
|
|
|
|
// right now it just routes the pong through to the standalone gui, which is the only
|
|
// system which uses ping and pong right now.
|
|
void process_pong_packet(ubyte * /*data*/, header *hinfo)
|
|
{
|
|
net_player *p;
|
|
net_addr addr;
|
|
int offset,lookup;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
fill_net_addr(&addr, hinfo->addr, hinfo->port);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// if we're connected , see who sent us this pong
|
|
if(Net_player->flags & NETINFO_FLAG_CONNECTED){
|
|
lookup = find_player_index(hinfo->id);
|
|
if(lookup == -1){
|
|
return;
|
|
}
|
|
|
|
p = &Net_players[lookup];
|
|
|
|
// evaluate the ping
|
|
multi_ping_eval_pong(&Net_players[lookup].s_info.ping, timer_get_fixed_seconds());
|
|
|
|
// put in calls to any functions which may want to know about the ping times from
|
|
// this guy
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
std_update_player_ping(p);
|
|
}
|
|
|
|
// mark his socket as still alive (extra precaution)
|
|
psnet_mark_received(Net_players[lookup].reliable_socket);
|
|
}
|
|
// otherwise, do any special processing
|
|
else {
|
|
// if we're in the join game state, see if this pong came from a server on our
|
|
// list
|
|
if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
|
|
multi_join_eval_pong(&addr, timer_get_fixed_seconds());
|
|
}
|
|
}
|
|
}
|
|
|
|
// send a ping packet
|
|
void send_ping(net_addr *addr)
|
|
{
|
|
unsigned char data[8];
|
|
int packet_size;
|
|
|
|
// build the header and send the packet
|
|
BUILD_HEADER( PING );
|
|
psnet_send(addr, &data[0], packet_size);
|
|
}
|
|
|
|
// send a pong packet
|
|
void send_pong(net_addr *addr)
|
|
{
|
|
unsigned char data[8];
|
|
int packet_size;
|
|
|
|
// build the header and send the packet
|
|
BUILD_HEADER(PONG);
|
|
psnet_send(addr, &data[0], packet_size);
|
|
}
|
|
|
|
// sent from host to master. give me the list of missions you have.
|
|
// this will be used only in a standalone mode
|
|
void send_mission_list_request( int /*what*/ )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
// build the header and ask for a list of missions or campaigns (depending
|
|
// on the 'what' flag).
|
|
BUILD_HEADER(MISSION_REQUEST);
|
|
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
// maximum number of bytes that we can send in a mission items packet.
|
|
#define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
|
|
|
|
// defines used to tell what type of packets are being sent
|
|
#define MISSION_LIST_ITEMS 1
|
|
#define CAMPAIGN_LIST_ITEMS 2
|
|
|
|
// send an individual mission file item
|
|
void send_mission_items( net_player *pl )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size, i;
|
|
ubyte stop, type;
|
|
|
|
// build the header
|
|
BUILD_HEADER(MISSION_ITEM);
|
|
|
|
// send the list of missions and campaigns avilable on the server. Stop when
|
|
// reaching a certain maximum
|
|
type = MISSION_LIST_ITEMS;
|
|
ADD_DATA( type );
|
|
for (i = 0; i < (int)Multi_create_mission_list.size(); i++ ) {
|
|
stop = 0;
|
|
ADD_DATA( stop );
|
|
|
|
ADD_STRING( Multi_create_mission_list[i].filename );
|
|
ADD_STRING( Multi_create_mission_list[i].name );
|
|
ADD_INT( Multi_create_mission_list[i].flags );
|
|
ADD_DATA( Multi_create_mission_list[i].max_players );
|
|
ADD_INT( Multi_create_mission_list[i].respawn );
|
|
|
|
// STANDALONE_ONLY
|
|
ADD_DATA( Multi_create_mission_list[i].valid_status );
|
|
|
|
if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
|
|
stop = 1;
|
|
ADD_DATA( stop );
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
BUILD_HEADER( MISSION_ITEM );
|
|
ADD_DATA( type );
|
|
}
|
|
}
|
|
stop = 1;
|
|
ADD_DATA(stop);
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
|
|
// send the campaign information
|
|
type = CAMPAIGN_LIST_ITEMS;
|
|
BUILD_HEADER(MISSION_ITEM);
|
|
ADD_DATA( type );
|
|
for (i = 0; i < (int)Multi_create_campaign_list.size(); i++ ) {
|
|
stop = 0;
|
|
ADD_DATA( stop );
|
|
|
|
ADD_STRING( Multi_create_campaign_list[i].filename );
|
|
ADD_STRING( Multi_create_campaign_list[i].name );
|
|
ADD_INT( Multi_create_campaign_list[i].flags );
|
|
ADD_DATA( Multi_create_campaign_list[i].max_players );
|
|
|
|
if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
|
|
stop = 1;
|
|
ADD_DATA( stop );
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
BUILD_HEADER( MISSION_ITEM );
|
|
ADD_DATA( type );
|
|
}
|
|
}
|
|
stop = 1;
|
|
ADD_DATA(stop);
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
}
|
|
|
|
// process a request for a list of missions
|
|
void process_mission_request_packet(ubyte * /*data*/, header *hinfo)
|
|
{
|
|
int player_num,offset;
|
|
|
|
offset = HEADER_LENGTH;
|
|
PACKET_SET_SIZE();
|
|
|
|
// fill in the address information of where this came from
|
|
player_num = find_player_index(hinfo->id);
|
|
if(player_num == -1){
|
|
nprintf(("Network","Could not find player to send mission list items to!\n"));
|
|
return;
|
|
}
|
|
|
|
send_mission_items( &Net_players[player_num] );
|
|
}
|
|
|
|
// process an individual mission file item
|
|
void process_mission_item_packet(ubyte *data,header *hinfo)
|
|
{
|
|
int offset, flags;
|
|
char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
|
|
ubyte stop, type,max_players;
|
|
uint respawn;
|
|
multi_create_info mcip;
|
|
|
|
Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_DATA( type );
|
|
GET_DATA(stop);
|
|
while( !stop ) {
|
|
GET_STRING( filename );
|
|
GET_STRING( name );
|
|
GET_INT( flags );
|
|
GET_DATA( max_players );
|
|
|
|
// missions also have respawns and a crc32 associated with them
|
|
if(type == MISSION_LIST_ITEMS){
|
|
GET_UINT(respawn);
|
|
|
|
// STANDALONE_ONLY
|
|
GET_DATA(valid_status);
|
|
|
|
strcpy_s(mcip.filename, filename );
|
|
strcpy_s(mcip.name, name );
|
|
mcip.flags = flags;
|
|
mcip.respawn = respawn;
|
|
mcip.max_players = max_players;
|
|
|
|
// STANDALONE_ONLY
|
|
mcip.valid_status = valid_status;
|
|
|
|
Multi_create_mission_list.push_back( mcip );
|
|
} else if ( type == CAMPAIGN_LIST_ITEMS ) {
|
|
strcpy_s(mcip.filename, filename );
|
|
strcpy_s(mcip.name, name );
|
|
mcip.flags = flags;
|
|
mcip.respawn = 0;
|
|
mcip.max_players = max_players;
|
|
|
|
Multi_create_campaign_list.push_back( mcip );
|
|
}
|
|
|
|
GET_DATA( stop );
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// this will cause whatever list to get resorted (although they should be appearing in order)
|
|
multi_create_setup_list_data(-1);
|
|
}
|
|
|
|
// send a request to the server to pause or unpause the game
|
|
void send_multi_pause_packet(int pause)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte val;
|
|
int packet_size = 0;
|
|
|
|
Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
|
|
// build the header
|
|
BUILD_HEADER(MULTI_PAUSE_REQUEST);
|
|
val = (ubyte) pause;
|
|
|
|
// add the pause info
|
|
ADD_DATA(val);
|
|
|
|
// send the request to the server
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
// process a pause update packet (pause, unpause, etc)
|
|
void process_multi_pause_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
ubyte val;
|
|
int player_index;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the data
|
|
GET_DATA(val);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get who sent the packet
|
|
player_index = find_player_index(hinfo->id);
|
|
// if we don't know who sent the packet, don't do anything
|
|
if(player_index == -1){
|
|
return;
|
|
}
|
|
|
|
// if we're the server, we should evaluate whether this guy is allowed to send the packet
|
|
multi_pause_server_eval_request(&Net_players[player_index],(int)val);
|
|
}
|
|
|
|
// send a game information update
|
|
void send_game_info_packet()
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE], paused;
|
|
|
|
// set the paused variable
|
|
paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
|
|
|
|
BUILD_HEADER(GAME_INFO);
|
|
ADD_INT( Missiontime ); // NOTE: this is a long so careful with swapping in 64-bit platforms - taylor
|
|
ADD_DATA( paused );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
// process a game information update
|
|
void process_game_info_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
fix mission_time;
|
|
ubyte paused;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the mission time -- we should examine our time and the time from the server. If off by some delta
|
|
// time, set our time to server time (should take ping time into account!!!)
|
|
GET_INT( mission_time ); // NOTE: this is a long so careful with swapping in 64-bit platforms - taylor
|
|
GET_DATA( paused );
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
// send an ingame nak packet
|
|
void send_ingame_nak(int state, net_player *p)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
packet_size = 0;
|
|
BUILD_HEADER(INGAME_NAK);
|
|
|
|
ADD_INT(state);
|
|
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
|
|
// process an ingame nak packet
|
|
void process_ingame_nak(ubyte *data, header *hinfo)
|
|
{
|
|
int offset,state,pid;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_INT(state);
|
|
PACKET_SET_SIZE();
|
|
|
|
pid = find_player_index(hinfo->id);
|
|
if(pid < 0){
|
|
return;
|
|
}
|
|
|
|
switch(state){
|
|
case ACK_FILE_ACCEPTED :
|
|
Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
|
|
nprintf(("Network","Mission file rejected by server, aborting...\n"));
|
|
multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the end_mission SEXP has been used tell clients to skip straight to the debrief screen
|
|
void send_force_end_mission_packet()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
packet_size = 0;
|
|
BUILD_HEADER(FORCE_MISSION_END);
|
|
|
|
if (Net_player->flags & NETINFO_FLAG_AM_MASTER)
|
|
{
|
|
// tell everyone to leave the game
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a packet indicating that we should jump straight to the debrief screen
|
|
void process_force_end_mission_packet(ubyte * /*data*/, header *hinfo)
|
|
{
|
|
int offset;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
ml_string("Receiving force end mission packet");
|
|
|
|
// Since only the server sends out these packets it should never receive one
|
|
Assert (!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
|
|
multi_handle_sudden_mission_end();
|
|
send_debrief_event();
|
|
}
|
|
|
|
// send a packet telling players to end the mission
|
|
void send_endgame_packet(net_player *pl)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
packet_size = 0;
|
|
BUILD_HEADER(MISSION_END);
|
|
|
|
// sending to a specific player?
|
|
if(pl != NULL){
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
return;
|
|
}
|
|
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// send all player stats here
|
|
multi_broadcast_stats(STATS_MISSION);
|
|
|
|
// if in dogfight mode, send all dogfight stats as well
|
|
ml_string("Before dogfight stats!");
|
|
if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
|
|
ml_string("Sending dogfight stats!");
|
|
|
|
multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
|
|
}
|
|
ml_string("After dogfight stats!");
|
|
|
|
// tell everyone to leave the game
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a packet indicating we should end the current mission
|
|
void process_endgame_packet(ubyte * /*data*/, header *hinfo)
|
|
{
|
|
int offset;
|
|
int player_num;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
ml_string("Receiving endgame packet");
|
|
|
|
// if I'm the server, I should evaluate whether the sender is authorized to end the game
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// determine who this came from and make sure he is allowed to end the game
|
|
player_num = find_player_index(hinfo->id);
|
|
Assert(player_num != -1);
|
|
if(player_num < 0){
|
|
return;
|
|
}
|
|
|
|
// if the player is allowed to end the mission
|
|
if(!multi_can_end_mission(&Net_players[player_num])){
|
|
return;
|
|
}
|
|
|
|
// act as if we hit alt+j locally
|
|
multi_handle_end_mission_request();
|
|
}
|
|
// all clients process immediately
|
|
else {
|
|
// ingame joiners should quit when they receive an endgame packet since the game is over
|
|
if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
|
|
multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
|
|
return;
|
|
}
|
|
|
|
// do any special processing for being in a state other than the gameplay state
|
|
multi_handle_state_special();
|
|
|
|
// make sure we're not already in the debrief state
|
|
if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
|
|
multi_warpout_all_players();
|
|
}
|
|
}
|
|
}
|
|
|
|
// send a position/orientation update for myself (if I'm an observer)
|
|
void send_observer_update_packet()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
int ret;
|
|
ushort target_sig;
|
|
|
|
// its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
|
|
// to send any update packets to anyone.
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
return;
|
|
}
|
|
|
|
if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
|
|
return;
|
|
}
|
|
|
|
packet_size = 0;
|
|
|
|
BUILD_HEADER(OBSERVER_UPDATE);
|
|
|
|
ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
|
|
packet_size += ret;
|
|
|
|
angles temp_angles;
|
|
|
|
vm_extract_angles_matrix_alternate(&temp_angles, &Player_obj->orient);
|
|
ret = multi_pack_unpack_orient( 1, data + packet_size, &temp_angles);
|
|
|
|
packet_size += ret;
|
|
|
|
// add targeting infomation
|
|
if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
|
|
target_sig = Objects[Player_ai->target_objnum].net_signature;
|
|
} else {
|
|
target_sig = 0;
|
|
}
|
|
ADD_USHORT(target_sig);
|
|
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
|
|
// process a position/orientation update from an observer
|
|
void process_observer_update_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset,ret;
|
|
int obs_num;
|
|
vec3d g_vec;
|
|
angles temp_angles;
|
|
matrix g_mat;
|
|
physics_info bogus_pi;
|
|
ushort target_sig;
|
|
object *target_obj;
|
|
offset = HEADER_LENGTH;
|
|
|
|
obs_num = find_player_index(hinfo->id);
|
|
|
|
memset(&bogus_pi,0,sizeof(physics_info));
|
|
ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
|
|
offset += ret;
|
|
|
|
ret = multi_pack_unpack_orient( 0, data + offset, &temp_angles );
|
|
vm_angles_2_matrix(&g_mat, &temp_angles);
|
|
offset += ret;
|
|
|
|
// targeting information
|
|
GET_USHORT(target_sig);
|
|
PACKET_SET_SIZE();
|
|
|
|
if((obs_num < 0) || (Net_players[obs_num].m_player->objnum < 0)){
|
|
return;
|
|
}
|
|
|
|
// set targeting info
|
|
if(target_sig == 0){
|
|
Net_players[obs_num].s_info.target_objnum = -1;
|
|
} else {
|
|
target_obj = multi_get_network_object(target_sig);
|
|
Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
|
|
}
|
|
|
|
Objects[Net_players[obs_num].m_player->objnum].pos = g_vec;
|
|
Objects[Net_players[obs_num].m_player->objnum].orient = g_mat;
|
|
Net_players[obs_num].s_info.eye_pos = g_vec;
|
|
Net_players[obs_num].s_info.eye_orient = g_mat;
|
|
}
|
|
|
|
void send_netplayer_slot_packet()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size,idx;
|
|
ubyte stop;
|
|
char val;
|
|
|
|
packet_size = 0;
|
|
stop = 0xff;
|
|
BUILD_HEADER(NETPLAYER_SLOTS_P);
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
|
|
ADD_DATA(stop);
|
|
ADD_SHORT(Net_players[idx].player_id);
|
|
ADD_USHORT(Objects[Net_players[idx].m_player->objnum].net_signature);
|
|
ADD_SHORT(static_cast<short>(Net_players[idx].p_info.ship_class));
|
|
val = static_cast<char>(Net_players[idx].p_info.ship_index);
|
|
ADD_DATA(val);
|
|
}
|
|
}
|
|
stop = 0x0;
|
|
ADD_DATA(stop);
|
|
|
|
// standalone case or not
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_netplayer_slot_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
int player_num;
|
|
char ship_index;
|
|
ushort net_sig;
|
|
object *objp;
|
|
ubyte stop;
|
|
short player_id, ship_class;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// first untag all of the player ships and make them OF_COULD_BE_PLAYER
|
|
multi_untag_player_ships();
|
|
|
|
GET_DATA(stop);
|
|
while(stop != 0x0){
|
|
GET_SHORT(player_id);
|
|
GET_USHORT(net_sig);
|
|
GET_SHORT(ship_class);
|
|
GET_DATA(ship_index);
|
|
player_num = find_player_index(player_id);
|
|
if(player_num < 0){
|
|
nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
|
|
} else {
|
|
// call the function in multiutil.cpp to set up the player object stuff
|
|
// being careful not to muck with the standalone object
|
|
if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
|
|
objp = multi_get_network_object(net_sig);
|
|
if (objp == NULL) {
|
|
Error(LOCATION, "Could not retrieve net object for signature %d!\n", net_sig);
|
|
}
|
|
multi_assign_player_ship( player_num, objp, ship_class );
|
|
Net_players[player_num].p_info.ship_index = ship_index;
|
|
objp->flags.remove(Object::Object_Flags::Could_be_player);
|
|
objp->flags.set(Object::Object_Flags::Player_ship);
|
|
}
|
|
}
|
|
GET_DATA(stop);
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// standalone should forward the packet and wait for a response
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
send_netplayer_slot_packet();
|
|
}
|
|
|
|
Net_player->state = NETPLAYER_STATE_SLOT_ACK;
|
|
send_netplayer_update_packet();
|
|
}
|
|
|
|
// two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
|
|
// if this change is a primary or secondary change. new_bank is the new current primary/secondary
|
|
// bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
|
|
void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], utmp;
|
|
int packet_size;
|
|
|
|
BUILD_HEADER(SHIP_WSTATE_CHANGE);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
utmp = (ubyte)(what);
|
|
ADD_DATA( utmp );
|
|
utmp = (ubyte)(new_bank);
|
|
ADD_DATA( utmp );
|
|
utmp = (ubyte)(link_status);
|
|
ADD_DATA( utmp );
|
|
|
|
// Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_ship_weapon_change( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
ubyte what, new_bank, link_status;
|
|
object *objp;
|
|
ship *shipp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT( signature );
|
|
GET_DATA( what );
|
|
GET_DATA( new_bank );
|
|
GET_DATA( link_status );
|
|
PACKET_SET_SIZE();
|
|
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
|
|
return;
|
|
}
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
|
|
// if this is my data, do nothing since I already have my own data
|
|
if ( objp == Player_obj ){
|
|
return;
|
|
}
|
|
|
|
// now, get the ship and set the new bank and link modes based on the 'what' value
|
|
shipp = &Ships[objp->instance];
|
|
if ( what == MULTI_PRIMARY_CHANGED ) {
|
|
shipp->weapons.current_primary_bank = new_bank;
|
|
shipp->flags.set(Ship::Ship_Flags::Primary_linked, link_status != 0);
|
|
} else {
|
|
shipp->weapons.current_secondary_bank = new_bank;
|
|
shipp->flags.set(Ship::Ship_Flags::Secondary_dual_fire, link_status != 0);
|
|
}
|
|
}
|
|
|
|
// ship status change procedure
|
|
// 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
|
|
// server critical.
|
|
// 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
|
|
// for added redundancy.
|
|
// 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
|
|
// ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
|
|
// 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
|
|
// back to the client. Also sends multiple times for redundancy
|
|
// 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
|
|
// and remove the item from the list. If no match is found it means that either he has received an ack, has acted
|
|
// on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
|
|
|
|
#define SHIP_STATUS_REPEAT 2
|
|
void send_ship_status_packet(net_player *pl, button_info *bi, int id)
|
|
{
|
|
int idx, temp;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
uint8_t num_fields = 0;
|
|
|
|
if(pl == NULL){
|
|
return;
|
|
}
|
|
|
|
BUILD_HEADER(SHIP_STATUS_CHANGE);
|
|
ADD_INT(id);
|
|
|
|
for (num_fields = NUM_BUTTON_FIELDS; num_fields > 0; num_fields--) {
|
|
if (bi->status[num_fields-1]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ADD_DATA(num_fields);
|
|
|
|
for (idx = 0; idx < num_fields; idx++) {
|
|
temp = bi->status[idx];
|
|
ADD_INT(temp);
|
|
}
|
|
|
|
// server should send reliably (response packet)
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
} else {
|
|
multi_io_send(pl, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_ship_status_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int idx;
|
|
int offset;
|
|
int player_num,unique_id;
|
|
button_info bi;
|
|
int i_tmp;
|
|
uint8_t num_fields = 0;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// zero out the button info structure for good measure
|
|
memset(&bi,0,sizeof(button_info));
|
|
|
|
// read the button-info
|
|
GET_INT(unique_id);
|
|
|
|
GET_DATA(num_fields);
|
|
|
|
for (idx = 0; idx < num_fields; idx++) {
|
|
GET_INT(i_tmp);
|
|
|
|
if (idx < NUM_BUTTON_FIELDS) {
|
|
bi.status[idx] = i_tmp;
|
|
}
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// this will be handled differently client and server side. Duh.
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
|
|
// find which net-player has sent us butotn information
|
|
player_num = find_player_index(hinfo->id);
|
|
Assert(player_num >= 0);
|
|
if(player_num < 0){
|
|
return;
|
|
}
|
|
|
|
// don't process critical button information for observers
|
|
// its a new button_info for this guy. apply and ack
|
|
if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
|
|
// mark that he's pressed this button
|
|
// add_net_button_info(&Net_players[player_num], &bi, unique_id);
|
|
|
|
// send a return packet
|
|
send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
|
|
|
|
// apply the button presses to his ship as normal
|
|
multi_apply_ship_status(&Net_players[player_num], &bi, 0);
|
|
}
|
|
// else ignore it as a repeat from the same guy
|
|
} else { // CLIENT SIDE
|
|
// this is the return from the server, so we should now apply them locally
|
|
// if(lookup_ship_status(Net_player,unique_id,1)){
|
|
multi_apply_ship_status(Net_player, &bi, 1);
|
|
// }
|
|
}
|
|
}
|
|
|
|
// MWA 4/28/9 -- redid this function since message all fighers was really broken
|
|
// for clients. Left all details to this function instead of higher level messaging
|
|
// code
|
|
void send_player_order_packet(int type, int index, int cmd)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte val;
|
|
ushort target_net_signature;
|
|
int s_index = -1;
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(PLAYER_ORDER_PACKET);
|
|
|
|
val = (ubyte)type;
|
|
ADD_DATA(val); // ship order or wing order, or message all fighters
|
|
|
|
// if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
|
|
if ( val != SQUAD_MSG_ALL ){
|
|
ADD_INT(index); // net signature of target ship
|
|
}
|
|
|
|
ADD_INT(cmd); // the command itself
|
|
|
|
// add target data.
|
|
target_net_signature = 0;
|
|
if ( Player_ai->target_objnum != -1 ){
|
|
target_net_signature = Objects[Player_ai->target_objnum].net_signature;
|
|
}
|
|
|
|
ADD_USHORT( target_net_signature );
|
|
|
|
if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
|
|
s_index = ship_get_subsys_index( Player_ai->targeted_subsys );
|
|
}
|
|
|
|
ADD_SHORT(static_cast<short>(s_index));
|
|
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
// brief explanation :
|
|
// in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
|
|
// functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
|
|
// (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
|
|
// Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
|
|
void process_player_order_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset, player_num, command, index = 0, tobjnum_save;
|
|
ushort target_net_signature;
|
|
char type;
|
|
short t_subsys;
|
|
object *objp, *target_objp;
|
|
ai_info *aip;
|
|
ship *shipp;
|
|
ship_subsys *tsubsys_save, *targeted_subsys;
|
|
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
// packet values - its easier to read all of these in first
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_DATA( type );
|
|
if ( type != SQUAD_MSG_ALL ){
|
|
GET_INT( index );
|
|
}
|
|
|
|
GET_INT( command );
|
|
GET_USHORT( target_net_signature );
|
|
GET_SHORT( t_subsys );
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
player_num = find_player_index(hinfo->id);
|
|
if(player_num == -1){
|
|
nprintf(("Network","Received player order packet from unknown player\n"));
|
|
return;
|
|
}
|
|
|
|
objp = &Objects[Net_players[player_num].m_player->objnum];
|
|
if ( objp->type != OBJ_SHIP ) {
|
|
nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
|
|
return;
|
|
}
|
|
|
|
// HACK HACK HACK HACK HACK HACK
|
|
// if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
|
|
// the data is BOGUS. All people should be able to to these things as well.
|
|
if(command == REARM_REPAIR_ME_ITEM){
|
|
hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].m_player->objnum]);
|
|
return;
|
|
} else if(command == ABORT_REARM_REPAIR_ITEM){
|
|
hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].m_player->objnum]);
|
|
return;
|
|
}
|
|
|
|
// if this player is not allowed to do messaging, quit here
|
|
if( !multi_can_message(&Net_players[player_num]) ){
|
|
nprintf(("Network","Received player order packet from player not allowed to give orders!!\n"));
|
|
return;
|
|
}
|
|
|
|
// check to see if the type of order is a reinforcement call. If so, intercept it, and
|
|
// then call them in.
|
|
if ( type == SQUAD_MSG_REINFORCEMENT ) {
|
|
Assert( (index >= 0) && (index < Num_reinforcements) );
|
|
hud_squadmsg_call_reinforcement(index, player_num);
|
|
return;
|
|
}
|
|
|
|
// set the player's ai information here
|
|
shipp = &Ships[objp->instance];
|
|
aip = &Ai_info[shipp->ai_index];
|
|
|
|
// get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
|
|
target_objp = multi_get_network_object( target_net_signature );
|
|
if ( target_objp == NULL ) {
|
|
return;
|
|
}
|
|
|
|
targeted_subsys = NULL;
|
|
if ( t_subsys != -1 ) {
|
|
Assert( target_objp != NULL );
|
|
targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
|
|
}
|
|
|
|
// save and restore the target objnum and targeted subsystem so that we don't mess up other things
|
|
// here
|
|
tobjnum_save = aip->target_objnum;
|
|
tsubsys_save = aip->targeted_subsys;
|
|
|
|
if ( target_objp ) {
|
|
aip->target_objnum = OBJ_INDEX(target_objp);
|
|
} else {
|
|
aip->target_objnum = -1;
|
|
}
|
|
|
|
aip->targeted_subsys = targeted_subsys;
|
|
|
|
if ( type == SQUAD_MSG_SHIP ) {
|
|
hud_squadmsg_send_ship_command(index, command, 1, SQUADMSG_HISTORY_ADD_ENTRY, player_num);
|
|
} else if ( type == SQUAD_MSG_WING ) {
|
|
hud_squadmsg_send_wing_command(index, command, 1, SQUADMSG_HISTORY_ADD_ENTRY, player_num);
|
|
} else if ( type == SQUAD_MSG_ALL ) {
|
|
hud_squadmsg_send_to_all_fighters( command, player_num );
|
|
}
|
|
|
|
Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
|
|
aip->target_objnum = tobjnum_save;
|
|
aip->targeted_subsys = tsubsys_save;
|
|
}
|
|
|
|
// FILE SIGNATURE stuff :
|
|
// there are 2 cases for file signature sending which are handled very differently
|
|
// 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
|
|
// posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
|
|
// 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
|
|
// the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
|
|
// would have to keep track of up to potentially 14 other ack handles (ouch).
|
|
void send_file_sig_packet(ushort sum_sig,int length_sig)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(FILE_SIG_INFO);
|
|
ADD_USHORT(sum_sig);
|
|
ADD_INT(length_sig);
|
|
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
void process_file_sig_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
int length_sig;
|
|
ushort sum_sig;
|
|
offset = HEADER_LENGTH;
|
|
|
|
// should only be received on the server-side
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
|
|
GET_USHORT(sum_sig);
|
|
GET_INT(length_sig);
|
|
PACKET_SET_SIZE();
|
|
server_verify_filesig(hinfo->id, sum_sig, length_sig);
|
|
}
|
|
|
|
void send_file_sig_request(char *file_name)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(FILE_SIG_REQUEST);
|
|
ADD_STRING(file_name);
|
|
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_file_sig_request(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
|
|
// get the mission name
|
|
GET_STRING(Netgame.mission_name);
|
|
PACKET_SET_SIZE();
|
|
|
|
// set the current mission filename
|
|
strcpy_s(Game_current_mission_filename,Netgame.mission_name);
|
|
|
|
// get the checksum
|
|
multi_get_mission_checksum(Game_current_mission_filename);
|
|
|
|
if(!multi_endgame_ending()){
|
|
// reply to the server
|
|
send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
|
|
}
|
|
}
|
|
|
|
// functions to deal with subsystems getting whacked
|
|
void send_subsystem_destroyed_packet( ship *shipp, int index, vec3d world_hitpos )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
ubyte uindex;
|
|
vec3d tmp, local_hitpos;
|
|
object *objp;
|
|
|
|
Assert ( index < UCHAR_MAX );
|
|
uindex = (ubyte)(index);
|
|
|
|
objp = &Objects[shipp->objnum];
|
|
|
|
vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
|
|
vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
|
|
|
|
BUILD_HEADER(SUBSYSTEM_DESTROYED);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
ADD_DATA( uindex );
|
|
ADD_VECTOR( local_hitpos );
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
ubyte uindex;
|
|
object *objp;
|
|
vec3d local_hit_pos, world_hit_pos;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_USHORT( signature );
|
|
GET_DATA( uindex );
|
|
GET_VECTOR( local_hit_pos );
|
|
|
|
// get the network object. process it if we find it.
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp != NULL ) {
|
|
ship *shipp;
|
|
ship_subsys *subsysp;
|
|
|
|
// be sure we have a ship!!!
|
|
// Assert ( objp->type == OBJ_SHIP );
|
|
if(objp->type != OBJ_SHIP){
|
|
PACKET_SET_SIZE();
|
|
return;
|
|
}
|
|
|
|
shipp = &Ships[objp->instance];
|
|
|
|
// call to get the pointer to the subsystem we should be working on
|
|
subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
|
|
if (subsysp == NULL) {
|
|
nprintf(("Network", "Could not find subsys %d for ship %s to process as being destroyed\n", (int)uindex, shipp->ship_name ));
|
|
PACKET_SET_SIZE();
|
|
return;
|
|
}
|
|
|
|
// Cyborg17 - We need to do this here because otherwise FSO will not mark subsystem strength as zero (It used to rely on the object Update packet)
|
|
subsysp->current_hits = 0;
|
|
|
|
vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
|
|
vm_vec_add2( &world_hit_pos, &objp->pos );
|
|
|
|
do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
|
|
if ( objp == Player_obj ) {
|
|
hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
|
|
}
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
|
|
// packet to tell clients cargo of a ship was revealed to all
|
|
void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], uindex;
|
|
int packet_size;
|
|
|
|
Assert ( index < UCHAR_MAX );
|
|
uindex = (ubyte)(index);
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(SUBSYS_CARGO_REVEALED);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
ADD_DATA( uindex );
|
|
|
|
// server sends to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// clients just send to the server
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a subsystem cargo revealed packet
|
|
void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
ubyte uindex;
|
|
object *objp;
|
|
ship *shipp;
|
|
ship_subsys *subsysp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT( signature );
|
|
GET_DATA( uindex );
|
|
PACKET_SET_SIZE();
|
|
|
|
// get a ship pointer and call the ship function to reveal the cargo
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
|
|
return;
|
|
}
|
|
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
shipp = &Ships[objp->instance];
|
|
|
|
// call to get the pointer to the subsystem we should be working on
|
|
subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
|
|
if (subsysp == NULL) {
|
|
nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
|
|
return;
|
|
}
|
|
|
|
// this will take care of re-routing to all other clients
|
|
ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
|
|
|
|
// server should rebroadcast
|
|
if(MULTIPLAYER_MASTER){
|
|
send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
|
|
}
|
|
}
|
|
|
|
// packet to tell clients cargo of a ship was hidden to all
|
|
void send_subsystem_cargo_hidden_packet( ship *shipp, int index )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], uindex;
|
|
int packet_size;
|
|
|
|
Assert ( index < UCHAR_MAX );
|
|
uindex = (ubyte)(index);
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(SUBSYS_CARGO_HIDDEN);
|
|
ADD_USHORT( Objects[shipp->objnum].net_signature );
|
|
ADD_DATA( uindex );
|
|
|
|
// server sends to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// clients just send to the server
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
// process a subsystem cargo hidden packet
|
|
void process_subsystem_cargo_hidden_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
ubyte uindex;
|
|
object *objp;
|
|
ship *shipp;
|
|
ship_subsys *subsysp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT( signature );
|
|
GET_DATA( uindex );
|
|
PACKET_SET_SIZE();
|
|
|
|
// get a ship pointer and call the ship function to reveal the cargo
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("Network", "Could not find object with net signature %d for cargo hidden\n", signature ));
|
|
return;
|
|
}
|
|
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
shipp = &Ships[objp->instance];
|
|
|
|
// call to get the pointer to the subsystem we should be working on
|
|
subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
|
|
if (subsysp == NULL) {
|
|
nprintf(("Network", "Could not find subsys for ship %s for cargo hidden\n", Ships[objp->instance].ship_name ));
|
|
return;
|
|
}
|
|
|
|
// this will take care of re-routing to all other clients
|
|
ship_do_cap_subsys_cargo_hidden( shipp, subsysp, 1 );
|
|
|
|
// server should rebroadcast
|
|
if(MULTIPLAYER_MASTER){
|
|
send_subsystem_cargo_hidden_packet(&Ships[objp->instance], (int)uindex);
|
|
}
|
|
}
|
|
|
|
void send_netplayer_load_packet(net_player *pl)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(LOAD_MISSION_NOW);
|
|
ADD_STRING(Netgame.mission_name);
|
|
|
|
if(pl == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_netplayer_load_packet(ubyte *data, header *hinfo)
|
|
{
|
|
char str[100];
|
|
int offset = HEADER_LENGTH;
|
|
|
|
GET_STRING(str);
|
|
PACKET_SET_SIZE();
|
|
|
|
strcpy_s(Netgame.mission_name,str);
|
|
strcpy_s(Game_current_mission_filename,str);
|
|
if(!Multi_mission_loaded){
|
|
|
|
// MWA 2/3/98 -- ingame join changes!!!
|
|
// everyone can go through the same mission loading path here!!!!
|
|
nprintf(("Network","Loading mission...\n"));
|
|
|
|
// notify everyone that I'm loading the mission
|
|
Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
|
|
send_netplayer_update_packet();
|
|
|
|
// do the load itself
|
|
game_start_mission();
|
|
|
|
// ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
|
|
// code will remark the correct player ships
|
|
if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
|
|
multi_untag_player_ships();
|
|
}
|
|
|
|
Net_player->flags |= NETINFO_FLAG_MISSION_OK;
|
|
Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
|
|
send_netplayer_update_packet();
|
|
|
|
Multi_mission_loaded = 1;
|
|
nprintf(("Network","Finished loading mission\n"));
|
|
}
|
|
}
|
|
|
|
void send_jump_into_mission_packet(net_player *pl)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
|
|
BUILD_HEADER(JUMP_INTO_GAME);
|
|
|
|
// ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
|
|
// and possible other things.
|
|
if ( pl != NULL ) {
|
|
if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
|
|
ADD_INT(Netgame.game_state);
|
|
}
|
|
}
|
|
|
|
// broadcast
|
|
if(pl == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// send to a specific player
|
|
else {
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_jump_into_mission_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
int state;
|
|
|
|
state = 0;
|
|
|
|
// if I am ingame joining, there should be extra data. For now, this data is the netgame state.
|
|
// the game could be paused, so ingame joiner needs to deal with it.
|
|
if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
|
|
GET_INT( state );
|
|
Netgame.game_state = state;
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// handle any special processing for being in a weird substate
|
|
multi_handle_state_special();
|
|
|
|
// if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
|
|
if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
|
|
if(Net_player->flags & NETINFO_FLAG_OBSERVER){
|
|
multi_ingame_observer_finish();
|
|
} else {
|
|
gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
|
|
Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
|
|
send_netplayer_update_packet();
|
|
}
|
|
} else {
|
|
// start the mission!!
|
|
if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
|
|
Netgame.game_state = NETGAME_STATE_IN_MISSION;
|
|
gameseq_post_event(GS_EVENT_ENTER_GAME);
|
|
Net_player->state = NETPLAYER_STATE_IN_MISSION;
|
|
send_netplayer_update_packet();
|
|
}
|
|
}
|
|
|
|
// recalc all object pairs now
|
|
extern void obj_reset_all_collisions();
|
|
obj_reset_all_collisions();
|
|
|
|
// display some cool text
|
|
multi_common_add_text(XSTR("Received mission start\n",720),1);
|
|
|
|
// NETLOG
|
|
ml_string(NOX("Client received mission start from server - entering mission"));
|
|
}
|
|
|
|
//XSTR:OFF
|
|
|
|
const char *repair_text[] = {
|
|
"unknown",
|
|
"REPAIR_INFO_BEGIN",
|
|
"REPAIR_INFO_END",
|
|
"REPAIR_INFO_UPDATE",
|
|
"REPAIR_INFO_QUEUE",
|
|
"REPAIR_INFO_ABORT",
|
|
"REPAIR_INFO_BROKEN",
|
|
"REPAIR_INFO_WARP_ADD",
|
|
"REPAIR_INFO_WARP_REMOVE",
|
|
"REPAIR_INFO_ONWAY",
|
|
"REPAIR_INFO_KILLED",
|
|
"REPAIR_INFO_COMPLETE",
|
|
};
|
|
|
|
//XSTR:ON
|
|
|
|
// the following two routines deal with updating and sending information regarding players
|
|
// rearming and repairing during the game. The process function calls the routines to deal with
|
|
// setting flags and other interesting things.
|
|
void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
|
|
{
|
|
int packet_size = 0;
|
|
ushort repaired_signature, repair_signature;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte cd;
|
|
|
|
// use the network signature of the destination object if there is one, -1 otherwise.
|
|
// client will piece it all together
|
|
repaired_signature = repaired_objp->net_signature;
|
|
|
|
// the repair ship may be NULL here since it might have been destroyed
|
|
repair_signature = 0;
|
|
if ( repair_objp ){
|
|
repair_signature = repair_objp->net_signature;
|
|
}
|
|
|
|
BUILD_HEADER(CLIENT_REPAIR_INFO);
|
|
cd = (ubyte)code;
|
|
ADD_DATA(cd);
|
|
ADD_USHORT( repaired_signature );
|
|
ADD_USHORT( repair_signature );
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
|
|
nprintf(("Network", "Repair: %s sent to all players (%s/%s)\n", repair_text[cd], Ships[repaired_objp->instance].ship_name, (repair_objp==NULL)?"<none>":Ships[repair_objp->instance].ship_name));
|
|
}
|
|
|
|
void process_repair_info_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ushort repaired_signature, repair_signature;
|
|
object *repaired_objp, *repair_objp;
|
|
ubyte code;
|
|
|
|
GET_DATA(code);
|
|
GET_USHORT( repaired_signature );
|
|
GET_USHORT( repair_signature );
|
|
PACKET_SET_SIZE();
|
|
|
|
repaired_objp = multi_get_network_object( repaired_signature );
|
|
repair_objp = multi_get_network_object( repair_signature );
|
|
|
|
nprintf(("Network", "Repair: %s received (%s/%s)\n", repair_text[code], (repaired_objp==NULL)?"<None>":Ships[repaired_objp->instance].ship_name, (repair_objp==NULL)?"<None>":Ships[repair_objp->instance].ship_name));
|
|
|
|
if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
|
|
return;
|
|
}
|
|
|
|
if ( repaired_objp == NULL ) {
|
|
Int3(); // Sandeep says this is bad bad bad. No ship to repair.
|
|
return;
|
|
}
|
|
|
|
// the hope is to simply call the routine in the ai code to set/unset flags
|
|
// based on the code value and everything else should happen..I hope....
|
|
if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
|
|
|
|
ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
|
|
|
|
// set the dock flags when repair begins. Prevents problem in lagging docking
|
|
// packet. Also set any other flags/modes which need to be set to prevent Asserts.
|
|
// bleah.
|
|
if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
|
|
// Karajorma removed this in revision 4808 to fix bug 1088. Problem is, if
|
|
// this was originally intended to prevent docking problems, will they return?
|
|
/*
|
|
// find indexes from goal
|
|
ai_info *aip = &Ai_info[Ships[repair_objp->instance].ai_index];
|
|
Assert(aip->active_goal >= 0);
|
|
ai_goal *aigp = &aip->goals[aip->active_goal];
|
|
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
|
|
|
int docker_index = aigp->docker.index;
|
|
int dockee_index = aigp->dockee.index;
|
|
|
|
ai_do_objects_docked_stuff( repair_objp, docker_index, repaired_objp, dockee_index );
|
|
*/
|
|
Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
|
|
}
|
|
|
|
// if the repair is done (either by abort, or ending), mark the repair ship's goal
|
|
// as being done.
|
|
if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
|
|
ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
|
|
}
|
|
} else {
|
|
if ( code == REPAIR_INFO_WARP_ADD ){
|
|
mission_bring_in_support_ship( repaired_objp );
|
|
} else {
|
|
mission_remove_scheduled_repair( repaired_objp );
|
|
}
|
|
}
|
|
}
|
|
|
|
// sends information updating clients on certain AI information that clients will
|
|
// need to know about to keep HUD information up to date. objp is the object that we
|
|
// are updating, and what is the type of stuff that we are updating.
|
|
void send_ai_info_update_packet( object *objp, char what, object * other_objp )
|
|
{
|
|
int packet_size;
|
|
ushort other_signature;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ai_info *aip;
|
|
ai_goal *aigp = NULL;
|
|
ubyte docker_index, dockee_index;
|
|
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
aip = &Ai_info[Ships[objp->instance].ai_index];
|
|
|
|
// do an out here
|
|
if ( Ships[objp->instance].is_dying_or_departing())
|
|
return;
|
|
|
|
switch( what ) {
|
|
|
|
case AI_UPDATE_DOCK:
|
|
case AI_UPDATE_UNDOCK:
|
|
Assert (other_objp != NULL);
|
|
if (other_objp == NULL) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Assert (other_objp == NULL);
|
|
break;
|
|
}
|
|
|
|
BUILD_HEADER( AI_INFO_UPDATE );
|
|
ADD_USHORT( objp->net_signature );
|
|
ADD_DATA( what );
|
|
|
|
// depending on the "what" value, we will send different information
|
|
// to the clients
|
|
switch( what ) {
|
|
|
|
case AI_UPDATE_DOCK:
|
|
// for docking ships, add the signature of the ship that we are docked with.
|
|
Assert( other_objp != NULL );
|
|
other_signature = other_objp->net_signature;
|
|
|
|
// Goober5000 - this is sort of weird, but it's the best way to do it
|
|
docker_index = (ubyte) dock_find_dockpoint_used_by_object(objp, other_objp);
|
|
dockee_index = (ubyte) dock_find_dockpoint_used_by_object(other_objp, objp);
|
|
|
|
ADD_USHORT( other_signature );
|
|
ADD_DATA( docker_index );
|
|
ADD_DATA( dockee_index );
|
|
break;
|
|
|
|
case AI_UPDATE_UNDOCK:
|
|
// same for undocking ships
|
|
Assert( other_objp != NULL );
|
|
other_signature = other_objp->net_signature;
|
|
ADD_USHORT( other_signature );
|
|
|
|
break;
|
|
|
|
case AI_UPDATE_ORDERS: {
|
|
int shipnum;
|
|
|
|
// for orders, we only need to send a little bit of information here. Be sure that the
|
|
// first order for this ship is active
|
|
Assert( (aip->active_goal != AI_ACTIVE_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
|
|
aigp = &aip->goals[aip->active_goal];
|
|
|
|
ADD_INT( static_cast<int>(aigp->ai_mode) );
|
|
ADD_INT( aigp->ai_submode );
|
|
|
|
shipnum = -1;
|
|
if ( aigp->target_name != NULL )
|
|
shipnum = ship_name_lookup( aigp->target_name );
|
|
|
|
// the ship_name member of the goals structure may or may not contain a real shipname. If we don't
|
|
// have a valid shipnum, then don't sweat it since it may not really be a ship.
|
|
if ( shipnum != -1 ) {
|
|
Assert( Ships[shipnum].objnum != -1 );
|
|
other_signature = Objects[Ships[shipnum].objnum].net_signature;
|
|
} else
|
|
other_signature = 0;
|
|
|
|
ADD_USHORT( other_signature );
|
|
|
|
// for docking, add the dock and dockee index
|
|
if (aigp->ai_mode == AI_GOAL_DOCK || aigp->ai_mode == AI_GOAL_REARM_REPAIR) {
|
|
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
|
Assert( (aigp->docker.index >= 0) && (aigp->docker.index < UCHAR_MAX) );
|
|
Assert( (aigp->dockee.index >= 0) && (aigp->dockee.index < UCHAR_MAX) );
|
|
docker_index = (ubyte) aigp->docker.index;
|
|
dockee_index = (ubyte) aigp->dockee.index;
|
|
ADD_DATA( docker_index );
|
|
ADD_DATA( dockee_index );
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Int3();
|
|
}
|
|
|
|
multi_rate_add(1, "aiu", packet_size);
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
|
|
// information is mainly used to keep the clients HUD up to date with the appropriate information.
|
|
void process_ai_info_update_packet( ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ai_goal_mode mode;
|
|
int int_mode, submode;
|
|
ushort net_signature, other_net_signature;
|
|
object *objp, *other_objp;
|
|
ai_info *aip;
|
|
ai_goal *aigp = NULL;
|
|
char code;
|
|
ubyte docker_index = 0, dockee_index = 0;
|
|
|
|
GET_USHORT( net_signature ); // signature of the object that we are dealing with.
|
|
GET_DATA( code ); // code of what we are doing.
|
|
objp = multi_get_network_object( net_signature );
|
|
if ( !objp )
|
|
nprintf(("Network", "Couldn't find object for ai update\n"));
|
|
|
|
switch( code ) {
|
|
case AI_UPDATE_DOCK:
|
|
GET_USHORT( other_net_signature );
|
|
GET_DATA( docker_index );
|
|
GET_DATA( dockee_index );
|
|
other_objp = multi_get_network_object( other_net_signature );
|
|
if ( !other_objp )
|
|
nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
|
|
|
|
// if we don't have an object to work with, break out of loop
|
|
if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
|
|
break;
|
|
}
|
|
|
|
// don't assign the dock indexes, because they're part of the docking ship's goal code... just dock them
|
|
// (and besides, we might be docking initially docked ships and we wouldn't have an active goal)
|
|
ai_do_objects_docked_stuff( objp, docker_index, other_objp, dockee_index );
|
|
break;
|
|
|
|
case AI_UPDATE_UNDOCK:
|
|
GET_USHORT( other_net_signature );
|
|
other_objp = multi_get_network_object( other_net_signature );
|
|
|
|
// if we don't have an object to work with, break out of loop
|
|
if ( !objp )
|
|
break;
|
|
|
|
ai_do_objects_undocked_stuff( objp, other_objp );
|
|
break;
|
|
|
|
case AI_UPDATE_ORDERS:
|
|
GET_INT( int_mode );
|
|
mode = int_to_ai_goal_mode(int_mode);
|
|
GET_INT( submode );
|
|
GET_USHORT( other_net_signature );
|
|
|
|
if ( mode == AI_GOAL_DOCK || mode == AI_GOAL_REARM_REPAIR ) {
|
|
GET_DATA(docker_index);
|
|
GET_DATA(dockee_index);
|
|
}
|
|
|
|
// be sure that we have a ship object!!!
|
|
if ( !objp || (objp->type != OBJ_SHIP) )
|
|
break;
|
|
|
|
// set up the information in the first goal element of the object in question
|
|
aip = &Ai_info[Ships[objp->instance].ai_index];
|
|
aip->active_goal = 0;
|
|
aigp = &aip->goals[aip->active_goal];
|
|
aigp->ai_mode = mode;
|
|
aigp->ai_submode = submode;
|
|
|
|
// for docking, add the docker and dockee index to the active goal
|
|
if ( mode == AI_GOAL_DOCK || mode == AI_GOAL_REARM_REPAIR ) {
|
|
aigp->docker.index = docker_index;
|
|
aigp->dockee.index = dockee_index;
|
|
aigp->flags.set(AI::Goal_Flags::Dockee_index_valid);
|
|
aigp->flags.set(AI::Goal_Flags::Docker_index_valid);
|
|
}
|
|
|
|
// get a shipname if we can.
|
|
other_objp = multi_get_network_object( other_net_signature );
|
|
if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
|
|
// get a pointer to the shipname in question. Use the ship_name value in the
|
|
// ship. We are only using this for HUD display, so I think that using this
|
|
// method will be fine.
|
|
aigp->target_name = ai_get_goal_target_name(Ships[other_objp->instance].ship_name, &aigp->target_name_index);
|
|
|
|
// special case for destroy subsystem -- get the ai_info pointer to our target ship
|
|
// so that we can properly set up what subsystem this ship is attacking.
|
|
if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
|
|
aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Int3(); // this Int3() should be temporary
|
|
nprintf(("Network", "Invalid code for ai update: %d\n", code));
|
|
break;
|
|
}
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
// tell the standalone to move into the MISSION_SYNC_STATE
|
|
void send_mission_sync_packet(int mode,int start_campaign)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],is_campaign;
|
|
int packet_size = 0;
|
|
|
|
Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
|
|
// build the header and add the sync mode (pre or post briefing)
|
|
BUILD_HEADER(MISSION_SYNC_DATA);
|
|
ADD_INT(mode);
|
|
|
|
// if this is a campaign game
|
|
if(mode == MULTI_SYNC_PRE_BRIEFING){
|
|
if(Game_mode & GM_CAMPAIGN_MODE){
|
|
// add a byte indicating campaign mode
|
|
is_campaign = 1;
|
|
ADD_DATA(is_campaign);
|
|
|
|
// add a byte indicating if we should be starting a campaign or continuing it
|
|
is_campaign = (ubyte)start_campaign;
|
|
ADD_DATA(is_campaign);
|
|
|
|
// add the campaign filename
|
|
ADD_STRING(Netgame.campaign_name);
|
|
}
|
|
// otherwise if this is a single mission
|
|
else {
|
|
// add a byte indicating single mission mode
|
|
is_campaign = 0;
|
|
ADD_DATA(is_campaign);
|
|
|
|
// add the mission filename
|
|
ADD_STRING(Game_current_mission_filename);
|
|
}
|
|
}
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
// move into the MISSION_SYNC state when this is received
|
|
// this packet is sent only from a game host to a standalone
|
|
void process_mission_sync_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int mode;
|
|
ubyte campaign_flag;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
|
|
// if this is a team vs team situation, lock the players send a final team update
|
|
if(Netgame.type_flags & NG_TYPE_TEAM){
|
|
multi_team_host_lock_all();
|
|
multi_team_send_update();
|
|
}
|
|
|
|
// get the sync mode (pre or post briefing)
|
|
GET_INT(mode);
|
|
|
|
if(mode == MULTI_SYNC_PRE_BRIEFING){
|
|
// get the flag indicating if this is a single mission or a campaign mode
|
|
GET_DATA(campaign_flag);
|
|
if(campaign_flag){
|
|
// get the flag indicating whether we should be starting a new campaign
|
|
GET_DATA(campaign_flag);
|
|
|
|
// get the campaign filename
|
|
GET_STRING(Netgame.campaign_name);
|
|
|
|
// either start a new campaign or continue on to the next mission in the current campaign
|
|
if(campaign_flag){
|
|
multi_campaign_start(Netgame.campaign_name);
|
|
} else {
|
|
multi_campaign_next_mission();
|
|
}
|
|
} else {
|
|
// make sure we remove the campaign mode flag
|
|
Game_mode &= ~(GM_CAMPAIGN_MODE);
|
|
|
|
// get the single mission filename
|
|
GET_STRING(Game_current_mission_filename);
|
|
strcpy_s(Netgame.mission_name,Game_current_mission_filename);
|
|
}
|
|
}
|
|
else if (mode == MULTI_SYNC_POST_BRIEFING) {
|
|
// process the initial orders now (moved from post_process_mission()in missionparse)
|
|
mission_parse_fixup_players();
|
|
ai_post_process_mission();
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// set the correct mode and m ove into the state
|
|
Multi_sync_mode = mode;
|
|
gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
|
|
}
|
|
|
|
// tell a player to merge his mission stats into his alltime stats
|
|
void send_store_stats_packet(int accept)
|
|
{
|
|
ubyte data[10],val;
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(STORE_MISSION_STATS);
|
|
|
|
// add whether we're accepting or tossing
|
|
val = (ubyte)accept;
|
|
ADD_DATA(val);
|
|
|
|
// if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_store_stats_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ubyte accept;
|
|
|
|
GET_DATA(accept);
|
|
PACKET_SET_SIZE();
|
|
|
|
// if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
// rebroadcast the packet to all others in the game
|
|
nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
|
|
multi_io_send_to_all_reliable(data, offset);
|
|
} else {
|
|
if(accept){
|
|
// all players should mark the stats as being accepted in the debriefing
|
|
multi_debrief_stats_accept();
|
|
|
|
|
|
} else {
|
|
// all players should mark the stats as being "tossed" in the debriefing
|
|
multi_debrief_stats_toss();
|
|
}
|
|
}
|
|
}
|
|
|
|
void send_debris_update_packet(object *objp,int code)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte val;
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(DEBRIS_UPDATE);
|
|
ADD_USHORT(objp->net_signature);
|
|
val = (ubyte) code;
|
|
ADD_DATA(val);
|
|
|
|
// add any extra relevant data
|
|
switch(code){
|
|
case DEBRIS_UPDATE_UPDATE:
|
|
ADD_VECTOR(objp->pos); // add position
|
|
ADD_ORIENT(objp->orient); // add orientation
|
|
ADD_VECTOR(objp->phys_info.vel); // add velocity
|
|
ADD_VECTOR(objp->phys_info.rotvel); // add rotational velocity
|
|
break;
|
|
}
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void process_debris_update_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ushort net_sig;
|
|
ubyte code;
|
|
object bogus_object;
|
|
object *objp;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
GET_USHORT(net_sig);
|
|
GET_DATA(code);
|
|
|
|
objp = NULL;
|
|
objp = multi_get_network_object(net_sig);
|
|
if(objp == NULL){
|
|
objp = &bogus_object;
|
|
}
|
|
|
|
switch((int)code){
|
|
// update the object
|
|
case DEBRIS_UPDATE_UPDATE:
|
|
GET_VECTOR(objp->pos);
|
|
GET_ORIENT(objp->orient);
|
|
GET_VECTOR(objp->phys_info.vel);
|
|
GET_VECTOR(objp->phys_info.rotvel);
|
|
break;
|
|
// simply remove it (no explosion)
|
|
case DEBRIS_UPDATE_REMOVE:
|
|
if(objp != &bogus_object){
|
|
Assert(objp->type == OBJ_DEBRIS);
|
|
obj_delete(OBJ_INDEX(objp));
|
|
}
|
|
break;
|
|
// blow it up
|
|
case DEBRIS_UPDATE_NUKE:
|
|
if(objp != &bogus_object)
|
|
debris_hit(objp, nullptr,&objp->pos,1000000.0f, nullptr);
|
|
break;
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
// ALAN begin
|
|
void send_wss_request_packet(short player_id, int from_slot, int from_index, int to_slot, int to_index, int wl_ship_slot, int ship_class, int mode, net_player *p)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(WSS_REQUEST_PACKET);
|
|
|
|
// add the request information
|
|
ADD_SHORT(player_id);
|
|
ADD_INT(from_slot);
|
|
ADD_INT(from_index);
|
|
ADD_INT(to_slot);
|
|
ADD_INT(to_index);
|
|
ADD_INT(wl_ship_slot); // only used in weapons loadout
|
|
ADD_INT(ship_class);
|
|
ADD_INT(mode);
|
|
|
|
// a standard request
|
|
if(p == NULL){
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
// being routed through the standalone to the host of the game
|
|
else {
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_wss_request_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
int from_slot,from_index;
|
|
int to_slot,to_index;
|
|
int mode;
|
|
int wl_ship_slot,ship_class;
|
|
short player_id;
|
|
int player_num;
|
|
|
|
// determine who this request is from
|
|
GET_SHORT(player_id);
|
|
player_num = find_player_index(player_id);
|
|
|
|
// read in the request data
|
|
GET_INT(from_slot);
|
|
GET_INT(from_index);
|
|
GET_INT(to_slot);
|
|
GET_INT(to_index);
|
|
GET_INT(wl_ship_slot); // only used in weapons loadout
|
|
GET_INT(ship_class); // only used in multi team select
|
|
GET_INT(mode);
|
|
PACKET_SET_SIZE();
|
|
|
|
Assert(player_num != -1);
|
|
if(player_num == -1){
|
|
return;
|
|
}
|
|
|
|
// if we're the standalone, we have to route this packet to the host of the game
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
|
|
}
|
|
// otherwise we're the host and should process the request
|
|
else {
|
|
switch(mode){
|
|
case WSS_WEAPON_SELECT :
|
|
wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
|
|
break;
|
|
case WSS_SHIP_SELECT :
|
|
multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
|
|
break;
|
|
default:
|
|
Int3();
|
|
}
|
|
}
|
|
}
|
|
|
|
void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],team;
|
|
int packet_size = 0;
|
|
|
|
Assert(size <= (MAX_PACKET_SIZE - 10));
|
|
|
|
BUILD_HEADER(WSS_UPDATE_PACKET);
|
|
|
|
// add the team/pool # this is for
|
|
team = (ubyte)team_num;
|
|
ADD_DATA(team);
|
|
|
|
// add the data block size
|
|
ADD_USHORT(static_cast<ushort>(size));
|
|
|
|
// add the data itself
|
|
memcpy(data + packet_size,wss_data,size);
|
|
packet_size += size;
|
|
|
|
// if we're also the master of the game (not on a standalone)
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// if we're only the host on the standalone, then send the packet to the standalone to be routed
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_wss_update_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte team;
|
|
ushort size;
|
|
int player_index, idx;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
// get the team/pool #
|
|
GET_DATA(team);
|
|
|
|
// get the data size
|
|
GET_USHORT(size);
|
|
|
|
// if we're the standalone, then we should be routing this data to all the other clients
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
// read in the data
|
|
offset += size;
|
|
PACKET_SET_SIZE();
|
|
|
|
// determine where this came from
|
|
player_index = find_player_index(hinfo->id);
|
|
Assert(player_index != -1);
|
|
if(player_index < 0){
|
|
return;
|
|
}
|
|
|
|
// route the packet (don't resend it to the host)
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
|
|
multi_io_send_reliable(&Net_players[idx], data, offset);
|
|
}
|
|
}
|
|
} else {
|
|
// set the proper pool pointers
|
|
common_set_team_pointers((int)team);
|
|
|
|
// read in the block of data, and apply it to the weapons/ship pools
|
|
offset += restore_wss_data(data + offset);
|
|
PACKET_SET_SIZE();
|
|
|
|
// set the pool pointers back to my own team
|
|
common_set_team_pointers(Net_player->p_info.team);
|
|
|
|
// sync the interface if this was for my own team
|
|
if((int)team == Net_player->p_info.team){
|
|
multi_ts_sync_interface();
|
|
}
|
|
}
|
|
}
|
|
// ALAN END
|
|
|
|
|
|
// function to send firing information from the client to the server once they reach
|
|
// the final sync screen.
|
|
void send_firing_info_packet()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
ubyte plinked, sdual;
|
|
|
|
Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
|
|
|
|
BUILD_HEADER(FIRING_INFO);
|
|
plinked = (ubyte)((Player_ship->flags[Ship::Ship_Flags::Primary_linked])?1:0);
|
|
sdual = (ubyte)((Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire])?1:0);
|
|
ADD_DATA( plinked );
|
|
ADD_DATA( sdual );
|
|
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
void process_firing_info_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, player_num;
|
|
ubyte plinked, sdual;
|
|
ship *shipp;
|
|
|
|
// only the master of the game should be dealing with these packets
|
|
Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_DATA( plinked );
|
|
GET_DATA( sdual );
|
|
PACKET_SET_SIZE();
|
|
|
|
player_num = find_player_index(hinfo->id);
|
|
if(player_num < 0){
|
|
nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
|
|
return;
|
|
}
|
|
|
|
// get the ship pointer for this player and set the flags accordingly.
|
|
shipp = &(Ships[Objects[Net_players[player_num].m_player->objnum].instance]);
|
|
shipp->flags.set(Ship::Ship_Flags::Primary_linked, plinked != 0);
|
|
shipp->flags.set(Ship::Ship_Flags::Secondary_dual_fire, sdual != 0);
|
|
|
|
}
|
|
|
|
// packet to deal with changing status of mission goals. used to be sent every so often from server
|
|
// to clients, but with addition of reliable sockets, send when complete, invalid, etc.
|
|
// goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
|
|
// valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
|
|
void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
|
|
BUILD_HEADER(MISSION_GOAL_INFO);
|
|
|
|
ADD_INT(goal_num);
|
|
ADD_INT(new_status);
|
|
ADD_INT(valid);
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_mission_goal_info_packet( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, goal_num, new_status, valid;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_INT(goal_num);
|
|
GET_INT(new_status);
|
|
GET_INT(valid);
|
|
PACKET_SET_SIZE();
|
|
|
|
// if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
|
|
if ( new_status != -1 ){
|
|
mission_goal_status_change( goal_num, new_status );
|
|
} else {
|
|
mission_goal_validation_change( goal_num, valid != 0 );
|
|
}
|
|
}
|
|
|
|
void send_player_settings_packet(net_player *p)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte stop;
|
|
int idx;
|
|
int packet_size = 0;
|
|
char val;
|
|
|
|
// build the header
|
|
BUILD_HEADER(PLAYER_SETTINGS);
|
|
|
|
// add all the data for all the players
|
|
stop = 0x0;
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx])){
|
|
ADD_DATA(stop);
|
|
ADD_SHORT(Net_players[idx].player_id);
|
|
|
|
// break the p_info structure by member, so we don't overwrite any absolute pointers
|
|
// ADD_DATA(Net_players[idx].p_info);
|
|
ADD_INT(Net_players[idx].p_info.team);
|
|
val = static_cast<char>(Net_players[idx].p_info.ship_index);
|
|
ADD_DATA(val);
|
|
ADD_SHORT(static_cast<short>(Net_players[idx].p_info.ship_class));
|
|
}
|
|
}
|
|
// add the stop byte
|
|
stop = 0xff;
|
|
ADD_DATA(stop);
|
|
|
|
// either broadcast the data or send to a specific player
|
|
if(p == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
} else {
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_player_settings_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset,player_num;
|
|
net_player_info bogus,*ptr;
|
|
short player_id;
|
|
ubyte stop;
|
|
short ship_class;
|
|
char ship_index;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// read in the data for all the players
|
|
GET_DATA(stop);
|
|
while(stop != 0xff){
|
|
// lookup the player
|
|
GET_SHORT(player_id);
|
|
player_num = find_player_index(player_id);
|
|
|
|
// make sure this is a valid player
|
|
if(player_num == -1){
|
|
ptr = &bogus;
|
|
} else {
|
|
ptr = &Net_players[player_num].p_info;
|
|
}
|
|
|
|
GET_INT(ptr->team);
|
|
GET_DATA(ship_index);
|
|
ptr->ship_index = ship_index;
|
|
GET_SHORT(ship_class);
|
|
ptr->ship_class = ship_class;
|
|
|
|
// next stop byte
|
|
GET_DATA(stop);
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// update the server with my new state
|
|
// MWA -- 3/31/98 -- check for in mission instead of state.
|
|
//if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
|
|
if( !(Game_mode & GM_IN_MISSION) ) {
|
|
Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
|
|
send_netplayer_update_packet();
|
|
}
|
|
|
|
|
|
// display some cool text
|
|
multi_common_add_text(XSTR("Received player settings packet\n",721),1);
|
|
}
|
|
|
|
void send_deny_packet(net_addr *addr, int code)
|
|
{
|
|
ubyte data[10];
|
|
int packet_size = 0;
|
|
|
|
// build the header and add the rejection code
|
|
BUILD_HEADER(DENY);
|
|
|
|
ADD_INT(code);
|
|
|
|
// send the packet
|
|
psnet_send(addr, data, packet_size);
|
|
}
|
|
|
|
void process_deny_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset,code;
|
|
|
|
// get the denial code
|
|
offset = HEADER_LENGTH;
|
|
GET_INT(code);
|
|
PACKET_SET_SIZE();
|
|
|
|
// if there is already a dialog active, do nothing - who cares at this point.
|
|
if(popup_active()){
|
|
return;
|
|
}
|
|
|
|
// display the appropriate dialog
|
|
switch(code){
|
|
case JOIN_DENY_JR_STATE :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is not in an appropriate state to accept",722));
|
|
break;
|
|
case JOIN_DENY_JR_TRACKER_INVAL :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a Parallax Online game and you are not a registered Parallax Online pilot",723));
|
|
break;
|
|
case JOIN_DENY_JR_PASSWD :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
|
|
break;
|
|
case JOIN_DENY_JR_CLOSED :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a closed game and the mission is in progress",725));
|
|
break;
|
|
case JOIN_DENY_JR_TEMP_CLOSED :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the netgame is forming and the host has temporarily closed it",726));
|
|
break;
|
|
case JOIN_DENY_JR_RANK_HIGH :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too high",727));
|
|
break;
|
|
case JOIN_DENY_JR_RANK_LOW :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too low",728));
|
|
break;
|
|
case JOIN_DENY_JR_DUP :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
|
|
break;
|
|
case JOIN_DENY_JR_FULL :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
|
|
break;
|
|
case JOIN_DENY_JR_BANNED :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
|
|
break;
|
|
case JOIN_DENY_JR_NOOBS :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
|
|
break;
|
|
case JOIN_DENY_JR_INGAME_JOIN :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join at this time since someone else is currently joining. Try again shortly.",733));
|
|
break;
|
|
case JOIN_DENY_JR_BAD_VERSION :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join this game because you are running an older version of FreeSpace than the server. Exit FreeSpace, and choose the 'Update FreeSpace' button in the FreeSpace launcher to download the latest version of FreeSpace.",734));
|
|
break;
|
|
case JOIN_DENY_JR_TYPE :
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join an in progress squadwar mission.",1433));
|
|
break;
|
|
}
|
|
|
|
// call this so that the join request timestamp automatically expires when we hear back from the server
|
|
multi_join_reset_join_stamp();
|
|
}
|
|
|
|
// this packet will consist of
|
|
// 1.) netplayer ship classes (85 bytes max)
|
|
// 2.) ship weapon state data (277 bytes max)
|
|
// 3.) player settings et. al. (133 bytes max)
|
|
// TOTAL 495 NOTE : keep this in mind when/if adding new data to this packet
|
|
void send_post_sync_data_packet(net_player *p, int std_request)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], val;
|
|
char bval;
|
|
ship *shipp;
|
|
net_player *pl;
|
|
ship_obj *so;
|
|
ushort sval, ship_ets;
|
|
int idx, player_index;
|
|
int packet_size = 0;
|
|
int ship_count;
|
|
|
|
BUILD_HEADER(POST_SYNC_DATA);
|
|
|
|
// some header information for standalone packet routing purposes
|
|
val = (ubyte)std_request;
|
|
ADD_DATA(val);
|
|
|
|
// the standalone has two situations
|
|
// 1.) sending a request to the host to distribute this block of data
|
|
// 2.) having recevied this block of data from the host, it redistributes it
|
|
if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
|
|
// case 1, send the request
|
|
multi_io_send_reliable(Netgame.host, data, packet_size);
|
|
return;
|
|
}
|
|
// case 2 for the standalone is below (as normal)
|
|
|
|
// otherwise build the data now
|
|
|
|
// add all deleted ships
|
|
val = (ubyte)Multi_ts_num_deleted;
|
|
ADD_DATA(val);
|
|
for(idx=0;idx<Multi_ts_num_deleted;idx++){
|
|
sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
|
|
ADD_USHORT(sval);
|
|
}
|
|
|
|
// ship count
|
|
ship_count = 0;
|
|
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
|
if (Objects[so->objnum].flags[Object::Object_Flags::Should_be_dead])
|
|
continue;
|
|
shipp = &Ships[Objects[so->objnum].instance];
|
|
|
|
// don't process non player wing ships
|
|
if ( !(shipp->flags[Ship::Ship_Flags::From_player_wing] ) )
|
|
continue;
|
|
|
|
ship_count++;
|
|
}
|
|
|
|
// # of ships - used multiple times in the packet
|
|
val = (ubyte)ship_count;
|
|
ADD_DATA(val);
|
|
|
|
// add ship class information (85 bytes max)
|
|
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
|
if (Objects[so->objnum].flags[Object::Object_Flags::Should_be_dead])
|
|
continue;
|
|
shipp = &Ships[Objects[so->objnum].instance];
|
|
|
|
// don't process non player wing ships
|
|
if ( !(shipp->flags[Ship::Ship_Flags::From_player_wing] ) )
|
|
continue;
|
|
|
|
// add the net signature of the object for look up
|
|
ADD_USHORT( Objects[so->objnum].net_signature );
|
|
|
|
// add the ship info index
|
|
ADD_SHORT(static_cast<short>(shipp->ship_info_index));
|
|
|
|
// add the ships's team select index
|
|
val = (ubyte)shipp->ts_index;
|
|
ADD_DATA(val);
|
|
}
|
|
|
|
// add weapon state information for all starting ships (277 bytes max)
|
|
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
|
if (Objects[so->objnum].flags[Object::Object_Flags::Should_be_dead])
|
|
continue;
|
|
shipp = &Ships[Objects[so->objnum].instance];
|
|
|
|
// don't process non player wing ships
|
|
if ( !(shipp->flags[Ship::Ship_Flags::From_player_wing] ) )
|
|
continue;
|
|
|
|
// if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
|
|
pl = NULL;
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
|
|
if(player_index == -1){
|
|
pl = NULL;
|
|
} else {
|
|
pl = &Net_players[player_index];
|
|
}
|
|
}
|
|
|
|
// add the net signature and other weapon information
|
|
ADD_USHORT( Objects[so->objnum].net_signature );
|
|
|
|
// add number of primary and secondary banks
|
|
bval = static_cast<char>(shipp->weapons.num_primary_banks);
|
|
ADD_DATA(bval);
|
|
bval = static_cast<char>(shipp->weapons.num_secondary_banks);
|
|
ADD_DATA(bval);
|
|
|
|
// add weapon bank status
|
|
bval = static_cast<char>(shipp->weapons.current_primary_bank);
|
|
if (pl != nullptr) {
|
|
pl->s_info.cur_primary_bank = bval;
|
|
}
|
|
// Assert(bval != -1);
|
|
ADD_DATA(bval);
|
|
|
|
bval = static_cast<char>(shipp->weapons.current_secondary_bank);
|
|
if (pl != nullptr) {
|
|
pl->s_info.cur_secondary_bank = bval;
|
|
}
|
|
// Assert(bval != -1);
|
|
ADD_DATA(bval);
|
|
|
|
// primary weapon info
|
|
for (idx = 0; idx < shipp->weapons.num_primary_banks; idx++) {
|
|
ADD_SHORT(static_cast<short>(shipp->weapons.primary_bank_weapons[idx]));
|
|
}
|
|
|
|
// secondary weapon info
|
|
for (idx = 0; idx < shipp->weapons.num_secondary_banks; idx++) {
|
|
ADD_SHORT(static_cast<short>(shipp->weapons.secondary_bank_weapons[idx]));
|
|
ADD_SHORT(static_cast<short>(shipp->weapons.secondary_bank_ammo[idx]));
|
|
}
|
|
|
|
// send primary and secondary weapon link status
|
|
val = 0x0;
|
|
if(shipp->flags[Ship::Ship_Flags::Primary_linked]){
|
|
if(pl != NULL){
|
|
pl->s_info.cur_link_status |= (1<<0);
|
|
}
|
|
val |= (1<<0);
|
|
}
|
|
if(shipp->flags[Ship::Ship_Flags::Secondary_dual_fire]){
|
|
if(pl != NULL){
|
|
pl->s_info.cur_link_status |= (1<<1);
|
|
}
|
|
val |= (1<<1);
|
|
}
|
|
// if this is a player ship add (1<<2)
|
|
if(Objects[shipp->objnum].flags[Object::Object_Flags::Player_ship]){
|
|
val |= (1<<2);
|
|
}
|
|
ADD_DATA(val);
|
|
|
|
// add a ship ets value
|
|
ship_ets = 0x0000;
|
|
// shield ets
|
|
ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
|
|
// weapon ets
|
|
ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
|
|
// engine ets
|
|
ship_ets |= ((ushort)shipp->engine_recharge_index);
|
|
ADD_USHORT(ship_ets);
|
|
|
|
}
|
|
|
|
// 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
|
|
// or if I'm the server as well as the host, I should be sending this to all players
|
|
if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// broadcast
|
|
if(p == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// send to a specific player
|
|
else {
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
// standalone mode
|
|
else {
|
|
// broadcast
|
|
if(p == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// send to a specific player
|
|
else {
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void process_post_sync_data_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte val, ts_index;
|
|
char b;
|
|
ushort net_sig, ship_ets, sval;
|
|
ship *shipp;
|
|
object *objp;
|
|
int idx, j;
|
|
int offset = HEADER_LENGTH;
|
|
int ship_count;
|
|
short val_short, sinfo_index;
|
|
|
|
// packet routing information
|
|
GET_DATA(val);
|
|
|
|
// if I'm the host on a standalone, this is going to be a request to send the data to the standalone to be routed
|
|
if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
|
|
PACKET_SET_SIZE();
|
|
|
|
// at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
|
|
multi_ts_create_wings();
|
|
|
|
// send to the standalone through my socket
|
|
send_post_sync_data_packet(Net_player);
|
|
return;
|
|
}
|
|
|
|
// process player
|
|
|
|
// add all deleted ships
|
|
GET_DATA(val);
|
|
Multi_ts_num_deleted = (int)val;
|
|
for(idx=0;idx<Multi_ts_num_deleted;idx++){
|
|
// get the ship's objnum
|
|
GET_USHORT(sval);
|
|
objp = NULL;
|
|
objp = multi_get_network_object(sval);
|
|
if(objp != NULL){
|
|
// delete the ship appropriately
|
|
// mark the object as having been deleted
|
|
Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
|
|
|
|
// delete the ship
|
|
ship_add_exited_ship(&Ships[objp->instance], Ship::Exit_Flags::Player_deleted);
|
|
obj_delete(Multi_ts_deleted_objnums[idx]);
|
|
ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
|
|
} else {
|
|
Multi_ts_num_deleted--;
|
|
nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
|
|
}
|
|
}
|
|
|
|
// ship count
|
|
GET_DATA(val);
|
|
ship_count = val;
|
|
|
|
// process ship class information
|
|
for(idx=0; idx<ship_count; idx++){
|
|
// get the object's net signature
|
|
GET_USHORT(net_sig);
|
|
GET_SHORT(sinfo_index);
|
|
GET_DATA(ts_index);
|
|
|
|
// attempt to get the object
|
|
objp = multi_get_network_object(net_sig);
|
|
|
|
// make sure we found a ship
|
|
Assertion(objp != NULL, "idx: %d, ship_count: %d, sinfo_index: %u, ts_index: %u, net_sig: %u",
|
|
idx, ship_count, sinfo_index, ts_index, net_sig);
|
|
Assertion(objp->type == OBJ_SHIP, "type: %d, idx: %d, ship_count: %d, sinfo_index: %u, ts_index: %u, net_sig: %u",
|
|
objp->type, idx, ship_count, sinfo_index, ts_index, net_sig);
|
|
|
|
// set the ship to be the right class
|
|
change_ship_type(objp->instance,(int)sinfo_index);
|
|
|
|
// set the ship's team select index
|
|
Ships[objp->instance].ts_index = (int)ts_index;
|
|
}
|
|
|
|
// process ship weapon state info
|
|
for(idx=0; idx<ship_count; idx++){
|
|
// get the object's net signature
|
|
GET_USHORT(net_sig);
|
|
|
|
// attempt to get the object
|
|
objp = multi_get_network_object(net_sig);
|
|
|
|
// make sure we found a ship
|
|
Assert((objp != NULL) && (objp->type == OBJ_SHIP));
|
|
|
|
// get a pointer to the ship
|
|
shipp = &Ships[objp->instance];
|
|
|
|
// get number of primary and secondary banks;
|
|
GET_DATA(b);
|
|
Assert( b != -1 );
|
|
shipp->weapons.num_primary_banks = static_cast<int>(b);
|
|
|
|
GET_DATA(b);
|
|
Assert( b != -1 );
|
|
shipp->weapons.num_secondary_banks = static_cast<int>(b);
|
|
|
|
// get bank selection info
|
|
GET_DATA(b);
|
|
if ( b == -1 ){
|
|
b = 0;
|
|
}
|
|
shipp->weapons.current_primary_bank = (int)b;
|
|
|
|
GET_DATA(b);
|
|
if ( b == -1 ){
|
|
b = 0;
|
|
}
|
|
shipp->weapons.current_secondary_bank = (int)b;
|
|
|
|
// primary weapon info
|
|
for (j = 0; j < shipp->weapons.num_primary_banks; j++) {
|
|
GET_SHORT(val_short);
|
|
shipp->weapons.primary_bank_weapons[j] = static_cast<int>(val_short);
|
|
shipp->weapons.primary_bank_start_ammo[j] = (int)std::lround(Ship_info[shipp->ship_info_index].primary_bank_ammo_capacity[j] / Weapon_info[shipp->weapons.primary_bank_weapons[j]].cargo_size);
|
|
}
|
|
|
|
// secondary weapon info
|
|
for (j = 0; j < shipp->weapons.num_secondary_banks; j++) {
|
|
GET_SHORT(val_short);
|
|
shipp->weapons.secondary_bank_weapons[j] = static_cast<int>(val_short);
|
|
GET_SHORT(val_short);
|
|
shipp->weapons.secondary_bank_ammo[j] = static_cast<int>(val_short);
|
|
shipp->weapons.secondary_bank_start_ammo[j] = (int)std::lround(Ship_info[shipp->ship_info_index].secondary_bank_ammo_capacity[j] / Weapon_info[shipp->weapons.secondary_bank_weapons[j]].cargo_size);
|
|
}
|
|
|
|
// other flags
|
|
val = 0x0;
|
|
GET_DATA(val);
|
|
|
|
if(val & (1<<0)){
|
|
shipp->flags.set(Ship::Ship_Flags::Primary_linked);
|
|
}
|
|
if(val & (1<<1)){
|
|
shipp->flags.set(Ship::Ship_Flags::Secondary_dual_fire);
|
|
}
|
|
Objects[shipp->objnum].flags.remove(Object::Object_Flags::Player_ship);
|
|
Objects[shipp->objnum].flags.remove(Object::Object_Flags::Could_be_player);
|
|
if(val & (1<<2)){
|
|
Objects[shipp->objnum].flags.set(Object::Object_Flags::Player_ship);
|
|
} else {
|
|
obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags + Object::Object_Flags::Could_be_player);
|
|
}
|
|
|
|
// get ship ets
|
|
GET_USHORT(ship_ets);
|
|
// shield, weapons, and engines ets in that order
|
|
set_recharge_rates(&Objects[shipp->objnum], ((ship_ets & 0x0f00) >> 8), ((ship_ets & 0x00f0) >> 4), (ship_ets & 0x000f));
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// ack the server
|
|
Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
|
|
send_netplayer_update_packet();
|
|
|
|
// the standalone server will receive this packet from the host of the game, to be applied locally and
|
|
// also to be rebroadcast.
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
// update player ets settings
|
|
for(idx=0;idx<MAX_PLAYERS;idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player->objnum != -1)){
|
|
multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].m_player->objnum].instance]);
|
|
}
|
|
}
|
|
|
|
send_post_sync_data_packet(NULL,0);
|
|
}
|
|
}
|
|
|
|
void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
short val_short;
|
|
int idx,i;
|
|
int packet_size = 0;
|
|
|
|
// build the header
|
|
BUILD_HEADER(WSS_SLOTS_DATA);
|
|
|
|
// some header information for standalone packet routing purposes
|
|
val = (ubyte)std_request;
|
|
ADD_DATA(val);
|
|
|
|
// add the team #
|
|
val = (ubyte)team_num;
|
|
ADD_DATA(val);
|
|
|
|
// add whether this is the final packet or not
|
|
val = (ubyte)final;
|
|
ADD_DATA(val);
|
|
|
|
// the standalone has two situations
|
|
// 1.) sending a request to the host to distribute this block of data
|
|
// 2.) having recevied this block of data from the host, it redistributes it
|
|
if((Game_mode & GM_STANDALONE_SERVER) && std_request){
|
|
// case 1, send the request
|
|
multi_io_send_reliable(Netgame.host, data, packet_size);
|
|
return;
|
|
}
|
|
// case 2 for the standalone is below (as normal)
|
|
|
|
// add all the slots
|
|
val = MULTI_TS_NUM_SHIP_SLOTS;
|
|
ADD_DATA(val);
|
|
|
|
val = MAX_SHIP_WEAPONS;
|
|
ADD_DATA(val);
|
|
|
|
for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
|
|
// add the ship class
|
|
val_short = static_cast<short>(Wss_slots_teams[team_num][idx].ship_class);
|
|
ADD_SHORT(val_short);
|
|
|
|
// add the weapons and counts
|
|
for(i = 0;i<MAX_SHIP_WEAPONS;i++){
|
|
val_short = static_cast<short>(Wss_slots_teams[team_num][idx].wep[i]);
|
|
ADD_SHORT(val_short);
|
|
val_short = static_cast<short>(Wss_slots_teams[team_num][idx].wep_count[i]);
|
|
ADD_SHORT(val_short);
|
|
}
|
|
}
|
|
|
|
// 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
|
|
// or if I'm the server as well as the host, I should be sending this to all players
|
|
if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
// broadcast
|
|
if(p == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// send to a specific player
|
|
else {
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
} else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
// standalone mode
|
|
else {
|
|
// broadcast
|
|
if(p == NULL){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// send to a specific player
|
|
else {
|
|
multi_io_send_reliable(p, data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void process_wss_slots_data_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte val, team_num, final, num_ship_slots, num_ship_weapons;
|
|
int idx ,i, j;
|
|
int offset = HEADER_LENGTH;
|
|
short val_short, b1, b2;
|
|
|
|
// packet routing information
|
|
GET_DATA(val);
|
|
|
|
// get team data
|
|
GET_DATA(team_num);
|
|
|
|
// get whether this is the final packet or not
|
|
GET_DATA(final);
|
|
|
|
// if I'm the host on a standalone, this is going to be a request to send the data to the standalone to be routed
|
|
if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
|
|
PACKET_SET_SIZE();
|
|
|
|
// send to the standalone through my socket
|
|
send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
|
|
return;
|
|
}
|
|
|
|
// read in all the slot data
|
|
GET_DATA(num_ship_slots);
|
|
GET_DATA(num_ship_weapons);
|
|
|
|
// reset unit data
|
|
for (i = 0; i < MAX_WSS_SLOTS; i++) {
|
|
Wss_slots_teams[team_num][i].ship_class = -1;
|
|
|
|
for (j = 0; j < MAX_SHIP_WEAPONS; j++) {
|
|
Wss_slots_teams[team_num][i].wep[j] = -1;
|
|
Wss_slots_teams[team_num][i].wep_count[j] = 0;
|
|
}
|
|
}
|
|
|
|
for (idx = 0; idx < num_ship_slots; idx++) {
|
|
// get the ship class
|
|
GET_SHORT(val_short);
|
|
|
|
if (idx < MAX_WSS_SLOTS) {
|
|
Wss_slots_teams[team_num][idx].ship_class = val_short;
|
|
}
|
|
|
|
// get the weapons and counts
|
|
for (i = 0; i < num_ship_weapons; i++) {
|
|
GET_SHORT(b1);
|
|
GET_SHORT(b2);
|
|
|
|
if ( (idx < MAX_WSS_SLOTS) && (i < MAX_SHIP_WEAPONS) ) {
|
|
Wss_slots_teams[team_num][idx].wep[i] = b1;
|
|
Wss_slots_teams[team_num][idx].wep_count[i] = b2;
|
|
}
|
|
}
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// update my netplayer state if this is the final packet
|
|
if(final){
|
|
Net_player->state = NETPLAYER_STATE_WSS_ACK;
|
|
send_netplayer_update_packet();
|
|
}
|
|
|
|
// the standalone server will receive this packet from the host of the game, to be applied locally and
|
|
// also to be rebroadcast.
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
|
|
} else {
|
|
// add some mission sync text
|
|
multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
|
|
}
|
|
}
|
|
|
|
#define OBJ_VISIBILITY_DOT 0.6f
|
|
|
|
// send and receive packets for shield explosion information
|
|
void send_shield_explosion_packet( int objnum, int tri_num, vec3d /*hit_pos*/ )
|
|
{
|
|
int packet_size, i;
|
|
ubyte data[MAX_PACKET_SIZE], utri_num;
|
|
|
|
Int3();
|
|
// Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
|
|
|
|
Assert( tri_num < UCHAR_MAX );
|
|
utri_num = (ubyte)tri_num;
|
|
|
|
// for each player, determine if this object is behind the player -- if so, don't
|
|
// send the packet.
|
|
for ( i = 0; i < MAX_PLAYERS; i++ ) {
|
|
if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
|
|
float dot;
|
|
vec3d eye_to_obj_vec, diff, eye_pos;
|
|
matrix eye_orient;
|
|
|
|
eye_pos = Net_players[i].s_info.eye_pos;
|
|
eye_orient = Net_players[i].s_info.eye_orient;
|
|
|
|
// check for same vectors
|
|
vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
|
|
if ( vm_vec_mag_quick(&diff) < 0.01 ){
|
|
continue;
|
|
}
|
|
|
|
vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
|
|
dot = vm_vec_dot(&eye_orient.vec.fvec, &eye_to_obj_vec);
|
|
|
|
if ( dot < OBJ_VISIBILITY_DOT ){
|
|
continue;
|
|
}
|
|
|
|
BUILD_HEADER(SHIELD_EXPLOSION);
|
|
|
|
ADD_USHORT( Objects[objnum].net_signature );
|
|
ADD_DATA(utri_num);
|
|
|
|
multi_io_send(&Net_players[i], data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void add_shield_point_multi(int objnum, int tri_num, vec3d *hit_pos);
|
|
|
|
void process_shield_explosion_packet( ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
ubyte utri_num;
|
|
object *objp;
|
|
|
|
// get the shield hit data
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(signature);
|
|
GET_DATA(utri_num);
|
|
//GET_DATA(hit_pos);
|
|
PACKET_SET_SIZE();
|
|
|
|
// find the object with this signature. If found, then do a shield explosion
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp ) {
|
|
polymodel *pm;
|
|
shield_info *shieldp;
|
|
shield_tri stri;
|
|
vec3d hit_pos;
|
|
int i;
|
|
|
|
// given the tri num, find the local position which is the average of the
|
|
// three vertices of the triangle affected. Use this average point as the hit
|
|
// point
|
|
// Assert( objp->type == OBJ_SHIP );
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
|
|
pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
|
|
shieldp = &pm->shield;
|
|
Assert( utri_num < shieldp->ntris );
|
|
stri = shieldp->tris[utri_num];
|
|
vm_vec_zero(&hit_pos);
|
|
for ( i = 0; i < 3; i++ ) {
|
|
vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
|
|
}
|
|
vm_vec_scale( &hit_pos, 1.0f/3.0f );
|
|
add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
|
|
}
|
|
}
|
|
|
|
#define MAX_SHIPS_PER_PACKET ((MAX_PACKET_SIZE-21)/static_cast<int>(sizeof(std::int32_t)))
|
|
|
|
void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target, const int offset, const int count)
|
|
{
|
|
scoring_struct *sc;
|
|
ubyte data[MAX_PACKET_SIZE], val;
|
|
int idx, len;
|
|
int packet_size = 0;
|
|
|
|
ushort u_tmp;
|
|
int i_tmp;
|
|
|
|
sc = &pl->m_player->stats;
|
|
|
|
// build the header
|
|
BUILD_HEADER(PLAYER_STATS);
|
|
|
|
// add the player id
|
|
ADD_SHORT(pl->player_id);
|
|
|
|
// add the byte indicating whether these stats are all-time or not
|
|
val = (ubyte)stats_code;
|
|
ADD_DATA(val);
|
|
|
|
// kill information - alltime
|
|
switch(stats_code){
|
|
case STATS_ALLTIME:
|
|
// alltime kills
|
|
idx = 0;
|
|
while (idx < MAX_SHIP_CLASSES) {
|
|
send_player_stats_block_packet(pl, STATS_ALLTIME_KILLS, target, idx, MAX_SHIP_CLASSES-idx);
|
|
idx += MAX_SHIPS_PER_PACKET;
|
|
}
|
|
|
|
Assert( ((int)Medals.size() >= 0) && ((int)Medals.size() < USHRT_MAX) );
|
|
ADD_USHORT( (ushort)Medals.size() );
|
|
|
|
// medal information
|
|
for(idx=0;idx<(int)Medals.size();idx++){
|
|
i_tmp = sc->medal_counts[idx];
|
|
ADD_INT(i_tmp);
|
|
}
|
|
|
|
ADD_INT(sc->score);
|
|
ADD_INT(sc->rank);
|
|
ADD_INT(sc->assists);
|
|
ADD_INT(sc->kill_count);
|
|
ADD_INT(sc->kill_count_ok);
|
|
ADD_UINT(sc->p_shots_fired);
|
|
ADD_UINT(sc->s_shots_fired);
|
|
ADD_UINT(sc->p_shots_hit);
|
|
ADD_UINT(sc->s_shots_hit);
|
|
ADD_UINT(sc->p_bonehead_hits);
|
|
ADD_UINT(sc->s_bonehead_hits);
|
|
ADD_INT(sc->bonehead_kills);
|
|
|
|
ADD_UINT(sc->missions_flown);
|
|
ADD_UINT(sc->flight_time);
|
|
ADD_INT(sc->last_flown); // should be 32-bit value - taylor
|
|
ADD_INT(sc->last_backup); // should be 32-bit value - taylor
|
|
break;
|
|
|
|
case STATS_MISSION:
|
|
// mission OKkills
|
|
idx = 0;
|
|
while (idx < MAX_SHIP_CLASSES) {
|
|
send_player_stats_block_packet(pl, STATS_MISSION_CLASS_KILLS, target, idx, MAX_SHIP_CLASSES-idx);
|
|
idx += MAX_SHIPS_PER_PACKET;
|
|
}
|
|
|
|
ADD_INT(sc->m_score);
|
|
ADD_INT(sc->m_assists);
|
|
ADD_INT(sc->m_kill_count);
|
|
ADD_INT(sc->m_kill_count_ok);
|
|
ADD_UINT(sc->mp_shots_fired);
|
|
ADD_UINT(sc->ms_shots_fired);
|
|
ADD_UINT(sc->mp_shots_hit);
|
|
ADD_UINT(sc->ms_shots_hit);
|
|
ADD_UINT(sc->mp_bonehead_hits);
|
|
ADD_UINT(sc->ms_bonehead_hits);
|
|
ADD_INT(sc->m_bonehead_kills);
|
|
ADD_INT(sc->m_player_deaths);
|
|
ADD_INT(sc->m_medal_earned);
|
|
break;
|
|
|
|
case STATS_MISSION_KILLS:
|
|
ADD_INT(sc->m_kill_count);
|
|
ADD_INT(sc->m_kill_count_ok);
|
|
ADD_INT(sc->m_assists);
|
|
break;
|
|
|
|
case STATS_DOGFIGHT_KILLS:
|
|
val = MAX_PLAYERS;
|
|
ADD_DATA(val);
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
u_tmp = (ushort)sc->m_dogfight_kills[idx];
|
|
ADD_USHORT(u_tmp);
|
|
}
|
|
ADD_INT(sc->m_kill_count);
|
|
ADD_INT(sc->m_kill_count_ok);
|
|
ADD_INT(sc->m_assists);
|
|
break;
|
|
|
|
case STATS_MISSION_CLASS_KILLS:
|
|
len = std::min(count, MAX_SHIPS_PER_PACKET);
|
|
|
|
ADD_USHORT(static_cast<ushort>(offset));
|
|
ADD_USHORT(static_cast<ushort>(len));
|
|
|
|
for (idx = offset; idx < len; idx++) {
|
|
ADD_INT(sc->m_okKills[idx]);
|
|
}
|
|
break;
|
|
|
|
case STATS_ALLTIME_KILLS:
|
|
len = std::min(count, MAX_SHIPS_PER_PACKET);
|
|
|
|
ADD_USHORT(static_cast<ushort>(offset));
|
|
ADD_USHORT(static_cast<ushort>(len));
|
|
|
|
for (idx = offset; idx < len; idx++) {
|
|
ADD_INT(sc->kills[idx]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
Assert(packet_size < MAX_PACKET_SIZE);
|
|
|
|
// if we're a client, always send the data to the server
|
|
if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
// otherwise do server specific stuff
|
|
else {
|
|
// send to a specific target
|
|
if(target != NULL){
|
|
multi_io_send_reliable(target, data, packet_size);
|
|
}
|
|
// otherwise, send to everyone
|
|
else {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void process_player_stats_block_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte val, num_players;
|
|
int player_num,idx;
|
|
scoring_struct *sc,bogus;
|
|
short player_id;
|
|
int offset = HEADER_LENGTH;
|
|
ushort u_tmp, num_medals;
|
|
int i_tmp;
|
|
ushort si_offset, si_count;
|
|
|
|
|
|
// nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
|
|
|
|
// get the player who these stats are for
|
|
GET_SHORT(player_id);
|
|
player_num = find_player_index(player_id);
|
|
if (player_num == -1) {
|
|
nprintf(("Network", "Couldn't find player for stats update!\n"));
|
|
ml_string("Couldn't find player for stats update!\n");
|
|
|
|
sc = &bogus;
|
|
Int3();
|
|
} else {
|
|
sc = &Net_players[player_num].m_player->stats;
|
|
}
|
|
|
|
// get the stats code
|
|
GET_DATA(val);
|
|
switch(val){
|
|
case STATS_ALLTIME_KILLS:
|
|
GET_USHORT(si_offset);
|
|
GET_USHORT(si_count);
|
|
|
|
for (idx = si_offset; idx < si_offset+si_count; idx++) {
|
|
GET_INT(i_tmp);
|
|
|
|
if (idx < MAX_SHIP_CLASSES) {
|
|
sc->kills[idx] = i_tmp;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATS_MISSION_CLASS_KILLS:
|
|
GET_USHORT(si_offset);
|
|
GET_USHORT(si_count);
|
|
|
|
for (idx = si_offset; idx < si_offset+si_count; idx++) {
|
|
GET_INT(i_tmp);
|
|
|
|
if (idx < MAX_SHIP_CLASSES) {
|
|
sc->m_okKills[idx] = i_tmp;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATS_ALLTIME:
|
|
ml_string("Received STATS_ALLTIME\n");
|
|
|
|
// read in the stats
|
|
GET_USHORT( num_medals );
|
|
|
|
for (idx = 0; idx < num_medals; idx++) {
|
|
GET_INT(i_tmp);
|
|
|
|
if (idx < (int)Medals.size()) {
|
|
sc->medal_counts[idx] = i_tmp;
|
|
}
|
|
}
|
|
|
|
GET_INT(sc->score);
|
|
GET_INT(sc->rank);
|
|
GET_INT(sc->assists);
|
|
GET_INT(sc->kill_count);
|
|
GET_INT(sc->kill_count_ok);
|
|
GET_UINT(sc->p_shots_fired);
|
|
GET_UINT(sc->s_shots_fired);
|
|
GET_UINT(sc->p_shots_hit);
|
|
GET_UINT(sc->s_shots_hit);
|
|
GET_UINT(sc->p_bonehead_hits);
|
|
GET_UINT(sc->s_bonehead_hits);
|
|
GET_INT(sc->bonehead_kills);
|
|
|
|
GET_UINT(sc->missions_flown);
|
|
GET_UINT(sc->flight_time);
|
|
GET_INT(sc->last_flown); // should be 32-bit value - taylor
|
|
GET_INT(sc->last_backup); // should be 32-bit value - taylor
|
|
break;
|
|
|
|
case STATS_MISSION:
|
|
ml_string("Received STATS_MISSION\n");
|
|
|
|
GET_INT(sc->m_score);
|
|
GET_INT(sc->m_assists);
|
|
GET_INT(sc->m_kill_count);
|
|
GET_INT(sc->m_kill_count_ok);
|
|
GET_UINT(sc->mp_shots_fired);
|
|
GET_UINT(sc->ms_shots_fired);
|
|
GET_UINT(sc->mp_shots_hit);
|
|
GET_UINT(sc->ms_shots_hit);
|
|
GET_UINT(sc->mp_bonehead_hits);
|
|
GET_UINT(sc->ms_bonehead_hits);
|
|
GET_INT(sc->m_bonehead_kills);
|
|
GET_INT(sc->m_player_deaths);
|
|
GET_INT(sc->m_medal_earned);
|
|
break;
|
|
|
|
case STATS_MISSION_KILLS:
|
|
ml_string("Received STATS_MISSION_KILLS\n");
|
|
|
|
GET_INT(sc->m_kill_count);
|
|
GET_INT(sc->m_kill_count_ok);
|
|
GET_INT(sc->m_assists);
|
|
break;
|
|
|
|
case STATS_DOGFIGHT_KILLS:
|
|
ml_string("Received STATS_DOGFIGHT_KILLS\n");
|
|
if(player_num >= 0){
|
|
ml_printf("Dogfight stats for %s", Net_players[player_num].m_player->callsign);
|
|
}
|
|
|
|
GET_DATA(num_players);
|
|
|
|
for (idx = 0; idx < num_players; idx++) {
|
|
GET_USHORT(u_tmp);
|
|
|
|
if (idx < MAX_PLAYERS) {
|
|
sc->m_dogfight_kills[idx] = u_tmp;
|
|
|
|
if (player_num >= 0) {
|
|
ml_printf("%d", Net_players[player_num].m_player->stats.m_dogfight_kills[idx]);
|
|
}
|
|
}
|
|
}
|
|
GET_INT(sc->m_kill_count);
|
|
GET_INT(sc->m_kill_count_ok);
|
|
GET_INT(sc->m_assists);
|
|
break;
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// if I'm the server of the game, I should always rebroadcast these stats
|
|
if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
|
|
// make sure these are alltime stats
|
|
Assert(val == STATS_ALLTIME || val == STATS_ALLTIME_KILLS);
|
|
|
|
multi_broadcast_stats(val);
|
|
}
|
|
}
|
|
|
|
// called to create asteroid stuff
|
|
void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vec3d *relvec )
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ubyte packet_type, atype;
|
|
vec3d vec;
|
|
|
|
vm_vec_zero(&vec);
|
|
if (relvec != NULL )
|
|
vec = *relvec;
|
|
|
|
BUILD_HEADER( ASTEROID_INFO );
|
|
packet_type = ASTEROID_CREATE;
|
|
|
|
Assert( asteroid_type < UCHAR_MAX );
|
|
atype = (ubyte)asteroid_type;
|
|
|
|
ADD_DATA( packet_type );
|
|
ADD_USHORT( parent_objp->net_signature );
|
|
ADD_USHORT( new_objp->net_signature );
|
|
ADD_DATA( atype );
|
|
ADD_VECTOR( vec );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void send_asteroid_throw( object *objp )
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE], packet_type;
|
|
|
|
BUILD_HEADER( ASTEROID_INFO );
|
|
|
|
// this packet type is an asteroid throw
|
|
packet_type = ASTEROID_THROW;
|
|
ADD_DATA( packet_type );
|
|
ADD_USHORT( objp->net_signature );
|
|
ADD_VECTOR( objp->pos );
|
|
ADD_VECTOR( objp->phys_info.vel );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void send_asteroid_hit( object *objp, object *other_objp, vec3d *hitpos, float damage, vec3d* force )
|
|
{
|
|
int packet_size;
|
|
ubyte data[MAX_PACKET_SIZE], packet_type;
|
|
|
|
if ( hitpos == nullptr )
|
|
hitpos = &vmd_zero_vector;
|
|
if ( force == nullptr )
|
|
force = &vmd_zero_vector;
|
|
|
|
// build up an asteroid hit packet
|
|
BUILD_HEADER( ASTEROID_INFO );
|
|
packet_type = ASTEROID_HIT;
|
|
ADD_DATA( packet_type );
|
|
ADD_USHORT( objp->net_signature );
|
|
|
|
if(other_objp == NULL){
|
|
ushort invalid_sig = 0xffff;
|
|
ADD_USHORT(invalid_sig);
|
|
} else {
|
|
ADD_USHORT( other_objp->net_signature );
|
|
}
|
|
ADD_VECTOR( (*hitpos) );
|
|
ADD_FLOAT( damage );
|
|
ADD_VECTOR( (*force) );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void process_asteroid_info( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
ubyte packet_type;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_DATA( packet_type );
|
|
|
|
// based on the packet type, do something interesting with an asteroid!
|
|
switch( packet_type ) {
|
|
|
|
case ASTEROID_CREATE: {
|
|
ushort psignature, signature;
|
|
ubyte atype;
|
|
vec3d relvec;
|
|
object *parent_objp;
|
|
|
|
GET_USHORT( psignature );
|
|
GET_USHORT( signature );
|
|
GET_DATA( atype );
|
|
GET_VECTOR( relvec );
|
|
|
|
// after getting the values, set the next network signature, and call the create sub function
|
|
multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
|
|
parent_objp = multi_get_network_object( psignature );
|
|
if ( parent_objp ) {
|
|
asteroid_sub_create( parent_objp, atype, &relvec );
|
|
} else {
|
|
nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
// asteroid throw packet -- asteroid has wrapped bounds
|
|
case ASTEROID_THROW: {
|
|
ushort signature;
|
|
vec3d pos, vel;
|
|
object *objp;
|
|
|
|
GET_USHORT( signature );
|
|
GET_VECTOR( pos );
|
|
GET_VECTOR( vel );
|
|
objp = multi_get_network_object( signature );
|
|
if ( !objp ) {
|
|
nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
|
|
break;
|
|
}
|
|
objp->pos = pos;
|
|
objp->phys_info.vel = vel;
|
|
objp->phys_info.desired_vel = vel;
|
|
break;
|
|
}
|
|
|
|
case ASTEROID_HIT: {
|
|
ushort signature, osignature;
|
|
object *objp, *other_objp;
|
|
vec3d hitpos, force;
|
|
float damage;
|
|
|
|
GET_USHORT( signature );
|
|
GET_USHORT( osignature );
|
|
GET_VECTOR( hitpos );
|
|
GET_FLOAT( damage );
|
|
GET_VECTOR( force );
|
|
|
|
objp = multi_get_network_object( signature );
|
|
if(osignature == 0xffff){
|
|
other_objp = NULL;
|
|
} else {
|
|
other_objp = multi_get_network_object( osignature );
|
|
}
|
|
if ( !objp ) {
|
|
nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
|
|
break;
|
|
}
|
|
|
|
if ( IS_VEC_NULL(&hitpos) ){
|
|
asteroid_hit( objp, other_objp, NULL, damage, &force );
|
|
} else {
|
|
asteroid_hit( objp, other_objp, &hitpos, damage, &force);
|
|
}
|
|
|
|
// if we know the other object is a weapon, then do a weapon hit to kill the weapon
|
|
if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
|
|
bool armed = weapon_hit( other_objp, objp, &hitpos );
|
|
maybe_play_conditional_impacts({}, other_objp, objp, armed, -1, &hitpos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
}
|
|
|
|
void send_host_restr_packet(const char *callsign,int code,int mode)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
int packet_size = 0;
|
|
|
|
// build the header and add the opcode
|
|
BUILD_HEADER(HOST_RESTR_QUERY);
|
|
val = (ubyte)code;
|
|
ADD_DATA(val);
|
|
val = (ubyte)mode;
|
|
ADD_DATA(val);
|
|
|
|
// add the callsign
|
|
ADD_STRING(callsign);
|
|
|
|
// if I'm the standalone server, I should be sending this to the game host
|
|
if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
|
|
multi_io_send_reliable(Netgame.host, data, packet_size);
|
|
}
|
|
// otherwise if I'm the host, I should be sending a reply back to the standalone server
|
|
else {
|
|
Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_host_restr_packet(ubyte *data, header *hinfo)
|
|
{
|
|
char callsign[255];
|
|
ubyte code,mode;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
// get the opcode and the callsign
|
|
GET_DATA(code);
|
|
GET_DATA(mode);
|
|
GET_STRING(callsign);
|
|
PACKET_SET_SIZE();
|
|
|
|
// do code specific operations
|
|
switch(code)
|
|
{
|
|
// query to the host from standalone
|
|
case 0:
|
|
Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
|
|
// set the join mode
|
|
Multi_join_restr_mode = mode;
|
|
|
|
// set the timestamp
|
|
Multi_restr_query_timestamp = ui_timestamp(MULTI_QUERY_RESTR_STAMP);
|
|
|
|
// notify the host of the event
|
|
gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG_FAIL);
|
|
HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
|
|
break;
|
|
|
|
// affirmative reply from the host to the standalone
|
|
case 1:
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
|
|
// let the player join if the timestamp has not already elapsed on the server
|
|
if(Multi_restr_query_timestamp.isValid()){
|
|
multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
|
|
}
|
|
break;
|
|
|
|
// negative reply
|
|
case 2 :
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
|
|
Multi_restr_query_timestamp = UI_TIMESTAMP::invalid();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void send_netgame_end_error_packet(int notify_code,int err_code)
|
|
{
|
|
ubyte data[10];
|
|
char code;
|
|
int packet_size = 0;
|
|
|
|
// only the server should ever be here - although this might change if for some reason the host wants to end the game
|
|
Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
|
|
|
|
// build the header and add the notification and error codes
|
|
BUILD_HEADER(NETGAME_END_ERROR);
|
|
code = (char)notify_code;
|
|
ADD_DATA(code);
|
|
code = (char)err_code;
|
|
ADD_DATA(code);
|
|
|
|
// send the packet
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_netgame_end_error_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
char notify_code,error_code;
|
|
|
|
// get the error and notification codes
|
|
GET_DATA(notify_code);
|
|
GET_DATA(error_code);
|
|
PACKET_SET_SIZE();
|
|
|
|
// quit the game
|
|
multi_quit_game(PROMPT_NONE,notify_code,error_code);
|
|
}
|
|
|
|
// sends info that a countermeasure succeeded.
|
|
void send_countermeasure_success_packet( int objnum )
|
|
{
|
|
int pnum, packet_size;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
|
|
pnum = multi_find_player_by_object( &Objects[objnum] );
|
|
if ( pnum == -1 ) {
|
|
nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
|
|
return;
|
|
}
|
|
|
|
BUILD_HEADER(COUNTERMEASURE_SUCCESS);
|
|
multi_io_send(&Net_players[pnum], data, packet_size);
|
|
}
|
|
|
|
// start the flashing of my hud gauge
|
|
void process_countermeasure_success_packet( ubyte * /*data*/, header *hinfo )
|
|
{
|
|
int offset;
|
|
|
|
offset = HEADER_LENGTH;
|
|
PACKET_SET_SIZE();
|
|
|
|
//Do this instead so there's less repeat code
|
|
//Player_obj is necessary...infinitely recursive function calls != FTW
|
|
cmeasure_maybe_alert_success(Player_obj);
|
|
/*hud_start_text_flash(XSTR("Evaded", 1430), 800);
|
|
snd_play(gamesnd_get_game_sound(SND_MISSILE_EVADED_POPUP));*/
|
|
}
|
|
|
|
#define UPDATE_IS_PAUSED (1<<0)
|
|
#define UPDATE_HULL_INFO (1<<1)
|
|
|
|
void send_client_update_packet(net_player *pl)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE],val;
|
|
int packet_size = 0;
|
|
|
|
// build the header
|
|
BUILD_HEADER(CLIENT_UPDATE);
|
|
|
|
val = 0;
|
|
|
|
// add the pause status
|
|
if ( Multi_pause_status ) {
|
|
val |= UPDATE_IS_PAUSED;
|
|
} else if ( (Game_mode & GM_IN_MISSION) && !(pl->flags & NETINFO_FLAG_INGAME_JOIN) && !(NETPLAYER_IS_OBSERVER(pl)) && !(NETPLAYER_IS_DEAD(pl)) && (Objects[pl->m_player->objnum].type == OBJ_SHIP) ) {
|
|
val |= UPDATE_HULL_INFO;
|
|
Assert( Player_ship ); // I"d better have one of these!!!!
|
|
}
|
|
|
|
ADD_DATA(val);
|
|
|
|
// if paused, add the net address of the guy who paused
|
|
if(val & UPDATE_IS_PAUSED){
|
|
Assert(Multi_pause_pauser != NULL);
|
|
ADD_SHORT(Multi_pause_pauser->player_id);
|
|
}
|
|
|
|
// when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
|
|
if ( val & UPDATE_HULL_INFO ) {
|
|
object *objp;
|
|
ubyte percent, threats, n_quadrants;
|
|
ship_info *sip;
|
|
ship *shipp;
|
|
ship_subsys *subsysp;
|
|
int i;
|
|
|
|
// get the object for the player
|
|
Assert( pl->m_player->objnum != -1 );
|
|
|
|
objp = &Objects[pl->m_player->objnum];
|
|
|
|
Assert ( objp->type == OBJ_SHIP );
|
|
shipp = &Ships[objp->instance];
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
// hull strength and shield mesh information are floats (as a percentage). Pass the integer
|
|
// percentage value since that should be close enough
|
|
percent = (ubyte)std::lround (get_hull_pct(objp) * 100.0f);
|
|
if ( (percent == 0) && (get_hull_pct(objp) > 0.0f) ) {
|
|
percent = 1;
|
|
}
|
|
ADD_DATA( percent );
|
|
|
|
n_quadrants = static_cast<ubyte>(objp->shield_quadrant.size());
|
|
ADD_DATA( n_quadrants );
|
|
float max_quad = shield_get_max_quad(objp);
|
|
for (float quadrant: objp->shield_quadrant) {
|
|
percent = static_cast<ubyte>(quadrant / max_quad * 100.0f);
|
|
ADD_DATA( percent );
|
|
}
|
|
|
|
// add the data for the subsystem hits. We can assume that the lists will be the same side of
|
|
// both machines. Added as percent since that number <= 100
|
|
|
|
// also write out the number of subsystems. We do this because the client might not know
|
|
// about the object he is getting data for. (i.e. he killed the object already).
|
|
ADD_USHORT(static_cast<ushort>(sip->n_subsystems));
|
|
|
|
// now the subsystems.
|
|
for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
|
|
percent = (ubyte)(subsysp->current_hits / subsysp->max_hits * 100.0f);
|
|
ADD_DATA( percent );
|
|
}
|
|
|
|
// compute the threats for this player. Only compute the threats if this player is actually
|
|
// playing (i.e. he has a ship)
|
|
hud_update_reticle( pl->m_player );
|
|
threats = (ubyte)pl->m_player->threat_flags;
|
|
ADD_DATA( threats );
|
|
|
|
// add his energy level for guns
|
|
ADD_FLOAT(shipp->weapon_energy);
|
|
|
|
// add his secondary bank ammo
|
|
val = static_cast<ubyte>(shipp->weapons.num_secondary_banks);
|
|
ADD_DATA(val);
|
|
|
|
for(i=0; i<shipp->weapons.num_secondary_banks; i++){
|
|
ADD_SHORT(static_cast<short>(shipp->weapons.secondary_bank_ammo[i]));
|
|
}
|
|
}
|
|
|
|
// add pl
|
|
ADD_INT(pl->sv_last_pl);
|
|
|
|
// send the packet reliably to the player
|
|
multi_io_send(pl, data, packet_size);
|
|
}
|
|
|
|
void process_client_update_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ubyte val;
|
|
short pauser;
|
|
int player_index;
|
|
int is_paused, have_hull_info;
|
|
ubyte ammo_count;
|
|
short ammo[MAX_SHIP_SECONDARY_BANKS];
|
|
float weapon_energy;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
// get the header byte containing useful information
|
|
GET_DATA(val);
|
|
|
|
is_paused = (val & UPDATE_IS_PAUSED)?1:0;
|
|
have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
|
|
|
|
// if we are paused, get who paused
|
|
if(is_paused){
|
|
GET_SHORT(pauser);
|
|
player_index = find_player_index(pauser);
|
|
if(player_index != -1){
|
|
Multi_pause_pauser = &Net_players[player_index];
|
|
} else {
|
|
Multi_pause_pauser = NULL;
|
|
}
|
|
}
|
|
|
|
// if we have hull information, then read it in.
|
|
if ( have_hull_info ) {
|
|
float fl_val;
|
|
ship_info *sip;
|
|
ship *shipp;
|
|
ubyte hull_percent, n_quadrants, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
|
|
SCP_vector<ubyte> shield_percent;
|
|
ubyte ub_tmp;
|
|
ship_subsys *subsysp;
|
|
object *objp;
|
|
int i;
|
|
ushort n_subsystems;
|
|
short s_val;
|
|
|
|
// hull strength and shield mesh information are floats (as a percentage). Pass the integer
|
|
// percentage value since that should be close enough
|
|
GET_DATA( hull_percent );
|
|
|
|
GET_DATA( n_quadrants );
|
|
shield_percent.resize(n_quadrants);
|
|
for (i = 0; i < n_quadrants; i++ ){
|
|
GET_DATA(ub_tmp);
|
|
shield_percent[i] = ub_tmp;
|
|
}
|
|
|
|
// get the data for the subsystems
|
|
GET_USHORT( n_subsystems );
|
|
for ( i = 0; i < n_subsystems; i++ ){
|
|
GET_DATA(ub_tmp);
|
|
subsystem_percent[i] = ub_tmp;
|
|
}
|
|
|
|
GET_DATA( threats );
|
|
|
|
// add his energy level for guns
|
|
GET_FLOAT(weapon_energy);
|
|
|
|
// add his secondary bank ammo
|
|
GET_DATA(ammo_count);
|
|
for(i=0; i<ammo_count; i++){
|
|
GET_SHORT(s_val);
|
|
|
|
if (i < MAX_SHIP_SECONDARY_BANKS) {
|
|
ammo[i] = s_val;
|
|
}
|
|
}
|
|
|
|
if (ammo_count > MAX_SHIP_SECONDARY_BANKS) {
|
|
ammo_count = MAX_SHIP_SECONDARY_BANKS;
|
|
}
|
|
|
|
// assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
|
|
// packet because of delay between reliable packet acknowledging my ingame ship and the start of these
|
|
// UDP client update packets. Only read this info if I have a ship.
|
|
if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
|
|
shipp = Player_ship;
|
|
objp = Player_obj;
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
fl_val = hull_percent * shipp->ship_max_hull_strength / 100.0f;
|
|
objp->hull_strength = fl_val;
|
|
|
|
float max_quad = shield_get_max_quad(objp);
|
|
for ( i = 0; i < n_quadrants; i++ ) {
|
|
fl_val = (shield_percent[i] * max_quad / 100.0f);
|
|
objp->shield_quadrant[i] = fl_val;
|
|
}
|
|
|
|
// for sanity, be sure that the number of susbystems that I read in matches the player. If not,
|
|
// then don't read these in.
|
|
if ( n_subsystems == sip->n_subsystems ) {
|
|
|
|
n_subsystems = 0; // reuse this variable
|
|
for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
|
|
int subsys_type;
|
|
|
|
fl_val = subsystem_percent[n_subsystems] * subsysp->max_hits / 100.0f;
|
|
subsysp->current_hits = fl_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 += fl_val;
|
|
}
|
|
n_subsystems++;
|
|
}
|
|
}
|
|
ship_recalc_subsys_strength( shipp );
|
|
|
|
shipp->weapon_energy = weapon_energy;
|
|
for(i=0; i<ammo_count; i++){
|
|
shipp->weapons.secondary_bank_ammo[i] = ammo[i];
|
|
}
|
|
|
|
// update my threat flags.
|
|
// temporarily commented out until tested.
|
|
Net_player->m_player->threat_flags = threats;
|
|
}
|
|
}
|
|
|
|
// get pl
|
|
int pl;
|
|
GET_INT(pl);
|
|
if(Net_player != NULL){
|
|
Net_player->cl_last_pl = pl;
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
// note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
|
|
if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
|
|
if( is_paused ) {
|
|
multi_pause_pause();
|
|
} else {
|
|
multi_pause_unpause();
|
|
}
|
|
}
|
|
}
|
|
|
|
void send_countdown_packet(int time)
|
|
{
|
|
ubyte data[20];
|
|
char val;
|
|
int packet_size = 0;
|
|
|
|
// build the header and add the time
|
|
BUILD_HEADER(COUNTDOWN);
|
|
val = (char)time;
|
|
ADD_DATA(val);
|
|
|
|
// if we're the server, we should broadcast to everyone
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
// otherwise we'de better be a host sending to the standalone
|
|
else {
|
|
Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_countdown_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
char time;
|
|
|
|
// get the time
|
|
GET_DATA(time);
|
|
PACKET_SET_SIZE();
|
|
|
|
// if we're not in the post sync data screen, ignore it
|
|
if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
|
|
return;
|
|
}
|
|
|
|
// if we're the standalone, this should be a -1 telling us to start the countdown
|
|
if(Game_mode & GM_STANDALONE_SERVER){
|
|
Assert((int)time == -1);
|
|
|
|
// start the countdown
|
|
multi_sync_start_countdown();
|
|
}
|
|
// otherwise if we're clients, just bash the countdown
|
|
else {
|
|
Multi_sync_countdown = (int)time;
|
|
}
|
|
}
|
|
|
|
// packets for debriefing information
|
|
void send_debrief_info( int stage_count[], int *stages[] )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size, i, j;
|
|
int i_tmp;
|
|
|
|
BUILD_HEADER(DEBRIEF_INFO);
|
|
|
|
// add the data for the teams
|
|
for ( i = 0; i < Num_teams; i++ ) {
|
|
int count;
|
|
|
|
count = stage_count[i];
|
|
ADD_INT( count );
|
|
for ( j = 0; j < count; j++ ) {
|
|
i_tmp = stages[i][j];
|
|
ADD_INT( i_tmp );
|
|
}
|
|
}
|
|
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
// process a debrief info packet from the server
|
|
void process_debrief_info( ubyte *data, header *hinfo )
|
|
{
|
|
int offset, i, j;
|
|
int stage_counts[MAX_TVT_TEAMS], active_stages[MAX_TVT_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TVT_TEAMS];
|
|
int i_tmp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
for ( i = 0; i < Num_teams; i++ ) {
|
|
int count;
|
|
|
|
GET_INT( count );
|
|
stage_counts[i] = count;
|
|
stages[i] = active_stages[i];
|
|
for ( j = 0; j < count; j++ ) {
|
|
GET_INT(i_tmp);
|
|
active_stages[i][j] = i_tmp;
|
|
}
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// now that we have the stage data for the debriefing stages, call debrief function with the
|
|
// data so that clients can now see the debriefing stuff. Do it only for my team.
|
|
Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
|
|
debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
|
|
}
|
|
|
|
constexpr ubyte HWIF_BIG_UPDATE = (1 << 0);
|
|
// sends homing information to all clients. We only need signature and num_missiles (because of hornets).
|
|
// sends homing_object and homing_subsystem to all clients.
|
|
void send_homing_weapon_info( int weapon_num )
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], flags = 0;
|
|
int s_index;
|
|
int packet_size;
|
|
object *homing_object;
|
|
ushort homing_signature;
|
|
weapon *wp;
|
|
|
|
wp = &Weapons[weapon_num];
|
|
|
|
// be sure that this weapon object is a homing object.
|
|
if ( !(Weapon_info[wp->weapon_info_index].is_homing()) )
|
|
return;
|
|
|
|
// default the subsystem
|
|
s_index = -1;
|
|
|
|
// get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
|
|
// homing signature.
|
|
homing_signature = 0;
|
|
homing_object = wp->homing_object;
|
|
|
|
if ( homing_object != &obj_used_list ) {
|
|
homing_signature = homing_object->net_signature;
|
|
|
|
// get the subsystem index.
|
|
if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
|
|
s_index = ship_get_subsys_index( wp->homing_subsys );
|
|
}
|
|
}
|
|
|
|
if (!wp->weapon_flags[Weapon::Weapon_Flags::Multi_Update_Sent] || IS_MAT_NULL(&Objects[wp->objnum].phys_info.ai_desired_orient)) {
|
|
flags |= HWIF_BIG_UPDATE;
|
|
wp->weapon_flags.set(Weapon::Weapon_Flags::Multi_Update_Sent);
|
|
}
|
|
|
|
BUILD_HEADER(HOMING_WEAPON_UPDATE);
|
|
ADD_DATA(flags);
|
|
ADD_USHORT( Objects[wp->objnum].net_signature );
|
|
ADD_USHORT( homing_signature );
|
|
ADD_SHORT( static_cast<short>(s_index) );
|
|
ADD_VECTOR( wp->homing_pos);
|
|
|
|
if (flags & HWIF_BIG_UPDATE) {
|
|
fix current_lifetime = Missiontime - wp->creation_time;
|
|
ADD_INT(current_lifetime);
|
|
ADD_VECTOR(Objects[wp->objnum].pos);
|
|
ADD_FLOAT(wp->launch_speed);
|
|
ADD_ORIENT(Objects[wp->objnum].orient);
|
|
}
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
// process a homing weapon info change packet. multiple_missiles parameter specifies is this
|
|
// packet contains information for multiple weapons (like hornets).
|
|
void process_homing_weapon_info( ubyte *data, header *hinfo )
|
|
{
|
|
ubyte flags;
|
|
int offset;
|
|
fix missile_lifetime = 0;
|
|
ushort weapon_signature, homing_signature;
|
|
short h_subsys;
|
|
float launch_speed = 0.0f;
|
|
vec3d missile_pos = vmd_zero_vector, homing_goal = vmd_zero_vector;
|
|
object *homing_object, *weapon_objp;
|
|
weapon *wp;
|
|
matrix orient_in = vmd_identity_matrix;
|
|
|
|
offset = HEADER_LENGTH;
|
|
|
|
// get the data for the packet
|
|
GET_DATA(flags);
|
|
GET_USHORT( weapon_signature );
|
|
GET_USHORT( homing_signature );
|
|
GET_SHORT( h_subsys );
|
|
GET_VECTOR( homing_goal );
|
|
|
|
if (flags & HWIF_BIG_UPDATE) {
|
|
GET_INT(missile_lifetime);
|
|
GET_VECTOR(missile_pos);
|
|
GET_FLOAT(launch_speed);
|
|
GET_ORIENT(orient_in);
|
|
}
|
|
PACKET_SET_SIZE();
|
|
|
|
// deal with changing this weapons homing information
|
|
weapon_objp = multi_get_network_object( weapon_signature );
|
|
|
|
if ( weapon_objp == nullptr ) {
|
|
nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
|
|
return;
|
|
}
|
|
|
|
Assert( weapon_objp->type == OBJ_WEAPON );
|
|
wp = &Weapons[weapon_objp->instance];
|
|
|
|
// be sure that we can find these weapons and
|
|
homing_object = multi_get_network_object( homing_signature );
|
|
|
|
if ( homing_object == nullptr ) {
|
|
wp->homing_object = &obj_used_list;
|
|
wp->homing_subsys = nullptr;
|
|
wp->target_num = -1;
|
|
wp->target_sig = -1;
|
|
|
|
nprintf(("Network", "Couldn't find homing object for homing update\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
if ( homing_object->type == OBJ_WEAPON ) {
|
|
auto flags_check = Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags;
|
|
|
|
if ( !((flags_check[Weapon::Info_Flags::Bomb, Weapon::Info_Flags::Cmeasure])) ) {
|
|
nprintf(("Network", "Homing object is invalid for homing update\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
wp->homing_object = homing_object;
|
|
wp->homing_subsys = nullptr;
|
|
wp->target_num = OBJ_INDEX(homing_object);
|
|
wp->target_sig = homing_object->signature;
|
|
wp->homing_pos = homing_goal;
|
|
|
|
if ( h_subsys != -1 ) {
|
|
Assert( homing_object->type == OBJ_SHIP );
|
|
wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
|
|
}
|
|
|
|
if (flags & HWIF_BIG_UPDATE) {
|
|
wp->creation_time = Missiontime + missile_lifetime;
|
|
weapon_objp->pos = missile_pos;
|
|
weapon_objp->orient = orient_in;
|
|
wp->launch_speed = launch_speed;
|
|
}
|
|
|
|
if ( homing_object->type == OBJ_SHIP ) {
|
|
nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
|
|
}
|
|
}
|
|
|
|
void send_emp_effect(ushort net_sig, float intensity, float time)
|
|
{
|
|
ubyte data[25];
|
|
int packet_size;
|
|
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
// build the packet and add the opcode
|
|
BUILD_HEADER(EMP_EFFECT);
|
|
ADD_USHORT(net_sig);
|
|
ADD_FLOAT(intensity);
|
|
ADD_FLOAT(time);
|
|
|
|
// send it to the player
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void process_emp_effect(ubyte *data, header *hinfo)
|
|
{
|
|
float intensity, time;
|
|
ushort net_sig;
|
|
object *objp;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
// read in the EMP effect data
|
|
GET_USHORT(net_sig);
|
|
GET_FLOAT(intensity);
|
|
GET_FLOAT(time);
|
|
PACKET_SET_SIZE();
|
|
|
|
// try and find the object
|
|
objp = multi_get_network_object(net_sig);
|
|
if((objp != NULL) && (objp->type == OBJ_SHIP)){
|
|
// if i'm not an observer and I have a valid ship, play the EMP effect
|
|
if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
|
|
emp_start_local(intensity, time);
|
|
}
|
|
|
|
// start the effect for the ship itself
|
|
emp_start_ship(objp, intensity, time);
|
|
}
|
|
}
|
|
|
|
// tells whether or not reinforcements are available
|
|
void send_reinforcement_avail( int rnum )
|
|
{
|
|
ubyte data[25];
|
|
int packet_size;
|
|
|
|
BUILD_HEADER(REINFORCEMENT_AVAIL);
|
|
ADD_INT( rnum );
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_reinforcement_avail( ubyte *data, header *hinfo )
|
|
{
|
|
int offset;
|
|
int rnum;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_INT( rnum );
|
|
PACKET_SET_SIZE();
|
|
|
|
// sanity check for a valid reinforcement number
|
|
if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
|
|
Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
|
|
}
|
|
}
|
|
|
|
// Sends a primary fire shot from any ship on the server, or as a backup packet type for the client
|
|
// if reference ship cannot be found.
|
|
void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
|
|
{
|
|
// in case nothing got fired, we don't have to do anything.
|
|
if(banks_fired <= 0){
|
|
return;
|
|
}
|
|
|
|
int packet_size, objnum;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
object *objp;
|
|
int np_index;
|
|
net_player *ignore = nullptr;
|
|
|
|
// get an object pointer for this ship.
|
|
objnum = shipp->objnum;
|
|
objp = &Objects[objnum];
|
|
|
|
// if i'm a multiplayer client, I should never send primary fired packets for anyone except me
|
|
if (MULTIPLAYER_CLIENT && (Player_obj != objp)){
|
|
return;
|
|
}
|
|
|
|
np_index = multi_find_player_by_net_signature(objp->net_signature);
|
|
if((np_index >= 0) && (np_index < MAX_PLAYERS)){
|
|
ignore = &Net_players[np_index];
|
|
}
|
|
|
|
// build up the standard weapon fired packet. This packet will get sent to all players if an AI
|
|
// ship fired the primary weapons. If a player fired the weapon, then this packet will get sent
|
|
// to every player but the guy who actullly fired the weapon. This method is used to help keep client
|
|
// and server in sync w.r.t. weapon energy for player ship
|
|
BUILD_HEADER( PRIMARY_FIRED_NEW );
|
|
ADD_USHORT(objp->net_signature);
|
|
|
|
// if I'm a server, broadcast to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all(data, packet_size, ignore);
|
|
|
|
// TEST CODE
|
|
multi_rate_add(1, "wfi", packet_size);
|
|
}
|
|
// otherwise just send to the server
|
|
else {
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
object* objp;
|
|
ushort shooter_sig;
|
|
|
|
// read all packet info
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(shooter_sig);
|
|
PACKET_SET_SIZE();
|
|
|
|
// find the object this fired packet is operating on
|
|
objp = multi_get_network_object( shooter_sig );
|
|
if ( objp == nullptr ) {
|
|
nprintf(("Network", "Could not find ship for fire primary packet NEW!\n"));
|
|
return;
|
|
}
|
|
// if this object is not actually a valid ship, don't do anything
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
// Juke - also check (objp->instance >= MAX_SHIPS)
|
|
if(objp->instance < 0 || objp->instance >= MAX_SHIPS){
|
|
return;
|
|
}
|
|
|
|
// if we're in client firing mode, ignore ones for myself
|
|
if((Player_obj != nullptr) && (Player_obj == objp)){
|
|
return;
|
|
}
|
|
|
|
ship_fire_primary( objp, 1 );
|
|
}
|
|
|
|
const int NON_HOMING_PACKET_MISSILE = (1 << 0);
|
|
|
|
// Cyborg17 -- send the relative position of my fire to my current target as well as the frametime.
|
|
// The whole goal of this is to create the weapon on the server that is heading at your target
|
|
// in the same exact way that it is on the client.
|
|
void send_non_homing_fired_packet(ship* shipp, int banks_or_number_of_missiles_fired, bool secondary)
|
|
{
|
|
Assertion(MULTIPLAYER_CLIENT, "Server is attempting to send the non_homing_fired_packet, which is client only. \n\nThis is a coder mistake. Please report!");
|
|
if (MULTIPLAYER_MASTER) {
|
|
return;
|
|
}
|
|
|
|
int packet_size, objnum;
|
|
ubyte data[MAX_PACKET_SIZE], flags = 0; // ubanks_fired, current_bank;
|
|
object* objp;
|
|
|
|
// just in case nothing got fired
|
|
if (banks_or_number_of_missiles_fired <= 0) {
|
|
return;
|
|
}
|
|
|
|
// get an object pointer for this ship.
|
|
objnum = shipp->objnum;
|
|
objp = &Objects[objnum];
|
|
|
|
// I should never send non-homing packets for anyone except me
|
|
if (Player_obj != objp) {
|
|
return;
|
|
}
|
|
|
|
object* ref_objp = multi_get_network_object(multi_client_lookup_ref_obj_net_sig());
|
|
if (ref_objp == nullptr || ref_objp->type != OBJ_SHIP) {
|
|
mprintf(("Unable to get accurate reference object for non-homing packet, because %s\n", (ref_objp == nullptr) ? "it was null." : "it was not a ship!"));
|
|
if (!secondary) {
|
|
send_NEW_primary_fired_packet(shipp, banks_or_number_of_missiles_fired);
|
|
}
|
|
return;
|
|
}
|
|
|
|
BUILD_HEADER(LINEAR_WEAPON_FIRED);
|
|
|
|
if (secondary) {
|
|
flags |= NON_HOMING_PACKET_MISSILE;
|
|
}
|
|
|
|
// Add the shooting object.
|
|
ADD_USHORT(objp->net_signature);
|
|
ADD_DATA(flags);
|
|
ADD_USHORT(ref_objp->net_signature);
|
|
|
|
// We need the time elpased, so send the last frame we got from the server and how much time has happened since then.
|
|
int last_received_frame = multi_client_lookup_frame_idx();
|
|
auto time_elapsed = static_cast<ushort>(timestamp_since(multi_client_lookup_frame_timestamp()));
|
|
|
|
ADD_INT(last_received_frame);
|
|
ADD_USHORT(time_elapsed);
|
|
|
|
// Save the transposed matrix so that we can optimize and use it twice.
|
|
matrix ref_ori;
|
|
vm_copy_transpose(&ref_ori, &ref_objp->orient);
|
|
|
|
vec3d ref_to_ship_vec, temp;
|
|
|
|
// Find the vector between the two objects and normalize.
|
|
float distance = vm_vec_normalized_dir(&temp, &objp->pos, &ref_objp->pos);
|
|
|
|
ADD_FLOAT(distance);
|
|
|
|
// unrotate via transposed matrix. This is an "unrotate", because the matrix has already been transposed.
|
|
// This finalized the relative position we will send to the server.
|
|
vm_vec_rotate(&ref_to_ship_vec, &temp, &ref_ori );
|
|
ADD_VECTOR(ref_to_ship_vec);
|
|
|
|
angles adjustment_angles, player_ship_angles;
|
|
|
|
// Save on bandwidth by changing to angles.
|
|
vm_extract_angles_matrix_alternate(&adjustment_angles, &ref_ori);
|
|
vm_extract_angles_matrix_alternate(&player_ship_angles, &objp->orient);
|
|
|
|
ADD_FLOAT(adjustment_angles.b);
|
|
ADD_FLOAT(adjustment_angles.h);
|
|
ADD_FLOAT(adjustment_angles.p);
|
|
ADD_FLOAT(player_ship_angles.b);
|
|
ADD_FLOAT(player_ship_angles.h);
|
|
ADD_FLOAT(player_ship_angles.p);
|
|
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
|
|
void process_non_homing_fired_packet(ubyte* data, header* hinfo)
|
|
{
|
|
int offset; // linked;
|
|
object* objp;
|
|
ushort shooter_sig;
|
|
ubyte flags;
|
|
bool secondary = false;
|
|
|
|
// read all packet info
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT(shooter_sig);
|
|
GET_DATA(flags);
|
|
|
|
// find the object this fired packet is operating on
|
|
objp = multi_get_network_object(shooter_sig);
|
|
|
|
if (flags & NON_HOMING_PACKET_MISSILE) {
|
|
secondary = true;
|
|
}
|
|
|
|
ushort time_elapsed;
|
|
ushort target_ref;
|
|
int client_frame;
|
|
angles adjustment_angles, player_ship_angles;
|
|
float distance;
|
|
vec3d ref_to_ship_vec;
|
|
|
|
//finish processing the packet
|
|
GET_USHORT(target_ref);
|
|
GET_INT(client_frame);
|
|
GET_USHORT(time_elapsed);
|
|
GET_FLOAT(distance);
|
|
GET_VECTOR(ref_to_ship_vec);
|
|
GET_FLOAT(adjustment_angles.b);
|
|
GET_FLOAT(adjustment_angles.h);
|
|
GET_FLOAT(adjustment_angles.p);
|
|
GET_FLOAT(player_ship_angles.b);
|
|
GET_FLOAT(player_ship_angles.h);
|
|
GET_FLOAT(player_ship_angles.p);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
if (objp == nullptr) {
|
|
nprintf(("Network", "Could not find ship for fire primary packet NEW!\n"));
|
|
return;
|
|
}
|
|
// if this object is not actually a valid ship, don't do anything
|
|
if (objp->type != OBJ_SHIP) {
|
|
return;
|
|
}
|
|
// Juke - also check (objp->instance >= MAX_SHIPS)
|
|
if (objp->instance < 0 || objp->instance >= MAX_SHIPS) {
|
|
return;
|
|
}
|
|
|
|
object* objp_ref = multi_get_network_object(target_ref);
|
|
|
|
if (objp_ref == nullptr || objp_ref->type != OBJ_SHIP) {
|
|
// new way failed, use the old new way.
|
|
|
|
if (objp_ref != nullptr){
|
|
mprintf(("Rollback's reference object was not a ship! It was a %d. Get Cyborg!!!\n", objp_ref->type));
|
|
}
|
|
|
|
if (secondary) {
|
|
// if this is a rollback shot from a dumbfire secondary, we have to mark this as a
|
|
// rollback shot so the client doesn't get an extra shot.
|
|
ship_fire_secondary(objp, 0, true);
|
|
}
|
|
else {
|
|
ship_fire_primary(objp, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// figure out correct start frame
|
|
int frame = multi_ship_record_find_frame(client_frame, static_cast<int>(time_elapsed));
|
|
|
|
if (frame > -1) {
|
|
// adjust time so that we can interpolate the position and orientation that was seen on the client.
|
|
int time_after_frame = multi_ship_record_find_time_after_frame(client_frame, frame, static_cast<int>(time_elapsed));
|
|
Assertion(time_after_frame >= 0, "Primary fire packet processor found an invalid time_after_frame of %d", time_after_frame);
|
|
|
|
vec3d new_tar_pos = multi_ship_record_lookup_position(objp_ref, frame);
|
|
matrix new_tar_ori = multi_ship_record_lookup_orientation(objp_ref, frame);
|
|
// find out where the angle to the new primary fire should be, by
|
|
// rotating the vector
|
|
|
|
vec3d temp_vec = ref_to_ship_vec;
|
|
|
|
// figure out the new position for the firing ship.
|
|
vm_vec_rotate(&ref_to_ship_vec, &temp_vec, &new_tar_ori);
|
|
|
|
// multiplay the distance back in
|
|
vm_vec_scale(&ref_to_ship_vec, distance);
|
|
|
|
// Finish finding the shot's starting position
|
|
vec3d new_player_pos;
|
|
vm_vec_add(&new_player_pos, &ref_to_ship_vec, &new_tar_pos);
|
|
|
|
// "decompress" the orientation matrix from the packet's angles.
|
|
matrix adjust_ship_matrix, old_player_ori;
|
|
vm_angles_2_matrix(&adjust_ship_matrix, &adjustment_angles);
|
|
vm_angles_2_matrix(&old_player_ori, &player_ship_angles);
|
|
|
|
// "decompress" in the second way. The firing ship orientation was also stored in the packet because
|
|
// it was unrotated on the client side using the target's old orientation.
|
|
matrix temp_ori;
|
|
vm_matrix_x_matrix(&temp_ori, &new_tar_ori, &adjust_ship_matrix);
|
|
vm_orthogonalize_matrix(&temp_ori);
|
|
|
|
// Now multiply the two matrices to find the orientation of the firing ship.
|
|
matrix new_player_ori;
|
|
vm_matrix_x_matrix(&new_player_ori, &old_player_ori, &temp_ori);
|
|
multi_ship_record_add_rollback_shot(objp, &new_player_pos, &new_player_ori, frame, secondary);
|
|
|
|
} // if the new way fails for some reason, use the old way.
|
|
else {
|
|
nprintf(("Network", "Rollback was not performed because the frame sent by the client is either too old or invalid.. Using the old system.\n"));
|
|
if (secondary) {
|
|
// if this is a rollback shot from a dumbfire secondary, we have to mark this as a
|
|
// rollback shot so the client doesn't get an extra missile for free.
|
|
ship_fire_secondary(objp, 0, true);
|
|
}
|
|
else {
|
|
ship_fire_primary(objp, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static constexpr size_t animation_direction_bit = 1 << 0;
|
|
static constexpr size_t animation_forced_bit = 1 << 1;
|
|
static constexpr size_t animation_instant_bit = 1 << 2;
|
|
static constexpr size_t animation_pause_bit = 1 << 3;
|
|
|
|
void send_animation_triggered_packet(unsigned int animationId, object* parent_object, ushort special_mode, const animation::ModelAnimationDirection& direction, bool force, bool instant, bool pause, const int* /*time*/) {
|
|
int packet_size;
|
|
ushort netsig_to_send = 0;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
|
|
Assertion(special_mode != 0 || parent_object != nullptr, "Tried to synchronize an animation with neither an attached object, nor a valid special object mode.");
|
|
if (special_mode == 0) {
|
|
if (parent_object == nullptr)
|
|
return;
|
|
|
|
netsig_to_send = parent_object->net_signature;
|
|
}
|
|
|
|
BUILD_HEADER(ANIMATION_TRIGGERED);
|
|
|
|
ADD_UINT(animationId);
|
|
ADD_USHORT(netsig_to_send);
|
|
ADD_USHORT(special_mode); // Currently empty. Reserved to find special pmi's for non-object animations
|
|
|
|
ubyte metadata = (direction == animation::ModelAnimationDirection::RWD ? animation_direction_bit : 0)
|
|
| (force ? animation_forced_bit : 0)
|
|
| (instant ? animation_instant_bit : 0)
|
|
| ( pause ? animation_pause_bit : 0);
|
|
ADD_DATA(metadata);
|
|
int actualTimestamp = 0; //If animation desync becomes a problem, send the ping-delay for animation rollback
|
|
ADD_INT(actualTimestamp);
|
|
|
|
// if I'm the server, send to everyone, else send to the server to be rebroadcasted
|
|
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_animation_triggered_packet(ubyte* data, header* hinfo) {
|
|
int offset; // linked;
|
|
unsigned int animationId;
|
|
ushort netsig, special_mode;
|
|
ubyte metadata;
|
|
int time;
|
|
|
|
// read all packet info
|
|
offset = HEADER_LENGTH;
|
|
GET_UINT(animationId);
|
|
GET_USHORT(netsig);
|
|
GET_USHORT(special_mode);
|
|
GET_DATA(metadata);
|
|
GET_INT(time);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
bool forced, instant, pause;
|
|
animation::ModelAnimationDirection direction;
|
|
direction = (metadata & (animation_direction_bit)) ? animation::ModelAnimationDirection::RWD : animation::ModelAnimationDirection::FWD;
|
|
forced = metadata & (animation_forced_bit);
|
|
instant = metadata & (animation_instant_bit);
|
|
pause = metadata & (animation_pause_bit);
|
|
|
|
float delay = time * 0.001f;
|
|
|
|
object* objp = multi_get_network_object(netsig);
|
|
|
|
if (objp == nullptr && special_mode == 0) //This means there is neither a good object, nor a known special mode. Don't even send the animation trigger on, just abort.
|
|
return;
|
|
|
|
auto animation = animation::ModelAnimationSet::s_animationById.find(animationId);
|
|
if (animation != animation::ModelAnimationSet::s_animationById.end()) {
|
|
if (special_mode == 0) {
|
|
//with the above exit condition, this guarantees a non-null objp
|
|
int model_instance_num = object_get_model_instance_num(objp);
|
|
if (model_instance_num >= 0)
|
|
animation->second->start(model_get_instance(model_instance_num), direction, forced, instant, pause, &delay);
|
|
}
|
|
else {
|
|
//Currently empty. Reserved to find special pmi's for non-object animations
|
|
}
|
|
}
|
|
|
|
//Need to broadcast back to other clients
|
|
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
|
|
send_animation_triggered_packet(animationId, objp, special_mode, direction, forced, instant, pause, &time);
|
|
}
|
|
}
|
|
|
|
void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size;
|
|
int np_index;
|
|
net_player *ignore = NULL;
|
|
|
|
// if i'm a multiplayer client, I should never send primary fired packets for anyone except me
|
|
if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
|
|
return;
|
|
}
|
|
|
|
Assert ( cmeasure_count < UCHAR_MAX );
|
|
BUILD_HEADER(COUNTERMEASURE_NEW);
|
|
ADD_USHORT( objp->net_signature );
|
|
ADD_INT( rand_val );
|
|
|
|
nprintf(("Network","Sending NEW countermeasure packet!\n"));
|
|
|
|
// determine if its a player
|
|
if(MULTIPLAYER_MASTER){
|
|
np_index = multi_find_player_by_net_signature(objp->net_signature);
|
|
if((np_index >= 0) && (np_index < MAX_PLAYERS)){
|
|
ignore = &Net_players[np_index];
|
|
}
|
|
}
|
|
|
|
// if I'm the server, send to all players
|
|
if(MULTIPLAYER_MASTER){
|
|
multi_io_send_to_all(data, packet_size, ignore);
|
|
}
|
|
// otherwise send to the server
|
|
else {
|
|
multi_io_send(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
ushort signature;
|
|
int rand_val;
|
|
object *objp;
|
|
|
|
offset = HEADER_LENGTH;
|
|
GET_USHORT( signature );
|
|
GET_INT( rand_val );
|
|
PACKET_SET_SIZE();
|
|
|
|
objp = multi_get_network_object( signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
|
|
return;
|
|
}
|
|
if(objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
|
|
// if we're in client firing mode, ignore ones for myself
|
|
if((Player_obj != NULL) && (Player_obj == objp)){
|
|
return;
|
|
}
|
|
|
|
if ( (rand_val >= NPERM_SIG_MIN) && (rand_val <= NPERM_SIG_MAX) ) {
|
|
multi_set_network_signature((ushort)rand_val, MULTI_SIG_NON_PERMANENT);
|
|
}
|
|
|
|
// make it so ship can fire right away!
|
|
Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
|
|
if ( objp == Player_obj ){
|
|
nprintf(("network", "firing countermeasure from my ship\n"));
|
|
}
|
|
ship_launch_countermeasure( objp, rand_val );
|
|
}
|
|
|
|
void send_beam_fired_packet(const beam_fire_info *fire_info, const beam_info *override)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], val;
|
|
int packet_size = 0;
|
|
short u_beam_info;
|
|
int shooter_subsys_index = -1, target_subsys_index = -1;
|
|
ushort target_sig, shooter_sig;
|
|
|
|
// only the server should ever be doing this
|
|
Assert(MULTIPLAYER_MASTER);
|
|
|
|
// setup outgoing data
|
|
Assert(override != nullptr);
|
|
|
|
if (override == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (fire_info->bfi_flags & BFIF_IS_FIGHTER_BEAM) {
|
|
Assertion(fire_info->bank >= 0, "Bank for fighter BEAM is invalid!");
|
|
Assertion(fire_info->point >= 0, "Bank point for fighter BEAM is invalid!");
|
|
|
|
if ( (fire_info->bank < 0) || (fire_info->point < 0) ) {
|
|
return;
|
|
}
|
|
} else if (fire_info->shooter && fire_info->turret) {
|
|
shooter_subsys_index = ship_get_subsys_index(fire_info->turret);
|
|
|
|
Assertion(shooter_subsys_index >= 0, "BEAM fired from unknown subsystem!");
|
|
Assertion(shooter_subsys_index < SHRT_MAX, "BEAM fired from a subsystem beyond max limits!");
|
|
|
|
if (shooter_subsys_index < 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
shooter_sig = (fire_info->shooter) ? fire_info->shooter->net_signature : 0;
|
|
target_sig = (fire_info->target) ? fire_info->target->net_signature : 0;
|
|
|
|
if (fire_info->target && fire_info->target_subsys) {
|
|
target_subsys_index = ship_get_subsys_index(fire_info->target_subsys);
|
|
}
|
|
|
|
u_beam_info = static_cast<short>(fire_info->beam_info_index);
|
|
|
|
// build the header
|
|
BUILD_HEADER(BEAM_FIRED);
|
|
|
|
ADD_USHORT(shooter_sig);
|
|
ADD_USHORT(target_sig);
|
|
ADD_SHORT(u_beam_info);
|
|
ADD_INT(fire_info->bfi_flags);
|
|
ADD_SHORT(static_cast<short>(shooter_subsys_index));
|
|
ADD_SHORT(static_cast<short>(target_subsys_index));
|
|
|
|
val = static_cast<ubyte>(fire_info->fire_method);
|
|
ADD_DATA(val);
|
|
|
|
if (fire_info->bfi_flags & BFIF_IS_FIGHTER_BEAM) {
|
|
ADD_SHORT(static_cast<short>(fire_info->bank));
|
|
ADD_SHORT(static_cast<short>(fire_info->point));
|
|
}
|
|
|
|
// override info
|
|
{
|
|
// dir_a
|
|
ADD_FLOAT(override->dir_a.xyz.x);
|
|
ADD_FLOAT(override->dir_a.xyz.y);
|
|
ADD_FLOAT(override->dir_a.xyz.z);
|
|
// dir_b
|
|
ADD_FLOAT(override->dir_b.xyz.x);
|
|
ADD_FLOAT(override->dir_b.xyz.y);
|
|
ADD_FLOAT(override->dir_b.xyz.z);
|
|
// rot_axis
|
|
ADD_FLOAT(override->rot_axis.xyz.x);
|
|
ADD_FLOAT(override->rot_axis.xyz.y);
|
|
ADD_FLOAT(override->rot_axis.xyz.z);
|
|
|
|
ADD_DATA(override->shot_count);
|
|
|
|
for (auto i = 0; i < override->shot_count; i++) {
|
|
ADD_FLOAT(override->shot_aim[i]);
|
|
}
|
|
}
|
|
|
|
if (fire_info->bfi_flags & BFIF_TARGETING_COORDS) {
|
|
// target 1
|
|
ADD_FLOAT(fire_info->target_pos1.xyz.x);
|
|
ADD_FLOAT(fire_info->target_pos1.xyz.y);
|
|
ADD_FLOAT(fire_info->target_pos1.xyz.z);
|
|
// target 2
|
|
ADD_FLOAT(fire_info->target_pos2.xyz.x);
|
|
ADD_FLOAT(fire_info->target_pos2.xyz.y);
|
|
ADD_FLOAT(fire_info->target_pos2.xyz.z);
|
|
}
|
|
|
|
if (fire_info->bfi_flags & BFIF_FLOATING_BEAM) {
|
|
ADD_FLOAT(fire_info->starting_pos.xyz.x);
|
|
ADD_FLOAT(fire_info->starting_pos.xyz.y);
|
|
ADD_FLOAT(fire_info->starting_pos.xyz.z);
|
|
ADD_DATA(fire_info->team);
|
|
}
|
|
|
|
// send to all clients
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_beam_fired_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int i, offset;
|
|
ushort shooter_sig, target_sig;
|
|
short shooter_subsys_index, target_subsys_index;
|
|
short u_beam_info;
|
|
beam_info b_info;
|
|
beam_fire_info fire_info;
|
|
float shot_aim;
|
|
short bank = -1, point = -1;
|
|
ubyte val;
|
|
|
|
// only clients should ever get this
|
|
Assert(MULTIPLAYER_CLIENT);
|
|
|
|
memset(&fire_info, 0, sizeof(beam_fire_info));
|
|
|
|
// read in packet data
|
|
offset = HEADER_LENGTH;
|
|
|
|
GET_USHORT(shooter_sig);
|
|
GET_USHORT(target_sig);
|
|
GET_SHORT(u_beam_info);
|
|
GET_INT(fire_info.bfi_flags);
|
|
GET_SHORT(shooter_subsys_index);
|
|
GET_SHORT(target_subsys_index);
|
|
|
|
GET_DATA(val);
|
|
fire_info.fire_method = val;
|
|
|
|
if (fire_info.bfi_flags & BFIF_IS_FIGHTER_BEAM) {
|
|
GET_SHORT(bank);
|
|
GET_SHORT(point);
|
|
}
|
|
|
|
// override info
|
|
{
|
|
// dir_a
|
|
GET_FLOAT(b_info.dir_a.xyz.x);
|
|
GET_FLOAT(b_info.dir_a.xyz.y);
|
|
GET_FLOAT(b_info.dir_a.xyz.z);
|
|
// dir_b
|
|
GET_FLOAT(b_info.dir_b.xyz.x);
|
|
GET_FLOAT(b_info.dir_b.xyz.y);
|
|
GET_FLOAT(b_info.dir_b.xyz.z);
|
|
// rot_axis
|
|
GET_FLOAT(b_info.rot_axis.xyz.x);
|
|
GET_FLOAT(b_info.rot_axis.xyz.y);
|
|
GET_FLOAT(b_info.rot_axis.xyz.z);
|
|
|
|
GET_DATA(b_info.shot_count);
|
|
|
|
for (i = 0; i < b_info.shot_count; i++) {
|
|
GET_FLOAT(shot_aim);
|
|
|
|
if (i < MAX_BEAM_SHOTS) {
|
|
b_info.shot_aim[i] = shot_aim;
|
|
}
|
|
}
|
|
|
|
fire_info.beam_info_override = &b_info;
|
|
}
|
|
|
|
if (fire_info.bfi_flags & BFIF_TARGETING_COORDS) {
|
|
// target 1
|
|
GET_FLOAT(fire_info.target_pos1.xyz.x);
|
|
GET_FLOAT(fire_info.target_pos1.xyz.y);
|
|
GET_FLOAT(fire_info.target_pos1.xyz.z);
|
|
// target 2
|
|
GET_FLOAT(fire_info.target_pos2.xyz.x);
|
|
GET_FLOAT(fire_info.target_pos2.xyz.y);
|
|
GET_FLOAT(fire_info.target_pos2.xyz.z);
|
|
}
|
|
|
|
if (fire_info.bfi_flags & BFIF_FLOATING_BEAM) {
|
|
GET_FLOAT(fire_info.starting_pos.xyz.x);
|
|
GET_FLOAT(fire_info.starting_pos.xyz.y);
|
|
GET_FLOAT(fire_info.starting_pos.xyz.z);
|
|
GET_DATA(fire_info.team);
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
fire_info.beam_info_index = u_beam_info;
|
|
fire_info.shooter = multi_get_network_object(shooter_sig);
|
|
fire_info.target = multi_get_network_object(target_sig);
|
|
fire_info.burst_index = 0;
|
|
|
|
if ( fire_info.target && (target_subsys_index >= 0) ) {
|
|
ship *targetp = &Ships[fire_info.target->instance];
|
|
fire_info.target_subsys = ship_get_indexed_subsys(targetp, target_subsys_index);
|
|
}
|
|
|
|
if ( fire_info.shooter && (fire_info.shooter->type == OBJ_SHIP) ) {
|
|
ship *shipp = &Ships[fire_info.shooter->instance];
|
|
|
|
if (fire_info.bfi_flags & BFIF_IS_FIGHTER_BEAM) {
|
|
Assertion(bank >= 0, "Fighter BEAM bank is invalid!");
|
|
Assertion(point >= 0, "Fighter BEAM point is invalid!");
|
|
|
|
if ( (bank < 0) || (point < 0) ) {
|
|
nprintf(("Network", "Couldn't get firing point for fighter BEAM weapon!\n"));
|
|
return;
|
|
}
|
|
|
|
polymodel *pm = model_get( Ship_info[shipp->ship_info_index].model_num );
|
|
float field_of_fire = Weapon_info[fire_info.beam_info_index].field_of_fire;
|
|
|
|
fire_info.local_fire_postion = pm->gun_banks[bank].pnt[point];
|
|
|
|
shipp->beam_sys_info.turret_norm.xyz.x = 0.0f;
|
|
shipp->beam_sys_info.turret_norm.xyz.y = 0.0f;
|
|
shipp->beam_sys_info.turret_norm.xyz.z = 1.0f;
|
|
shipp->beam_sys_info.model_num = Ship_info[shipp->ship_info_index].model_num;
|
|
shipp->beam_sys_info.turret_gun_sobj = pm->detail[0];
|
|
shipp->beam_sys_info.turret_num_firing_points = 1;
|
|
shipp->beam_sys_info.turret_fov = cosf(fl_radians((field_of_fire != 0.0f) ? field_of_fire : 180.0f) / 2.0f);
|
|
shipp->beam_sys_info.pnt = fire_info.local_fire_postion;
|
|
shipp->beam_sys_info.turret_firing_point[0] = fire_info.local_fire_postion;
|
|
|
|
shipp->fighter_beam_turret_data.disruption_timestamp = timestamp(0);
|
|
shipp->fighter_beam_turret_data.turret_next_fire_pos = 0;
|
|
shipp->fighter_beam_turret_data.current_hits = 1.0;
|
|
shipp->fighter_beam_turret_data.system_info = &shipp->beam_sys_info;
|
|
|
|
fire_info.turret = &shipp->fighter_beam_turret_data;
|
|
fire_info.bank = bank;
|
|
fire_info.point = point;
|
|
} else if (shooter_subsys_index >= 0) {
|
|
fire_info.turret = ship_get_indexed_subsys(shipp, shooter_subsys_index);
|
|
}
|
|
}
|
|
|
|
// fire da beam
|
|
beam_fire(&fire_info);
|
|
}
|
|
|
|
void send_sw_query_packet(ubyte code, char *txt)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
// build the packet and add the code
|
|
BUILD_HEADER(SW_STD_QUERY);
|
|
ADD_DATA(code);
|
|
if((code == SW_STD_START) || (code == SW_STD_BAD)){
|
|
ADD_STRING(txt);
|
|
}
|
|
|
|
// if I'm the host, send to standalone
|
|
if(MULTIPLAYER_HOST){
|
|
Assert(!MULTIPLAYER_MASTER);
|
|
Assert(code == SW_STD_START);
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
// otherwise standalone sends back to host
|
|
else {
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
Assert(code != SW_STD_START);
|
|
Assert(Netgame.host != NULL);
|
|
if(Netgame.host != NULL){
|
|
multi_io_send_reliable(Netgame.host, data, packet_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void process_sw_query_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ubyte code;
|
|
char txt[MAX_SQUAD_RESPONSE_LEN+1];
|
|
|
|
GET_DATA(code);
|
|
|
|
if ( (code == SW_STD_START) || (code == SW_STD_BAD) ) {
|
|
GET_STRING(txt);
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
// to host from standalone
|
|
if (MULTIPLAYER_HOST) {
|
|
Assert( !MULTIPLAYER_MASTER );
|
|
|
|
if (code == SW_STD_OK) {
|
|
Multi_sw_std_query = 1;
|
|
} else {
|
|
Assert(code == SW_STD_BAD);
|
|
|
|
SDL_strlcpy(Multi_sw_bad_reply, txt, SDL_arraysize(Multi_sw_bad_reply));
|
|
Multi_sw_std_query = 0;
|
|
}
|
|
}
|
|
// to standalone from host
|
|
else {
|
|
Assert(Game_mode & GM_STANDALONE_SERVER);
|
|
Assert(code == SW_STD_START);
|
|
|
|
multi_sw_std_query(txt);
|
|
}
|
|
}
|
|
|
|
void send_event_update_packet(int event)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
ushort u_event = (ushort)event;
|
|
int packet_size = 0;
|
|
|
|
// build the header and add the event
|
|
BUILD_HEADER(EVENT_UPDATE);
|
|
ADD_USHORT(u_event);
|
|
ADD_INT(Mission_events[event].flags);
|
|
ADD_INT(Mission_events[event].formula);
|
|
ADD_INT(Mission_events[event].result);
|
|
ADD_INT(Mission_events[event].count);
|
|
|
|
// send to all players
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_event_update_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
int store_flags;
|
|
ushort u_event;
|
|
|
|
// get the data
|
|
GET_USHORT(u_event);
|
|
store_flags = Mission_events[u_event].flags;
|
|
GET_INT(Mission_events[u_event].flags);
|
|
GET_INT(Mission_events[u_event].formula);
|
|
GET_INT(Mission_events[u_event].result);
|
|
GET_INT(Mission_events[u_event].count);
|
|
PACKET_SET_SIZE();
|
|
|
|
// went from non directive special to directive special
|
|
if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
|
|
mission_event_set_directive_special(u_event);
|
|
}
|
|
// if we went directive special to non directive special
|
|
else if((store_flags & MEF_DIRECTIVE_SPECIAL) && !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
|
|
mission_event_unset_directive_special(u_event);
|
|
}
|
|
|
|
if (Mission_events[u_event].result && !Mission_events[u_event].satisfied_time.isValid()) {
|
|
Mission_events[u_event].satisfied_time = _timestamp();
|
|
if ( !Mission_events[u_event].objective_text.empty() ) {
|
|
mission_event_set_completion_sound_timestamp();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Karajorma - Sends a packet to all clients telling them that a SEXP variable has changed its value
|
|
void send_variable_update_packet(int variable_index, char *value)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
if(Net_player == NULL){
|
|
return;
|
|
}
|
|
if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
|
|
return;
|
|
}
|
|
|
|
// build the packet and add the data
|
|
BUILD_HEADER(VARIABLE_UPDATE);
|
|
ADD_INT(variable_index);
|
|
ADD_STRING(value);
|
|
|
|
if (MULTIPLAYER_MASTER) {
|
|
// send to all players
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
else {
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
}
|
|
|
|
void process_variable_update_packet( ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
char value[TOKEN_LENGTH];
|
|
int variable_index;
|
|
|
|
// get the data
|
|
GET_INT(variable_index);
|
|
GET_STRING(value);
|
|
PACKET_SET_SIZE();
|
|
|
|
// set the sexp_variable
|
|
if ( (variable_index >= 0) && (variable_index < sexp_variable_count()) )
|
|
{
|
|
strcpy_s(Sexp_variables[variable_index].text, value);
|
|
}
|
|
|
|
// send the packet on to all clients.
|
|
if (MULTIPLAYER_MASTER) {
|
|
send_variable_update_packet(variable_index, Sexp_variables[variable_index].text);
|
|
}
|
|
}
|
|
|
|
// weapon detonate packet
|
|
void send_weapon_detonate_packet(object *objp)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
// sanity checks
|
|
Assert(MULTIPLAYER_MASTER);
|
|
if(!MULTIPLAYER_MASTER){
|
|
return;
|
|
}
|
|
Assert(objp != NULL);
|
|
if(objp == NULL){
|
|
return;
|
|
}
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(WEAPON_DET);
|
|
ADD_USHORT(objp->net_signature);
|
|
|
|
// send to all players
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void process_weapon_detonate_packet(ubyte *data, header *hinfo)
|
|
{
|
|
ushort net_sig;
|
|
int offset = HEADER_LENGTH;
|
|
object *objp = NULL;
|
|
|
|
// get the weapon signature
|
|
GET_USHORT(net_sig);
|
|
PACKET_SET_SIZE();
|
|
|
|
// lookup the weapon
|
|
objp = multi_get_network_object(net_sig);
|
|
if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
|
|
weapon_detonate(objp);
|
|
}
|
|
}
|
|
|
|
// flak fired packet
|
|
void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range, float dist_to_target, float target_radius)
|
|
{
|
|
int packet_size;
|
|
ushort pnet_signature;
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
object *objp;
|
|
ship_subsys *ssp;
|
|
|
|
// sanity
|
|
if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
|
|
return;
|
|
}
|
|
|
|
// local setup -- be sure we are actually passing a weapon!!!!
|
|
objp = &Objects[weapon_objnum];
|
|
Assert ( objp->type == OBJ_WEAPON );
|
|
pnet_signature = Objects[ship_objnum].net_signature;
|
|
|
|
ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index );
|
|
if(ssp == NULL){
|
|
return;
|
|
}
|
|
|
|
// build the fire turret packet.
|
|
BUILD_HEADER(FLAK_FIRED);
|
|
packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.vec.fvec);
|
|
ADD_USHORT( pnet_signature );
|
|
ADD_SHORT( static_cast<short>(Weapons[Objects[weapon_objnum].instance].weapon_info_index) );
|
|
ADD_SHORT( static_cast<short>(subsys_index) );
|
|
|
|
// where we might accidentally dereference nullptrs, send 0 instead
|
|
constexpr float ZERO_VALUE = 0.0f;
|
|
|
|
// ensure a nullptr is not dereferenced for the next two values.
|
|
if (ssp->submodel_instance_1 != nullptr) {
|
|
ADD_FLOAT( ssp->submodel_instance_1->cur_angle );
|
|
}
|
|
else {
|
|
ADD_FLOAT( ZERO_VALUE );
|
|
}
|
|
|
|
if (ssp->submodel_instance_2 != nullptr) {
|
|
ADD_FLOAT( ssp->submodel_instance_2->cur_angle );
|
|
}
|
|
else {
|
|
ADD_FLOAT(ZERO_VALUE);
|
|
}
|
|
ADD_FLOAT( flak_range );
|
|
|
|
ADD_FLOAT( dist_to_target );
|
|
|
|
ADD_FLOAT( target_radius );
|
|
|
|
multi_io_send_to_all(data, packet_size);
|
|
|
|
multi_rate_add(1, "flk", packet_size);
|
|
}
|
|
|
|
void process_flak_fired_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset, weapon_objnum;
|
|
ushort pnet_signature;
|
|
vec3d pos, dir;
|
|
matrix orient;
|
|
vec3d o_fvec;
|
|
short turret_index, wid;
|
|
object *objp;
|
|
ship_subsys *ssp;
|
|
ship *shipp;
|
|
float angle1, angle2;
|
|
float flak_range;
|
|
float dist_to_target;
|
|
float target_radius;
|
|
|
|
// get the data for the turret fired packet
|
|
offset = HEADER_LENGTH;
|
|
offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
|
|
GET_USHORT( pnet_signature );
|
|
GET_SHORT( wid );
|
|
GET_SHORT( turret_index );
|
|
GET_FLOAT( angle1 );
|
|
GET_FLOAT( angle2 );
|
|
GET_FLOAT( flak_range );
|
|
GET_FLOAT( dist_to_target );
|
|
GET_FLOAT( target_radius );
|
|
PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
|
|
|
|
// if we don't have a valid weapon index then bail
|
|
if ( (wid < 0) || (wid >= weapon_info_size()) || !(Weapon_info[wid].wi_flags[Weapon::Info_Flags::Flak]) ) {
|
|
return;
|
|
}
|
|
|
|
// find the object
|
|
objp = multi_get_network_object( pnet_signature );
|
|
if ( objp == NULL ) {
|
|
nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
|
|
return;
|
|
}
|
|
|
|
// if this isn't a ship, do nothing
|
|
if ( objp->type != OBJ_SHIP ){
|
|
return;
|
|
}
|
|
|
|
// make an orientation matrix from the o_fvec
|
|
vm_vector_2_matrix_norm(&orient, &o_fvec, nullptr, nullptr);
|
|
|
|
// find this turret, and set the position of the turret that just fired to be where it fired. Quite a
|
|
// hack, but should be suitable.
|
|
shipp = &Ships[objp->instance];
|
|
ssp = ship_get_indexed_subsys( shipp, turret_index );
|
|
if(ssp == NULL){
|
|
return;
|
|
}
|
|
|
|
// bash the position and orientation of the turret, if it's not a nulltpr
|
|
if (ssp->submodel_instance_1 != nullptr) {
|
|
ssp->submodel_instance_1->cur_angle = angle1;
|
|
}
|
|
|
|
if (ssp->submodel_instance_2 != nullptr) {
|
|
ssp->submodel_instance_2->cur_angle = angle2;
|
|
}
|
|
|
|
// get the world position of the weapon
|
|
ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
|
|
|
|
auto launch_curve_data = WeaponLaunchCurveData {
|
|
ssp->system_info->turret_num_firing_points,
|
|
dist_to_target,
|
|
target_radius,
|
|
};
|
|
|
|
// create the weapon object
|
|
weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), -1, true, false, 0.0f, ssp, launch_curve_data);
|
|
if (weapon_objnum != -1) {
|
|
const weapon_info& wip = Weapon_info[wid];
|
|
if ( wip.launch_snd.isValid() ) {
|
|
snd_play_3d( gamesnd_get_game_sound(Weapon_info[wid].launch_snd), &pos, &View_position );
|
|
}
|
|
|
|
object& wp_obj = Objects[weapon_objnum];
|
|
const weapon& wp = Weapons[wp_obj.instance];
|
|
|
|
if (wip.muzzle_effect.isValid()) {
|
|
float radius_mult = 1.f;
|
|
if (wip.render_type == WRT_LASER) {
|
|
radius_mult = wip.weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_RADIUS_MULT, wp, &wp.modular_curves_instance);
|
|
}
|
|
//spawn particle effect
|
|
auto particleSource = particle::ParticleManager::get()->createSource(wip.muzzle_effect);
|
|
//This could potentially be attached to the ship, but might look weird if the spawn position of the weapon is ever interpolated away from the ship's barrel.
|
|
particleSource->setHost(make_unique<EffectHostVector>(pos, orient, objp->phys_info.vel));
|
|
particleSource->setTriggerRadius(wp_obj.radius * radius_mult);
|
|
particleSource->setTriggerVelocity(vm_vec_mag_quick(&wp_obj.phys_info.vel));
|
|
particleSource->finishCreation();
|
|
}
|
|
|
|
// set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
|
|
flak_set_range(&wp_obj, (float)flak_range);
|
|
}
|
|
}
|
|
|
|
#define ADD_NORM_VEC(d) do { Assert((packet_size + 3) < MAX_PACKET_SIZE); char vnorm[3] = { (char)(d.x * 127.0f), (char)(d.y * 127.0f), (char)(d.z * 127.0f) }; memcpy(data + packet_size, vnorm, 3); packet_size += 3; } while(false);
|
|
#define GET_NORM_VEC(d) do { char vnorm[3]; memcpy(vnorm, data+offset, 3); d.x = (float)vnorm[0] / 127.0f; d.y = (float)vnorm[1] / 127.0f; d.z = (float)vnorm[2] / 127.0f; } while(false);
|
|
|
|
// player pain packet
|
|
void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, const vec3d *force, const vec3d *hitpos, int quadrant_num)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
short windex;
|
|
ushort udamage;
|
|
int packet_size = 0;
|
|
|
|
Assert(MULTIPLAYER_MASTER);
|
|
if(!MULTIPLAYER_MASTER){
|
|
return;
|
|
}
|
|
Assert(pl != NULL);
|
|
if(pl == NULL){
|
|
return;
|
|
}
|
|
|
|
// build the packet and add the code
|
|
BUILD_HEADER(NETPLAYER_PAIN);
|
|
windex = (short)weapon_info_index;
|
|
ADD_SHORT(windex);
|
|
udamage = (ushort)damage;
|
|
ADD_USHORT(udamage);
|
|
ADD_VECTOR((*force));
|
|
ADD_VECTOR((*hitpos));
|
|
ADD_INT(quadrant_num);
|
|
|
|
// send to the player
|
|
multi_io_send(pl, data, packet_size);
|
|
|
|
multi_rate_add(1, "pai", packet_size);
|
|
}
|
|
|
|
void process_player_pain_packet(const ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
short windex = 0;
|
|
ushort udamage;
|
|
vec3d force;
|
|
vec3d hit_pos;
|
|
weapon_info *wip;
|
|
int quadrant_num;
|
|
|
|
// get the data for the pain packet
|
|
offset = HEADER_LENGTH;
|
|
GET_SHORT(windex);
|
|
GET_USHORT(udamage);
|
|
GET_VECTOR(force);
|
|
GET_VECTOR(hit_pos);
|
|
GET_INT(quadrant_num);
|
|
PACKET_SET_SIZE();
|
|
|
|
// mprintf(("PAIN!\n"));
|
|
|
|
// get weapon info pointer
|
|
Assert((windex < weapon_info_size()) && (Weapon_info[windex].subtype == WP_LASER));
|
|
if(! ((windex < weapon_info_size()) && (Weapon_info[windex].subtype == WP_LASER)) ){
|
|
return;
|
|
}
|
|
wip = &Weapon_info[windex];
|
|
|
|
// play the weapon hit sound
|
|
Assert(Player_obj != NULL);
|
|
if(Player_obj == NULL){
|
|
return;
|
|
}
|
|
|
|
//Assume the weapon is armed -WMC
|
|
weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos, true, quadrant_num);
|
|
|
|
// we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
|
|
ship_hit_pain((float)udamage, quadrant_num);
|
|
|
|
// apply the whack
|
|
ship_apply_whack(&force, &hit_pos, Player_obj);
|
|
}
|
|
|
|
// lightning packet
|
|
void send_lightning_packet(int bolt_type_internal, vec3d *start, vec3d *strike)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
char val;
|
|
int packet_size = 0;
|
|
|
|
// build the header and add the data
|
|
BUILD_HEADER(LIGHTNING_PACKET);
|
|
val = (char)bolt_type_internal;
|
|
ADD_DATA(val);
|
|
ADD_VECTOR((*start));
|
|
ADD_VECTOR((*strike));
|
|
|
|
// send to everyone unreliable for now
|
|
multi_io_send_to_all(data, packet_size);
|
|
}
|
|
|
|
void process_lightning_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset;
|
|
char bolt_type_internal;
|
|
vec3d start, strike;
|
|
|
|
// read the data
|
|
offset = HEADER_LENGTH;
|
|
GET_DATA(bolt_type_internal);
|
|
GET_VECTOR(start);
|
|
GET_VECTOR(strike);
|
|
PACKET_SET_SIZE();
|
|
|
|
// invalid bolt?
|
|
if(bolt_type_internal < 0){
|
|
return;
|
|
}
|
|
|
|
// fire it up
|
|
nebl_bolt(bolt_type_internal, &start, &strike);
|
|
}
|
|
|
|
void send_bytes_recvd_packet(net_player *pl)
|
|
{
|
|
// only clients should ever be doing this
|
|
if(pl == NULL){
|
|
return;
|
|
}
|
|
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
BUILD_HEADER(BYTES_SENT);
|
|
ADD_INT(pl->cl_bytes_recvd);
|
|
|
|
// send to the server
|
|
multi_io_send_reliable(pl, data, packet_size);
|
|
}
|
|
|
|
void process_bytes_recvd_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int bytes;
|
|
int pid;
|
|
net_player *pl = NULL;
|
|
int offset = HEADER_LENGTH;
|
|
|
|
GET_INT(bytes);
|
|
PACKET_SET_SIZE();
|
|
|
|
// not server?
|
|
if(Net_player == NULL){
|
|
return;
|
|
}
|
|
if(!MULTIPLAYER_MASTER){
|
|
return;
|
|
}
|
|
|
|
// make sure we know what player sent this
|
|
pid = find_player_index(hinfo->id);
|
|
if((pid < 0) || (pid >= MAX_PLAYERS)){
|
|
return;
|
|
}
|
|
pl = &Net_players[pid];
|
|
|
|
// compute his pl
|
|
pl->cl_bytes_recvd = bytes;
|
|
if(bytes < 0){
|
|
return;
|
|
}
|
|
pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
|
|
|
|
// reset bytes sent
|
|
pl->sv_bytes_sent = 0;
|
|
}
|
|
|
|
// host transfer
|
|
void send_host_captain_change_packet(short player_id, bool captain_change)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE], val;
|
|
int packet_size = 0;
|
|
|
|
// build the packet
|
|
BUILD_HEADER(TRANSFER_HOST);
|
|
ADD_SHORT(player_id);
|
|
val = captain_change ? 1 : 0;
|
|
ADD_DATA(val);
|
|
|
|
// send to all
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_host_captain_change_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
int idx, found_player;
|
|
short player_id;
|
|
ubyte captain_change;
|
|
|
|
// get the player id
|
|
GET_SHORT(player_id);
|
|
GET_DATA(captain_change);
|
|
PACKET_SET_SIZE();
|
|
|
|
// captain change
|
|
if(captain_change){
|
|
// flag the new guy
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
|
|
HUD_printf("%s is the new captain of team %d", Net_players[idx].m_player->callsign, Net_players[idx].p_info.team + 1);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// unflag all old players
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
|
|
}
|
|
|
|
// flag the new guy
|
|
found_player = 0;
|
|
for(idx=0; idx<MAX_PLAYERS; idx++){
|
|
if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
|
|
Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
|
|
|
|
// spew to the HUD config
|
|
if(Net_players[idx].m_player != NULL){
|
|
HUD_printf("%s is the new game host", Net_players[idx].m_player->callsign);
|
|
}
|
|
|
|
found_player = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// doh
|
|
if(!found_player){
|
|
multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
|
|
}
|
|
}
|
|
}
|
|
|
|
void send_self_destruct_packet()
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
// bogus
|
|
if(Net_player == NULL){
|
|
return;
|
|
}
|
|
|
|
// if i'm the server, I shouldn't be here
|
|
Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
|
|
if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
|
|
return;
|
|
}
|
|
|
|
// only if this is valid
|
|
if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
|
|
return;
|
|
}
|
|
|
|
// bogus object?
|
|
if((Player_ship == NULL) || (Player_obj == NULL)){
|
|
return;
|
|
}
|
|
|
|
// self destruct
|
|
BUILD_HEADER(SELF_DESTRUCT);
|
|
ADD_USHORT(Player_obj->net_signature);
|
|
|
|
// send to the server
|
|
multi_io_send_reliable(Net_player, data, packet_size);
|
|
}
|
|
|
|
void process_self_destruct_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ushort net_sig;
|
|
int np_index;
|
|
|
|
// get the net signature
|
|
GET_USHORT(net_sig);
|
|
PACKET_SET_SIZE();
|
|
|
|
// get the player
|
|
np_index = find_player_index(hinfo->id);
|
|
if(np_index < 0){
|
|
return;
|
|
}
|
|
if(MULTI_OBSERVER(Net_players[np_index])){
|
|
return;
|
|
}
|
|
if(Net_players[np_index].m_player == NULL){
|
|
return;
|
|
}
|
|
if((Net_players[np_index].m_player->objnum < 0) || (Net_players[np_index].m_player->objnum >= MAX_OBJECTS)){
|
|
return;
|
|
}
|
|
if(Objects[Net_players[np_index].m_player->objnum].net_signature != net_sig){
|
|
return;
|
|
}
|
|
if(Objects[Net_players[np_index].m_player->objnum].type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
if((Objects[Net_players[np_index].m_player->objnum].instance < 0) || (Objects[Net_players[np_index].m_player->objnum].instance >= MAX_SHIPS)){
|
|
return;
|
|
}
|
|
|
|
// do eet
|
|
ship_self_destruct(&Objects[Net_players[np_index].m_player->objnum]);
|
|
}
|
|
|
|
void send_sexp_packet(ubyte *sexp_packet, int num_ubytes)
|
|
{
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
Assert (MULTIPLAYER_MASTER);
|
|
|
|
// must have a bare minimum of OP, COUNT and TERMINATOR
|
|
if (num_ubytes < MIN_SEXP_PACKET_SIZE) {
|
|
Warning(LOCATION, "Invalid call to send_sexp_packet. Not enough data included!");
|
|
return;
|
|
}
|
|
|
|
const int max_size = MAX_PACKET_SIZE - HEADER_LENGTH - static_cast<int>(sizeof(ushort));
|
|
|
|
if (num_ubytes > max_size) {
|
|
Warning(LOCATION, "Invalid call to send_sexp_packet. Too much data included!");
|
|
return;
|
|
}
|
|
|
|
BUILD_HEADER(SEXP);
|
|
|
|
ADD_USHORT(static_cast<ushort>(num_ubytes));
|
|
ADD_DATA_BLOCK(sexp_packet, num_ubytes);
|
|
|
|
// send to all
|
|
multi_io_send_to_all_reliable(data, packet_size);
|
|
}
|
|
|
|
void process_sexp_packet(ubyte *data, header *hinfo)
|
|
{
|
|
int offset = HEADER_LENGTH;
|
|
ushort num_ubytes;
|
|
ubyte received_packet[MAX_PACKET_SIZE];
|
|
|
|
GET_USHORT(num_ubytes);
|
|
GET_DATA_BLOCK(received_packet, num_ubytes);
|
|
|
|
PACKET_SET_SIZE();
|
|
|
|
sexp_packet_received(received_packet, num_ubytes);
|
|
}
|
|
|
|
|
|
// sends as many turrets as possible, and returns those it can not yet send because of packet size.
|
|
SCP_vector<int> send_turret_tracking_packet(ushort parent_sig, SCP_vector<int>& subsys_indexes)
|
|
{
|
|
|
|
Assertion(MULTIPLAYER_MASTER, "A non-server is trying to send a turret tracking packet. This is a coder mistake, please report!");
|
|
|
|
// Max size, minus some bytes for easy handling and safety.
|
|
constexpr int MAX_TURRET_PACKET_SIZE = MAX_PACKET_SIZE - 20;
|
|
SCP_vector<int> overflow_list;
|
|
|
|
Assertion (parent_sig != 0, "The turret tracking packet manager is trying to send a packet with a net signature of 0. This is nonsense and a coder mistake, please report!");
|
|
|
|
// somehow trying to send for an an invalid or non-network object
|
|
if (parent_sig == 0) {
|
|
// returning an empty list will signal the manager to clear its entries
|
|
return overflow_list;
|
|
}
|
|
|
|
auto temp = multi_get_network_object(parent_sig);
|
|
|
|
// this means that the ship may have been destroyed before being able to send the packet. Ignore.
|
|
if (temp == nullptr){
|
|
// returning an empty list will signal the manager to clear its entries
|
|
return overflow_list;
|
|
}
|
|
|
|
ship* shipp = &Ships[temp->instance];
|
|
|
|
// Required for all send functions
|
|
ubyte data[MAX_PACKET_SIZE];
|
|
int packet_size = 0;
|
|
|
|
BUILD_HEADER(TURRET_TRACK);
|
|
|
|
ubyte last_index = 0;
|
|
|
|
// this is not usually done, but if the number of subsystems actually ends up being different than the expected,
|
|
// this ubyte in the packet will get overwritten at the end of this function.
|
|
ADD_DATA(last_index);
|
|
|
|
// send the time that this happened.
|
|
int time_out = Multi_Timing_Info.get_current_time();
|
|
ADD_INT(time_out);
|
|
ADD_USHORT(parent_sig);
|
|
|
|
ubyte count = 0;
|
|
|
|
// no, I don't expect to send UINT turrets, but I don't want to be stuck
|
|
// in an infinite loop with a smaller word size.
|
|
for (uint i = 0; i < subsys_indexes.size(); i++){
|
|
|
|
// pre count is now being switched to a limiter to ensure that we do not exceed MAX_TURRET_PACKET_SIZE
|
|
if (packet_size < MAX_TURRET_PACKET_SIZE) {
|
|
auto ssp = ship_get_indexed_subsys( shipp, subsys_indexes[i]);
|
|
|
|
// make sure the subsytem is valid. If not, just continue.
|
|
if (ssp != nullptr){
|
|
// Add the data for the valid entry
|
|
ADD_SHORT( static_cast<short>(subsys_indexes[i]));
|
|
|
|
ushort target_netsig = (ssp->turret_enemy_objnum > -1) ? Objects[ssp->turret_enemy_objnum].net_signature : 0;
|
|
ADD_USHORT(target_netsig);
|
|
|
|
packet_size += multi_pack_turret_angles(data + packet_size, ssp);
|
|
++count;
|
|
} else {
|
|
// if we don't have a valid subsystem.
|
|
++last_index;
|
|
}
|
|
|
|
} else {
|
|
overflow_list.push_back(subsys_indexes[i]);
|
|
}
|
|
}
|
|
|
|
// nothing to send means that we should not send the packet, and just send back the overflow list.
|
|
if (count < 1) {
|
|
return overflow_list;
|
|
}
|
|
|
|
// and now, actually place the number of turrets we are updating
|
|
// this is a little hacky, but it's best to work through the serialization macros to rewrite the data.
|
|
int packet_size_record = packet_size;
|
|
packet_size = HEADER_LENGTH;
|
|
ADD_DATA(count);
|
|
packet_size = packet_size_record;
|
|
|
|
// The packet is now finished, so send.
|
|
multi_io_send_to_all(data, packet_size);
|
|
|
|
return overflow_list;
|
|
}
|
|
|
|
void process_turret_tracking_packet(ubyte *data, header *hinfo)
|
|
{
|
|
Assertion(MULTIPLAYER_CLIENT, "A non-client is receiving a turret tracking packet. This is a coder mistake, please report!");
|
|
|
|
int offset = HEADER_LENGTH;
|
|
|
|
int time_in;
|
|
ushort parent_netsig;
|
|
ubyte last_index;
|
|
|
|
GET_DATA(last_index);
|
|
GET_INT(time_in);
|
|
GET_USHORT(parent_netsig);
|
|
|
|
auto parent = multi_get_network_object(parent_netsig);
|
|
|
|
std::pair<bool, float> angle1;
|
|
std::pair<bool, float> angle2;
|
|
|
|
angle1.first = false;
|
|
angle1.second = 0.0f;
|
|
angle2.first = false;
|
|
angle2.second = 0.0f;
|
|
|
|
for (ubyte index = 0; index < last_index; index++){
|
|
short subsystem_index;
|
|
ushort target_netsig;
|
|
GET_SHORT(subsystem_index);
|
|
GET_USHORT(target_netsig);
|
|
|
|
offset += multi_unpack_turret_angles(data + offset, angle1, angle2);
|
|
|
|
// even if the ship does not exist
|
|
if (parent != nullptr){
|
|
Multi_Turret_Manager.add_incoming_packet(time_in, OBJ_INDEX(parent), subsystem_index, target_netsig, angle1, angle2);
|
|
}
|
|
|
|
angle1.first = false;
|
|
angle2.first = false;
|
|
}
|
|
|
|
PACKET_SET_SIZE();
|
|
}
|