diff options
Diffstat (limited to 'src/parsec/net_game_peer.cpp')
| -rw-r--r-- | src/parsec/net_game_peer.cpp | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/src/parsec/net_game_peer.cpp b/src/parsec/net_game_peer.cpp new file mode 100644 index 0000000..f2ba4ad --- /dev/null +++ b/src/parsec/net_game_peer.cpp @@ -0,0 +1,571 @@ +/* + * PARSEC - Network Game Functions + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:30 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +//#include "aud_defs.h" +#include "net_defs.h" +#include "sys_defs.h" + +// mathematics header +#include "utl_math.h" + +// network code config +#include "net_conf.h" + +// subsystem linkage info +#include "linkinfo.h" + +// local module header +#include "net_game_peer.h" + +// proprietary module headers +#include "con_aux.h" +//#include "aud_game.h" +//#include "g_supp.h" +//#include "h_supp.h" +#include "net_game.h" +#include "obj_creg.h" +//#include "obj_ctrl.h" +//#include "g_sfx.h" + + + +// set correct function names for dynamic binding------------------------------ +// +#ifdef DBIND_PROTOCOL + + #undef NETs_RmEvList_GetMaxSize + #undef NETs_UpdateKillStats + + #define NETs_RmEvList_GetMaxSize NETs_PEERTOPEER_RmEvList_GetMaxSize + #define NETs_UpdateKillStats NETs_PEERTOPEER_UpdateKillStats + +#endif + + +// queue of slot requests received while trying to establish connection ------- +// +int NumSlotRequests = 0; +slotrequest_s SlotReqQueue[ MAX_SLOT_REQUESTS ]; + + +// return the max. size of the RE list that can fit in one external packet ---- +// +size_t NETs_RmEvList_GetMaxSize() +{ + // NOTE: NET_MAX_DATA_LENGTH is either the max. external packet size for IPX or UDP + + return ( NET_MAX_DATA_LENGTH - sizeof( NetPacketExternal_PEER ) ); +} + + +// if this is enabled the kill stats will not be updated from packets --------- +// +extern int kill_stats_frozen; + +// do complete kill stats update from received remote event ------------------- +// +void NETs_UpdateKillStats( RE_KillStats* killstats ) +{ + ASSERT( NetConnected ); + ASSERT( killstats != NULL ); + + //NOTE: + // this function always sets the current kill stat + // to the maximum of all received kill stats to + // ensure that no kills are lost. + + // needed for kill stats reset + if ( kill_stats_frozen ) { + return; + } + + // update entire kill stats list + for ( int nPlayer = 0; nPlayer < MAX_NET_ALLOC_SLOTS; nPlayer++ ) { + if ( Player_Status[ nPlayer ] ) { + Player_KillStat[ nPlayer ] = max( Player_KillStat[ nPlayer ], (int) killstats->PlayerKills[ nPlayer ] ); + } + } +} + +// copy fields from the (old) ShipRemInfo to the (new) RE_PlayerAndShipStatus - +// +PRIVATE +void _ShipRemInfo_To_PlayerAndShipStatus( ShipRemInfo* pShipInfo, RE_PlayerAndShipStatus* pas_status ) +{ + memcpy( &pas_status->ObjPosition, pShipInfo->ObjPosition, sizeof( Xmatrx ) ); + pas_status->CurDamage = pShipInfo->CurDamage; + pas_status->CurShield = pShipInfo->CurShield; + pas_status->CurSpeed = pShipInfo->CurSpeed; + pas_status->CurYaw = pShipInfo->CurYaw; + pas_status->CurPitch = pShipInfo->CurPitch; + pas_status->CurRoll = pShipInfo->CurRoll; + pas_status->CurSlideHorz= pShipInfo->CurSlideHorz; + pas_status->CurSlideVert= pShipInfo->CurSlideVert; +} + + +// translate PKTP_JOIN peer-to-peer packet to RE_PlayerAndShipStatus ---------- +// +void NET_Translate_PKTP_JOIN( NetPacket_PEER* gamepacket, RE_PlayerAndShipStatus* pas_status ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( pas_status != NULL ); + ASSERT( gamepacket->Command == PKTP_JOIN ); + ASSERT( ( gamepacket->SendPlayerId >= 0 ) && ( gamepacket->SendPlayerId < MAX_NET_PROTO_PLAYERS ) ); + + pas_status->senderid = gamepacket->SendPlayerId; + pas_status->objectindex = gamepacket->params[ 3 ]; + + // copy fields from ShipRemInfo + _ShipRemInfo_To_PlayerAndShipStatus( &gamepacket->ShipInfo, pas_status ); + + pas_status->params[ 0 ] = gamepacket->params[ 0 ]; + pas_status->params[ 1 ] = gamepacket->params[ 1 ]; + pas_status->params[ 2 ] = gamepacket->params[ 2 ]; + pas_status->params[ 3 ] = gamepacket->params[ 3 ]; + + // set desired player status + pas_status->player_status = PLAYER_JOINED; +} + +// translate PKTP_GAME_STATE peer-to-peer packet to RE_PlayerAndShipStatus/RE_KillStats +// +void NET_Translate_PKTP_GAME_STATE( NetPacket_PEER* gamepacket, RE_PlayerAndShipStatus* pas_status, RE_KillStats* pKillStats ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( pas_status != NULL ); + ASSERT( pKillStats != NULL ); + ASSERT( gamepacket->Command == PKTP_GAME_STATE ); + ASSERT( ( gamepacket->SendPlayerId >= 0 ) && ( gamepacket->SendPlayerId < MAX_NET_PROTO_PLAYERS ) ); + + pas_status->senderid = gamepacket->SendPlayerId; + pas_status->objectindex = gamepacket->params[ 3 ]; + + // copy fields from ShipRemInfo + _ShipRemInfo_To_PlayerAndShipStatus( &gamepacket->ShipInfo, pas_status ); + + pas_status->params[ 0 ] = gamepacket->params[ 0 ]; + pas_status->params[ 1 ] = gamepacket->params[ 1 ]; + pas_status->params[ 2 ] = gamepacket->params[ 2 ]; + pas_status->params[ 3 ] = gamepacket->params[ 3 ]; + + // set desired player status + pas_status->player_status = PLAYER_JOINED; + + // fill RE_KillStats + for( int nPlayer = 0; nPlayer < MAX_NET_ALLOC_SLOTS; nPlayer++ ) { + pKillStats->PlayerKills[ nPlayer ] = gamepacket->PlayerKills[ nPlayer ]; + } +} + +// translate PKTP_UNJOIN peer-to-peer packet to RE_PlayerAndShipStatus/RE_KillStats +// +void NET_Translate_PKTP_UNJOIN( NetPacket_PEER* gamepacket, RE_PlayerAndShipStatus* pas_status, RE_KillStats* pKillStats ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( pas_status != NULL ); + ASSERT( pKillStats != NULL ); + ASSERT( gamepacket->Command == PKTP_UNJOIN ); + ASSERT( ( gamepacket->SendPlayerId >= 0 ) && ( gamepacket->SendPlayerId < MAX_NET_PROTO_PLAYERS ) ); + + // fill RE_PlayerAndShipStatus + pas_status->senderid = gamepacket->SendPlayerId; + pas_status->objectindex = gamepacket->params[ 3 ]; + + // copy fields from ShipRemInfo + _ShipRemInfo_To_PlayerAndShipStatus( &gamepacket->ShipInfo, pas_status ); + + pas_status->params[ 0 ] = gamepacket->params[ 0 ]; + pas_status->params[ 1 ] = gamepacket->params[ 1 ]; + pas_status->params[ 2 ] = gamepacket->params[ 2 ]; + pas_status->params[ 3 ] = gamepacket->params[ 3 ]; + + // set desired player status + pas_status->player_status = PLAYER_CONNECTED; + + // fill RE_KillStats + for( int nPlayer = 0; nPlayer < MAX_NET_ALLOC_SLOTS; nPlayer++ ) { + pKillStats->PlayerKills[ nPlayer ] = gamepacket->PlayerKills[ nPlayer ]; + } +} + +// translate PKTP_NODE_ALIVE peer-to-peer packet to RE_PlayerAndShipStatus/RE_KillStats +// +void NET_Translate_PKTP_NODE_ALIVE( NetPacket_PEER* gamepacket, RE_PlayerAndShipStatus* pas_status, RE_KillStats* pKillStats ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( pas_status != NULL ); + ASSERT( pKillStats != NULL ); + ASSERT( gamepacket->Command == PKTP_NODE_ALIVE ); + ASSERT( ( gamepacket->SendPlayerId >= 0 ) && ( gamepacket->SendPlayerId < MAX_NET_PROTO_PLAYERS ) ); + + // fill RE_PlayerAndShipStatus + pas_status->senderid = gamepacket->SendPlayerId; + pas_status->objectindex = gamepacket->params[ 3 ]; + + // copy fields from ShipRemInfo + _ShipRemInfo_To_PlayerAndShipStatus( &gamepacket->ShipInfo, pas_status ); + + pas_status->params[ 0 ] = gamepacket->params[ 0 ]; + pas_status->params[ 1 ] = gamepacket->params[ 1 ]; + pas_status->params[ 2 ] = gamepacket->params[ 2 ]; + pas_status->params[ 3 ] = gamepacket->params[ 3 ]; + + // set desired player status + pas_status->player_status = PLAYER_CONNECTED; + + // fill RE_KillStats + for( int nPlayer = 0; nPlayer < MAX_NET_ALLOC_SLOTS; nPlayer++ ) { + pKillStats->PlayerKills[ nPlayer ] = gamepacket->PlayerKills[ nPlayer ]; + } +} + + +// acquire slot for remote player --------------------------------------------- +// +int NET_AcquireRemoteSlot( node_t *node ) +{ + ASSERT( node != NULL ); + + //NOTE: + // this function is only used by NET_PEER::ConnectQueue() + // to acquire an arbitrary slot for a new remote player. + + ASSERT( NumRemPlayers > 0 ); + + int slotid = SLOTID_CONNECT_REFUSED; + int numplayers = NumRemPlayers; + + node_t sendnode; + NETs_MakeNodeRaw( &sendnode, node ); + + // try all slots + for ( int id = 0; id < MAX_NET_PROTO_PLAYERS; id++ ) { + + if ( Player_Status[ id ] != PLAYER_INACTIVE ) { + + // check whether remote player already has an active slot + node_t slotnode; + NETs_MakeNodeRaw( &slotnode, &Player_Node[ id ] ); + if ( NETs_CompareNodes( &sendnode, &slotnode ) == NODECMP_EQUAL ) { + DBGTXT( MSGOUT( "NET_AcquireRemoteSlot(): slot %d reacquired.", id ); ); + slotid = id; + NumRemPlayers = numplayers; + break; + } + + } else if ( slotid == SLOTID_CONNECT_REFUSED ) { + + // take first free slot if not already + // allocated slot encountered later on + slotid = id; + NumRemPlayers++; + } + } + + return slotid; +} + + +// append single slot request if not duplicate -------------------------------- +// +int NET_AddSlotRequest( node_t *node, char *name, int timetag ) +{ + ASSERT( node != NULL ); + ASSERT( name != NULL ); + + // compare to old requests + for ( int creq = 0; creq < NumSlotRequests; creq++ ) + if ( SlotReqQueue[ creq ].slotid != SLOTID_DELETED ) + if ( NETs_CompareNodes( &SlotReqQueue[ creq ].node, node ) == NODECMP_EQUAL ) + return 0; + + if ( NumSlotRequests >= MAX_SLOT_REQUESTS ) { + MSGOUT( "NET_AddSlotRequest(): too many slot requests." ); + ASSERT( 0 ); + return 0; + } + + // set special slot id for local host + int slotid = ( timetag == TIMETAG_LOCALHOST ) ? + SLOTID_LOCALHOST : SLOTID_NOT_ASSIGNED; + + int qpos = NumSlotRequests++; + + SlotReqQueue[ qpos ].slotid = slotid; + SlotReqQueue[ qpos ].timetag = timetag; + SlotReqQueue[ qpos ].node = *node; + CopyRemoteName( SlotReqQueue[ qpos ].name, name ); + + DBGTXT( MSGOUT( "NET_AddSlotRequest(): appended entry." ); ); + ADXTXT( NETs_PrintNode( node ); ); + + // return number of added slot requests + return 1; +} + + +// delete single slot request from queue -------------------------------------- +// +int NET_DelSlotRequest( node_t *node ) +{ + ASSERT( node != NULL ); + + int skipall = TRUE; + int nodefound = FALSE; + + // look for request with specified address + for ( int creq = 0; creq < NumSlotRequests; creq++ ) + if ( SlotReqQueue[ creq ].slotid != SLOTID_DELETED ) { + if ( NETs_CompareNodes( &SlotReqQueue[ creq ].node, node ) == NODECMP_EQUAL ) { + // disable request (no actual deletion) + SlotReqQueue[ creq ].slotid = SLOTID_DELETED; + nodefound = TRUE; + } else { + skipall = FALSE; + } + } + + if ( nodefound ) { + DBGTXT( MSGOUT( "NET_DelSlotRequest(): removed entry." ); ); + ADXTXT( NETs_PrintNode( node ); ); + } + + // reset queue if only deleted entries found + if ( ( NumSlotRequests > 0 ) && skipall ) { + DBGTXT( MSGOUT( "NET_DelSlotRequest(): reset queue." ); ); + NET_ResetSlotReqQueue(); + } + + return nodefound; +} + + +// merge slot request queue of remote host with local queue ------------------- +// +int NET_MergeSlotRequests( NetPacket_PEER *gamepacket, int timetag ) +{ + ASSERT( gamepacket != NULL ); + + //NOTE: + // the timetags stored into the queue are set by + // the local host. they have nothing to do with + // the original timetags of the sender. that is, + // the receive times count, not the send times. + // (the send times would have no meaning on + // another host anyway, since there is no + // synchronized timebase whatsoever.) there are + // also special timetags like TIMETAG_REPLYNOW. + + RE_ConnectQueue *re_cq = (RE_ConnectQueue *) &gamepacket->RE_List; + + // return if no queue to merge + if ( re_cq->RE_Type != RE_CONNECTQUEUE ) + return 0; + + ASSERT( re_cq->NumRequests >= 0 ); + ASSERT( re_cq->NumRequests <= MAX_SLOT_REQUESTS ); + + // process all new slot requests + int numadded = 0; + for ( int newreq = 0; newreq < re_cq->NumRequests; newreq++ ) { + numadded += NET_AddSlotRequest( &re_cq->AddressTable[ newreq ], re_cq->NameTable[ newreq ], timetag ); + } + return numadded; +} + + +// delete all entries in slot request queue ----------------------------------- +// +void NET_ResetSlotReqQueue() +{ + DBGTXT( MSGOUT( "NET_ResetSlotReqQueue(): reset queue." ); ); + NumSlotRequests = 0; +} + +// store list of all remote players as RE_List head --------------------------- +// +void NET_RmEvSinglePlayerTable( NetPacket_PEER* gamepacket ) +{ + ASSERT( gamepacket != NULL ); + + RE_PlayerList *re_playerlist = (RE_PlayerList *) &gamepacket->RE_List; + + ASSERT( re_playerlist->RE_Type == RE_EMPTY ); + re_playerlist->RE_BlockSize = RE_BLOCKSIZE_INVALID; //sizeof( RE_PlayerList ); *TOO BIG FOR BYTE* + + re_playerlist->RE_Type = RE_PLAYERLIST; + + ASSERT( sizeof( RE_PlayerList ) <= RE_LIST_MAXAVAIL ); + + // fill table + for ( int id = 0; id < MAX_NET_IPX_PEER_PLAYERS; id++ ) { + + // store status + re_playerlist->Status[ id ] = Player_Status[ id ]; + + // store address and name if player connected + if ( Player_Status[ id ] != PLAYER_INACTIVE ) { + + NETs_MakeNodeRaw( &re_playerlist->AddressTable[ id ], &Player_Node[ id ] ); + CopyRemoteName( re_playerlist->NameTable[ id ], Player_Name[ id ] ); + } + + // if player connected and joined store ship info + if ( Player_Status[ id ] == PLAYER_JOINED ) { + + ShipObject *shippo = (ShipObject *) Player_Ship[ id ]; + ASSERT( shippo != NULL ); + + int shipindex = ObjClassShipIndex[ shippo->ObjectClass ]; + ASSERT( shipindex != SHIPINDEX_NO_SHIP ); + + re_playerlist->ShipInfoTable[ id ].ShipIndex = shipindex; + memcpy( &re_playerlist->ShipInfoTable[ id ].ObjPosition, &shippo->ObjPosition, sizeof( Xmatrx ) ); + } + } + + // store critical sync values + re_playerlist->SyncValKillLimit = AUX_KILL_LIMIT_FOR_GAME_END; + re_playerlist->SyncValNebulaId = AUXDATA_BACKGROUND_NEBULA_ID; + + re_playerlist++; + re_playerlist->RE_Type = RE_EMPTY; + + if ( CurMaxPlayers > 4 ) { + + re_playerlist->RE_BlockSize = RE_BLOCKSIZE_INVALID; //sizeof( RE_PlayerList ); *TOO BIG FOR BYTE* + re_playerlist->RE_Type = RE_PLAYERLIST; + + ASSERT( ( 2 * sizeof( RE_PlayerList ) ) <= RE_LIST_MAXAVAIL ); + + // fill table + for ( int id = 0; id < MAX_NET_IPX_PEER_PLAYERS; id++ ) { + + // store status + re_playerlist->Status[ id ] = Player_Status[ id + 4 ]; + + // store address and name if player connected + if ( Player_Status[ id + 4 ] != PLAYER_INACTIVE ) { + + NETs_MakeNodeRaw( &re_playerlist->AddressTable[ id ], &Player_Node[ id + 4 ] ); + CopyRemoteName( re_playerlist->NameTable[ id ], Player_Name[ id + 4 ] ); + } + + // if player connected and joined store ship info + if ( Player_Status[ id + 4 ] == PLAYER_JOINED ) { + + ShipObject *shippo = (ShipObject *) Player_Ship[ id + 4 ]; + ASSERT( shippo != NULL ); + + int shipindex = ObjClassShipIndex[ shippo->ObjectClass ]; + ASSERT( shipindex != SHIPINDEX_NO_SHIP ); + + re_playerlist->ShipInfoTable[ id ].ShipIndex = shipindex; + memcpy( &re_playerlist->ShipInfoTable[ id ].ObjPosition, &shippo->ObjPosition, sizeof( Xmatrx ) ); + } + } + + // store critical sync values + re_playerlist->SyncValKillLimit = AUX_KILL_LIMIT_FOR_GAME_END; + re_playerlist->SyncValNebulaId = AUXDATA_BACKGROUND_NEBULA_ID; + + re_playerlist++; + re_playerlist->RE_Type = RE_EMPTY; + } +} + + +// store global slot request queue as RE_List head ---------------------------- +// +void NET_RmEvSingleConnectQueue( NetPacket_PEER* gamepacket ) +{ + ASSERT( gamepacket != NULL ); + + RE_ConnectQueue *re_connectqueue = (RE_ConnectQueue *) &gamepacket->RE_List; + ASSERT( re_connectqueue->RE_Type == RE_EMPTY ); + re_connectqueue->RE_Type = RE_CONNECTQUEUE; + re_connectqueue->RE_BlockSize = RE_BLOCKSIZE_INVALID; //sizeof( RE_ConnectQueue ); *TOO BIG FOR BYTE* + + ASSERT( sizeof( RE_ConnectQueue ) <= RE_LIST_MAXAVAIL ); + + ASSERT( NumSlotRequests >= 0 ); + ASSERT( NumSlotRequests <= MAX_SLOT_REQUESTS ); + + // fill table + int numvalid = 0; + for ( int req = 0; req < NumSlotRequests; req++ ) { + + // skip deleted entries + if ( SlotReqQueue[ req ].slotid != SLOTID_DELETED ) { + + // store node address and name of sender + CopyRemoteNode( re_connectqueue->AddressTable[ numvalid ], + SlotReqQueue[ req ].node ); + CopyRemoteName( re_connectqueue->NameTable[ numvalid ], + SlotReqQueue[ req ].name ); + numvalid++; + } + } + + // store size of queue + re_connectqueue->NumRequests = numvalid; + + re_connectqueue++; + re_connectqueue->RE_Type = RE_EMPTY; +} + + +// set player kill stats in packet to current values -------------------------- +// +void NET_SetPacketKillStats( NetPacket_PEER* gamepacket ) +{ + ASSERT( NetConnected ); + ASSERT( gamepacket != NULL ); + + // simply copy kill stats into packet + for ( int id = 0; id < MAX_NET_ALLOC_SLOTS; id++ ) { + if ( Player_Status[ id ] ) { + gamepacket->PlayerKills[ id ] = Player_KillStat[ id ]; + } + } +} + |
