2010-08-02 08:13:51 +00:00
//-------------------------------------------------------------------------
/*
2018-12-08 00:40:39 +00:00
Copyright ( C ) 2017 EDuke32 developers and contributors
2010-08-02 08:13:51 +00:00
This file is part of EDuke32 .
EDuke32 is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation .
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
2014-07-20 08:55:56 +00:00
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2010-08-02 08:13:51 +00:00
*/
//-------------------------------------------------------------------------
2019-09-21 18:59:54 +00:00
# include "ns.h" // Must come before everything else!
2018-12-08 00:40:39 +00:00
/// Preprocessor directives:
///
/// ITERATE_ON_UPDATE: Every time the netcode updates the sprite linked lists, attempt to iterate through all of them.
/// SPRLIST_PRINT: Print in the console every time a sprite's status in the link list changes due to <see cref="Net_UpdateSpriteLinkedLists">
/// PACKET_RECV_PRINT: Print all received packets
/// NETCODE_DISABLE: Remove pretty much all of the game's interaction with the code in this file.
2010-08-02 08:13:51 +00:00
# include "duke3d.h"
# include "game.h"
# include "gamedef.h"
2019-03-21 02:24:19 +00:00
# include "network.h"
2010-08-02 08:13:51 +00:00
# include "premap.h"
2012-05-17 23:54:43 +00:00
# include "savegame.h"
2012-06-03 16:11:22 +00:00
# include "input.h"
2019-10-21 22:05:21 +00:00
# include "gamecvars.h"
2010-08-02 08:13:51 +00:00
2019-10-19 23:44:15 +00:00
# include "enet.h"
2019-11-02 21:10:53 +00:00
# include "m_crc32.h"
2010-08-02 08:13:51 +00:00
2019-09-21 20:53:00 +00:00
BEGIN_DUKE_NS
2018-12-08 00:40:39 +00:00
// Data needed even if netcode is disabled
ENetHost * g_netServer = NULL ;
ENetHost * g_netClient = NULL ;
ENetPeer * g_netClientPeer = NULL ;
enet_uint16 g_netPort = 23513 ;
int32_t g_netDisconnect = 0 ;
char g_netPassword [ 32 ] ;
int32_t g_networkMode = NET_CLIENT ;
// to support (gcc only) -f-strict-aliasing, the netcode needs to specify that its 32 bit chunks to and from the
// packet code should not be subject to strict aliasing optimizations
// it would appear that only GCC does this, Clang doesn't seem to and Microsoft Visual C++ definitely doesn't.
# ifdef __GNUC__
# define TYPE_PUNNED __attribute__((__may_alias__))
# else
2018-12-15 01:40:08 +00:00
# define TYPE_PUNNED
2017-09-19 19:10:45 +00:00
# endif
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
typedef TYPE_PUNNED int32_t NetChunk32 ;
2019-10-19 23:41:35 +00:00
void faketimerhandler ( void ) { ; }
2018-12-08 00:40:39 +00:00
2019-10-19 23:41:35 +00:00
# ifndef NETCODE_DISABLE
2018-12-16 23:28:09 +00:00
static void Net_Disconnect ( void ) ;
static void Net_HandleClientPackets ( void ) ;
static void Net_HandleServerPackets ( void ) ;
2018-12-08 00:40:39 +00:00
# endif
2010-08-02 08:13:51 +00:00
2018-12-15 22:33:15 +00:00
void Net_GetPackets ( void )
{
# ifndef NETCODE_DISABLE
2019-10-19 23:41:35 +00:00
if ( g_netServer = = NULL & & g_netClient = = NULL )
return ;
2018-12-15 22:33:15 +00:00
if ( g_netDisconnect )
{
Net_Disconnect ( ) ;
g_netDisconnect = 0 ;
if ( g_gameQuit )
G_GameExit ( " " ) ;
return ;
}
2019-10-19 23:41:35 +00:00
enet_host_service ( g_netServer ? g_netServer : g_netClient , NULL , 0 ) ;
2018-12-15 22:33:15 +00:00
if ( g_netServer )
Net_HandleClientPackets ( ) ;
else if ( g_netClient )
Net_HandleServerPackets ( ) ;
# endif
}
2018-12-08 00:40:39 +00:00
// (the rest of the file)
# ifndef NETCODE_DISABLE
// this attribute is for variables that are only used to highlight interesting conditions in the code
// when I'm in a debugger.
# define NET_DEBUG_VAR EDUKE32_UNUSED
// internal data
# define SNAPSHOTS_TO_SAVE 32
# define MAX_SNAPSHOT_ACTORS 256
# define MAX_SNAPSHOT_WALLS MAXWALLS // 16384
# define MAX_SNAPSHOT_SECTORS MAXSECTORS // 4096
////////////////////////////////////////////////////////////////////////////////
// Server Update Packets
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
# pragma pack(push, 1)
typedef struct serverupdate_s
{
uint8_t header ;
uint8_t numplayers ;
input_t nsyn ;
int32_t seed ;
int16_t pause_on ;
} serverupdate_t ;
2012-05-17 23:54:43 +00:00
# pragma pack(pop)
2018-12-08 00:40:39 +00:00
# pragma pack(push, 1)
typedef struct serverplayerupdate_s
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
uint8_t extra ;
int16_t cstat ;
uint16_t owner ;
uint16_t picnum ;
uint16_t gotweapon ;
uint8_t kickback_pic ;
uint8_t frags [ MAXPLAYERS ] ;
int16_t inv_amount [ GET_MAX ] ;
int16_t ammo_amount [ MAX_WEAPONS ] ;
uint8_t curr_weapon ;
uint8_t last_weapon ;
uint8_t wantweaponfire ;
uint8_t weapon_pos ;
uint8_t frag_ps ;
uint8_t frag ;
uint8_t fraggedself ;
uint8_t last_extra ;
uint8_t pal ;
uint16_t ping ;
uint16_t newowner ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
playerupdate_t player ;
} serverplayerupdate_t ;
# pragma pack(pop)
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
# pragma pack(push, 1)
typedef struct clientupdate_s
{
uint8_t header ;
int32_t RevisionNumber ;
input_t nsyn ;
playerupdate_t player ;
} clientupdate_t ;
# pragma pack(pop)
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// Client player ready for world updates (player spawned and ready)
static int ClientPlayerReady = 0 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// all zero NetActor
static const netactor_t cNullNetActor = netactor_t ( ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// all zero NetWall
static const netWall_t cNullNetWall = netWall_t ( ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// all zero NetSector
static const netSector_t cNullNetSector = netSector_t ( ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// the revision number reserved for the state of the map when it was loaded from the .map file.
// NOTE: Don't confuse this with a revision index. Index 0 into the snapshot history is NOT the initial map state.
// g_MapStateHistory[0] could be revision 0, revision 64, revision 128, etc...
static const uint32_t cInitialMapStateRevisionNumber = 0 ;
// the revision array starts here instead of zero (may as well have the initial map state be revision 0)...
static const uint32_t cStartingRevisionIndex = 1 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static const int32_t cLocSprite_DeletedSpriteStat = MAXSTATUS ;
2019-02-28 07:21:33 +00:00
# ifdef CURRENTLY_UNUSED
2018-12-08 00:40:39 +00:00
static const int32_t cNetSprite_DeletedSpriteStat = STAT_NETALLOC ;
2019-02-28 07:21:33 +00:00
# endif
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
//[75] When a client attempts to allocate a sprite during the game loop, which is not defined as a clientside sprite,
// the insert will go through, and the sprite will be put on this list.
//
// every time the client gets a new snapshot, this entire list will be deleted
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static int32_t headscratchpadsprite = - 1 ;
static int32_t nextscratchpadsprite [ MAXSPRITES ] ;
2014-02-10 11:00:25 +00:00
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static char recbuf [ 180 ] ;
static int32_t g_chatPlayer = - 1 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// note that NETINDEX_BITS must be large enough to store the largest of
// MAXWALLS, MAXSECTORS, and MAXSPRITES, plus one bit so that the stop
// code won't be mistaken for a valid index
//
// as of 5/4/2016 MAXSPRITES is 16384, which technically only needs 14 bits but I'm going to
// set this to 16 + 1 to make it a more even number
//
// also NETINDEX_BITS should not exceed 32
# define NETINDEX_BITS (16 + 1)
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// worst case: 2 bits per field per stuct if every field in every struct is changed
# define WORLD_CHANGEBITSSIZE \
( MAXWALLS * ARRAY_SIZE ( WallFields ) * 2 ) + ( MAXSECTORS * ARRAY_SIZE ( SectorFields ) * 2 ) + ( MAXSPRITES * ARRAY_SIZE ( ActorFields ) * 2 )
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// each changed entry has a netindex + stop codes bits to indicate whether a field
// changed/zeroed convert to bytes....
# define WORLD_OVERHEADSIZE (((MAXSECTORS + MAXWALLS + MAXSPRITES + 3) * NETINDEX_BITS + WORLD_CHANGEBITSSIZE) >> 8) + 1
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
typedef struct netField_s
{
const char * name ; // field name
int32_t offset ; // offset from the start of the entity struct
2018-12-15 01:40:08 +00:00
int32_t bits ; // field size
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
} netField_t ;
2012-10-28 22:27:53 +00:00
2019-02-28 07:21:30 +00:00
# define SECTF(x) #x,(int32_t)(size_t)&((netSector_t*)0)->x
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static netField_t SectorFields [ ] =
2013-08-06 23:54:09 +00:00
{
2018-12-08 00:40:39 +00:00
{ SECTF ( wallptr ) , 16 } ,
{ SECTF ( wallnum ) , 16 } ,
2013-08-06 23:54:09 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( ceilingz ) , 32 } ,
{ SECTF ( floorz ) , 32 } ,
2013-08-06 23:54:09 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( ceilingstat ) , 16 } ,
{ SECTF ( floorstat ) , 16 } ,
{ SECTF ( ceilingpicnum ) , 16 } ,
{ SECTF ( ceilingheinum ) , 16 } ,
2013-08-06 23:54:09 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( ceilingshade ) , 8 } ,
{ SECTF ( ceilingpal ) , 8 } ,
{ SECTF ( ceilingxpanning ) , 8 } ,
{ SECTF ( ceilingypanning ) , 8 } ,
2013-08-06 23:54:09 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( floorpicnum ) , 16 } ,
{ SECTF ( floorheinum ) , 16 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( floorshade ) , 8 } ,
{ SECTF ( floorpal ) , 8 } ,
{ SECTF ( floorxpanning ) , 8 } ,
{ SECTF ( floorypanning ) , 8 } ,
{ SECTF ( visibility ) , 8 } ,
{ SECTF ( fogpal ) , 8 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ SECTF ( lotag ) , 16 } ,
{ SECTF ( hitag ) , 16 } ,
{ SECTF ( extra ) , 16 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
} ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
# undef SECTF
2019-02-28 07:21:30 +00:00
# define WALLF(x) #x,(int32_t)(size_t)&((netWall_t*)0)->x
2018-12-08 00:40:39 +00:00
static netField_t WallFields [ ] =
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
{ WALLF ( x ) , 32 } ,
{ WALLF ( y ) , 32 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ WALLF ( point2 ) , 16 } ,
{ WALLF ( nextwall ) , 16 } ,
{ WALLF ( nextsector ) , 16 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ WALLF ( cstat ) , 16 } ,
{ WALLF ( picnum ) , 16 } ,
{ WALLF ( overpicnum ) , 16 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ WALLF ( shade ) , 8 } ,
{ WALLF ( pal ) , 8 } ,
{ WALLF ( xrepeat ) , 8 } ,
{ WALLF ( yrepeat ) , 8 } ,
{ WALLF ( xpanning ) , 8 } ,
{ WALLF ( ypanning ) , 8 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
{ WALLF ( lotag ) , 16 } ,
{ WALLF ( hitag ) , 16 } ,
{ WALLF ( extra ) , 16 } ,
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
} ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
# undef WALLF
2012-10-28 22:27:53 +00:00
2019-02-28 07:21:30 +00:00
# define ACTF(x) #x,(int32_t)(size_t)&((netactor_t*)0)->x
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static netField_t ActorFields [ ] =
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
{ ACTF ( t_data_0 ) , 32 } ,
{ ACTF ( t_data_1 ) , 32 } ,
{ ACTF ( t_data_2 ) , 32 } ,
{ ACTF ( t_data_3 ) , 32 } ,
{ ACTF ( t_data_4 ) , 32 } ,
{ ACTF ( t_data_5 ) , 32 } ,
{ ACTF ( t_data_6 ) , 32 } ,
{ ACTF ( t_data_7 ) , 32 } ,
{ ACTF ( t_data_8 ) , 32 } ,
{ ACTF ( t_data_9 ) , 32 } ,
# ifdef LUNATIC
// need to update this section if LUNATIC is ever brought back in
{ ACTF ( hvel ) , 16 } ,
{ ACTF ( vvel ) , 16 } ,
{ ACTF ( startframe ) , 16 } ,
{ ACTF ( numframes ) , 16 } ,
{ ACTF ( viewtype ) , 16 } ,
{ ACTF ( incval ) , 16 } ,
{ ACTF ( delay ) , 16 } ,
# endif
2012-05-17 23:54:43 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( flags ) , 32 } ,
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( bpos_x ) , 32 } ,
{ ACTF ( bpos_y ) , 32 } ,
{ ACTF ( bpos_z ) , 32 } ,
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( floorz ) , 32 } ,
{ ACTF ( ceilingz ) , 32 } ,
{ ACTF ( lastvx ) , 32 } ,
{ ACTF ( lastvy ) , 32 } ,
2012-06-03 19:20:41 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( lasttransport ) , 8 } ,
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( picnum ) , 16 } ,
{ ACTF ( ang ) , 16 } ,
{ ACTF ( extra ) , 16 } ,
{ ACTF ( owner ) , 16 } ,
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
{ ACTF ( movflag ) , 16 } ,
{ ACTF ( tempang ) , 16 } ,
{ ACTF ( timetosleep ) , 16 } ,
{ ACTF ( stayput ) , 16 } ,
{ ACTF ( dispicnum ) , 16 } ,
# if defined LUNATIC
{ ACTF ( movflags ) , 16 } ,
# endif
{ ACTF ( cgg ) , 8 } ,
//------------------------------------------------------
// sprite fields
{ ACTF ( spr_x ) , 32 } ,
{ ACTF ( spr_y ) , 32 } ,
{ ACTF ( spr_z ) , 32 } ,
{ ACTF ( spr_cstat ) , 16 } ,
{ ACTF ( spr_picnum ) , 16 } ,
{ ACTF ( spr_shade ) , 8 } ,
{ ACTF ( spr_pal ) , 8 } ,
{ ACTF ( spr_clipdist ) , 8 } ,
{ ACTF ( spr_blend ) , 8 } ,
{ ACTF ( spr_xrepeat ) , 8 } ,
{ ACTF ( spr_yrepeat ) , 8 } ,
{ ACTF ( spr_xoffset ) , 8 } ,
{ ACTF ( spr_yoffset ) , 8 } ,
{ ACTF ( spr_sectnum ) , 16 } ,
{ ACTF ( spr_statnum ) , 16 } ,
{ ACTF ( spr_ang ) , 16 } ,
{ ACTF ( spr_owner ) , 16 } ,
{ ACTF ( spr_xvel ) , 16 } ,
{ ACTF ( spr_yvel ) , 16 } ,
{ ACTF ( spr_zvel ) , 16 } ,
{ ACTF ( spr_lotag ) , 16 } ,
{ ACTF ( spr_hitag ) , 16 } ,
{ ACTF ( spr_extra ) , 16 } ,
//--------------------------------------------------------------
//spriteext fields
{ ACTF ( ext_mdanimtims ) , 32 } ,
{ ACTF ( ext_mdanimcur ) , 16 } ,
{ ACTF ( ext_angoff ) , 16 } ,
{ ACTF ( ext_pitch ) , 16 } ,
{ ACTF ( ext_roll ) , 16 } ,
{ ACTF ( ext_offset_x ) , 32 } ,
{ ACTF ( ext_offset_y ) , 32 } ,
{ ACTF ( ext_offset_z ) , 32 } ,
{ ACTF ( ext_flags ) , 8 } ,
{ ACTF ( ext_xpanning ) , 8 } ,
{ ACTF ( ext_ypanning ) , 8 } ,
{ ACTF ( ext_alpha ) , 0 } , // float
//--------------------------------------------------------------
//spritesmooth fields
{ ACTF ( sm_smoothduration ) , 0 } , // float
{ ACTF ( sm_mdcurframe ) , 16 } ,
{ ACTF ( sm_mdoldframe ) , 16 } ,
{ ACTF ( sm_mdsmooth ) , 16 } ,
} ;
# undef ACTF
// actual game struct data size
# define WORLD_DATASIZE (MAXSECTORS * sizeof(netSector_t) + MAXWALLS * sizeof(netWall_t) + MAXSPRITES * sizeof(netactor_t))
// max packet array size
# define MAX_WORLDBUFFER WORLD_DATASIZE + WORLD_OVERHEADSIZE
2019-02-28 07:21:33 +00:00
# ifdef CURRENTLY_UNUSED
2018-12-08 00:40:39 +00:00
// Just so you can get an idea of how much memory the netcode needs...
static const int64_t cWORLD_DataSize = WORLD_DATASIZE ;
static const int64_t cWORLD_OverheadSize = WORLD_OVERHEADSIZE ;
static const int64_t cWORLD_TotalSize = MAX_WORLDBUFFER ;
// ...it's pretty big for now (!)
static const int64_t SnapshotArraySize = sizeof ( netmapstate_t ) * NET_REVISIONS ;
2019-02-28 07:21:33 +00:00
# endif
2018-12-08 00:40:39 +00:00
// both the client and server store their current revision number here,
// when the client sends their revision to the server,
// the server stores it in g_player[that player's index].revision
static uint32_t g_netMapRevisionNumber = 0 ;
// In addition to the client keeping track of what revision the server is using
// (g_netRevisionNumber), it also increments its own revision that represents
// what version of the game the client has interpolated to.
static uint32_t g_cl_InterpolatedRevision = 0 ;
static netmapstate_t g_mapStartState ;
static netmapstate_t g_cl_InterpolatedMapStateHistory [ NET_REVISIONS ] ;
// note that the map state number is not an index into here,
// to get the index into this array out of a map state number, do <Map state number> % NET_REVISONS
static netmapstate_t g_mapStateHistory [ NET_REVISIONS ] ;
static uint8_t tempnetbuf [ MAX_WORLDBUFFER ] ;
// Remember that this constant needs to be one bit longer than a struct index, so it can't be mistaken for a valid wall, sprite, or sector index
static const int32_t cSTOP_PARSING_CODE = ( ( 1 < < NETINDEX_BITS ) - 1 ) ;
static uint32_t NET_75_CHECK ;
// Externally available data / functions
int32_t g_netPlayersWaiting = 0 ;
int32_t g_netIndex = 2 ;
newgame_t pendingnewgame ;
bool g_enableClientInterpolationCheck = true ;
// Internal functions
static void Net_ReadWorldUpdate ( uint8_t * packetData , int32_t packetSize ) ;
//Adds a sprite with index 'spriteIndex' to the netcode's internal scratch sprite list,
//this does NOT allocate a new sprite or insert it into the other arrays.
//
//ONLY use this to insert sprites inserted between snapshots that are NOT defined to be clientside.
static void Net_InsertScratchPadSprite ( int spriteIndex )
{
if ( ! g_netClient )
2010-08-02 08:13:51 +00:00
{
return ;
}
2018-12-08 00:40:39 +00:00
Bassert ( spriteIndex < MAXSPRITES ) ;
Bassert ( spriteIndex > = 0 ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
int16_t const oldHead = headscratchpadsprite ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
nextscratchpadsprite [ spriteIndex ] = oldHead ;
headscratchpadsprite = spriteIndex ;
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Inserted scratch pad sprite %d \n " , spriteIndex ) ;
# endif
}
static void Net_DeleteFromSect ( int16_t spriteIndex )
{
if ( spriteIndex > = MAXSPRITES )
2010-08-02 08:13:51 +00:00
{
return ;
}
2018-12-08 00:40:39 +00:00
if ( sprite [ spriteIndex ] . sectnum > = MAXSECTORS )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
return ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
do_deletespritesect ( spriteIndex ) ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_DeleteFromStat ( int16_t spriteIndex )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
if ( spriteIndex > = MAXSPRITES )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
return ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( sprite [ spriteIndex ] . statnum > = MAXSTATUS )
{
return ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
do_deletespritestat ( spriteIndex ) ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_DoDeleteSprite ( int32_t spritenum )
{
NET_75_CHECK + + ; // Need to add a check to Net_DoDeleteSprite so that the client does not give sprites that were not previously STAT_NETALLOC back to STAT_NETALLOC,
// otherwise the client is leaking non-STAT_NETALLOC sprites.
//
// may want to add a flag to actor_t for SFLAG_SCRATCHSPRITE and SFLAG_CLIENTSIDE
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( sprite [ spritenum ] . statnum = = STAT_NETALLOC )
{
2010-08-02 08:13:51 +00:00
return ;
}
2018-12-08 00:40:39 +00:00
changespritestat ( spritenum , STAT_NETALLOC ) ;
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
Net_DeleteFromSect ( spritenum ) ;
sprite [ spritenum ] . sectnum = MAXSECTORS ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_InitScratchPadSpriteList ( )
{
if ( ! g_netClient )
{
return ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
headscratchpadsprite = - 1 ;
Bmemset ( & nextscratchpadsprite [ 0 ] , - 1 , sizeof ( nextscratchpadsprite ) ) ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_DeleteAllScratchPadSprites ( )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
if ( ! g_netClient )
2012-10-28 22:27:53 +00:00
{
return ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
int16_t spriteIndex = headscratchpadsprite ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
for ( spriteIndex = headscratchpadsprite ; spriteIndex > = 0 ; spriteIndex = nextscratchpadsprite [ spriteIndex ] )
{
Net_DoDeleteSprite ( spriteIndex ) ;
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Deleted scratch pad sprite (set to STAT_NETALLOC) %d \n " , spriteIndex ) ;
2012-12-09 13:24:44 +00:00
# endif
2018-12-08 00:40:39 +00:00
}
2012-12-09 13:24:44 +00:00
2018-12-08 00:40:39 +00:00
Net_InitScratchPadSpriteList ( ) ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_Error_Disconnect ( const char * message )
{
OSD_Printf ( " Net_Error_Disconnect: %s \n " , message ) ;
2018-12-15 01:40:08 +00:00
// Here I could longjmp to the game main loop and unload the map somehow
2018-12-08 00:40:39 +00:00
//
// If we go to C++ it may be a good idea to throw an exception
2018-12-15 01:40:08 +00:00
//
2018-12-08 00:40:39 +00:00
Bassert ( 0 ) ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_InitNetActor ( netactor_t * netActor )
{
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
* ( netActor ) = cNullNetActor ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
netActor - > netIndex = cSTOP_PARSING_CODE ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
// Low level "Copy net structs to / from game structs" functions
//------------------------------------------------------------------------------
// Net -> Game Arrays
//------------------------------------------------------------------------------
2012-12-09 13:24:44 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopyWallFromNet ( netWall_t * netWall , walltype * gameWall )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
// (convert data from 32 bit integers)
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Bassert ( netWall ) ;
Bassert ( gameWall ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameWall - > point2 = netWall - > point2 ;
gameWall - > nextwall = netWall - > nextwall ;
gameWall - > nextsector = netWall - > nextsector ;
gameWall - > cstat = netWall - > cstat ;
gameWall - > picnum = netWall - > picnum ;
gameWall - > overpicnum = netWall - > overpicnum ;
gameWall - > shade = netWall - > shade ;
gameWall - > pal = netWall - > pal ;
gameWall - > xrepeat = netWall - > xrepeat ;
gameWall - > yrepeat = netWall - > yrepeat ;
gameWall - > xpanning = netWall - > xpanning ;
gameWall - > ypanning = netWall - > ypanning ;
gameWall - > lotag = netWall - > lotag ;
gameWall - > hitag = netWall - > hitag ;
gameWall - > extra = netWall - > extra ;
int positionChanged = ( netWall - > x ! = gameWall - > x ) | |
( netWall - > y ! = gameWall - > y ) ;
if ( positionChanged )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
dragpoint ( netWall - > netIndex , netWall - > x , netWall - > y , 0 ) ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// unfortunately I don't know of any fields that would be guaranteed to be nonzero in here for error checking purposes.
NET_75_CHECK + + ; // I should initialize netWall_t fields to some nonsense value other than zero, or maybe check for netWall->netIndex == cSTOP_PARSING_CODE?
2014-02-10 11:00:25 +00:00
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySectorFromNet ( netSector_t * netSector , sectortype * gameSector )
{
Bassert ( gameSector ) ;
Bassert ( netSector ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// (convert data from 32 bit integers)
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > wallptr = netSector - > wallptr ;
gameSector - > wallnum = netSector - > wallnum ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > ceilingz = netSector - > ceilingz ;
gameSector - > floorz = netSector - > floorz ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > ceilingstat = netSector - > ceilingstat ;
gameSector - > floorstat = netSector - > floorstat ;
2011-02-25 21:50:19 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > ceilingpicnum = netSector - > ceilingpicnum ;
gameSector - > ceilingheinum = netSector - > ceilingheinum ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > ceilingshade = netSector - > ceilingshade ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > ceilingpal = netSector - > ceilingpal ;
gameSector - > ceilingxpanning = netSector - > ceilingxpanning ;
gameSector - > ceilingypanning = netSector - > ceilingypanning ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > floorpicnum = netSector - > floorpicnum ;
gameSector - > floorheinum = netSector - > floorheinum ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > floorshade = netSector - > floorshade ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSector - > floorpal = netSector - > floorpal ;
gameSector - > floorxpanning = netSector - > floorxpanning ;
gameSector - > floorypanning = netSector - > floorypanning ;
gameSector - > visibility = netSector - > visibility ;
gameSector - > fogpal = netSector - > fogpal ;
gameSector - > lotag = netSector - > lotag ;
gameSector - > hitag = netSector - > hitag ;
gameSector - > extra = netSector - > extra ;
// sanity check
if ( gameSector - > wallnum < = 0 )
{
Net_Error_Disconnect ( " Net_CopySectorFromNet: Invalid wallnum from server. " ) ;
2011-02-25 21:50:19 +00:00
}
2018-12-08 00:40:39 +00:00
2012-05-17 23:54:43 +00:00
}
2010-08-02 08:13:51 +00:00
2011-02-25 21:50:19 +00:00
2018-12-08 00:40:39 +00:00
// Try to catch infinite loops in the sprite linked lists, unfortunately this rarely works.
EDUKE32_UNUSED static void Test_Iterate ( )
{
int32_t watchdogIndex = 0 ;
const int32_t cThreshold = MAXSPRITES * 2 ;
2011-02-25 21:50:19 +00:00
2018-12-08 00:40:39 +00:00
for ( int32_t statnum = 0 ; statnum < MAXSTATUS ; statnum + + )
2011-02-25 21:50:19 +00:00
{
2018-12-08 00:40:39 +00:00
watchdogIndex = 0 ;
for ( int32_t spriteIndex = headspritestat [ statnum ] ; spriteIndex > = 0 ; spriteIndex = nextspritestat [ spriteIndex ] )
2012-05-17 23:54:43 +00:00
{
2018-12-08 00:40:39 +00:00
watchdogIndex + + ;
if ( watchdogIndex > cThreshold )
{
Bassert ( watchdogIndex < = cThreshold ) ;
}
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
}
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
for ( int32_t sectnum = 0 ; sectnum < MAXSECTORS ; sectnum + + )
{
watchdogIndex = 0 ;
for ( int32_t spriteIndex = headspritesect [ sectnum ] ; spriteIndex > = 0 ; spriteIndex = nextspritesect [ spriteIndex ] )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
watchdogIndex + + ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( watchdogIndex > cThreshold )
{
Bassert ( watchdogIndex < = cThreshold ) ;
}
}
2012-05-19 16:25:22 +00:00
}
}
2018-12-08 00:40:39 +00:00
static void Net_UpdateSpriteLinkedLists ( int16_t spriteIndex , const netactor_t * snapActor )
2012-05-19 16:25:22 +00:00
{
2018-12-08 00:40:39 +00:00
if ( ( spriteIndex > = MAXSPRITES ) | | ( spriteIndex < 0 ) )
{
Net_Error_Disconnect ( " Can't update linked lists. Actor index invalid. " ) ;
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
int16_t oldGameStatnum = sprite [ spriteIndex ] . statnum ;
// changes to game lists for this sprite:
//
// stats sectors
// -------------------------
// 1. Game sprite stat == snap sprite stat -- nothing nothing
//
//
// this leaks a STAT_NETALLOC
// 2. game sprite <any stat>, snap sprite MAXSTATUS -- delete delete
//
// this leaks a client sprite
// 3. game sprite MAXSTATUS, snap sprite STAT_NETALLOC or normal-- insert insert
// (implied in `else`)
//
// 4. game sprite STAT_NETALLOC, snap sprite Normal -- change insert
//
// 5. game sprite Normal, snap sprite STAT_NETALLOC -- change delete
//
// 6. game sprite Normal, snap sprite Normal -- change change
// [1]
if ( snapActor - > spr_statnum = = oldGameStatnum )
2012-05-19 16:25:22 +00:00
{
2018-12-08 00:40:39 +00:00
return ;
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
bool snapActorIsNormalStat = ( snapActor - > spr_statnum ! = MAXSTATUS ) & & ( snapActor - > spr_statnum ! = STAT_NETALLOC ) ;
bool gameSpriteIsNormalStat = ( oldGameStatnum ! = MAXSTATUS ) & & ( oldGameStatnum ! = STAT_NETALLOC ) ;
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
// [2]
if ( snapActor - > spr_statnum = = MAXSTATUS )
{
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d: Case 2 (delete game sprite) \n " , spriteIndex ) ;
# endif
Net_DeleteFromStat ( spriteIndex ) ;
Net_DeleteFromSect ( spriteIndex ) ;
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
return ;
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
// [3]
else if ( oldGameStatnum = = MAXSTATUS )
{
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d: Case 3 (insert new game sprite) \n " , spriteIndex ) ;
# endif
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
do_insertsprite_at_headofstat ( spriteIndex , snapActor - > spr_statnum ) ;
do_insertsprite_at_headofsect ( spriteIndex , snapActor - > spr_sectnum ) ;
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
// [4]
else if ( ( oldGameStatnum = = STAT_NETALLOC ) & & snapActorIsNormalStat )
2018-12-15 01:40:08 +00:00
{
2018-12-08 00:40:39 +00:00
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d: Case 4 (STAT_NETALLOC to stat %d) \n " , spriteIndex , snapActor - > spr_statnum ) ;
# endif
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
changespritestat ( spriteIndex , snapActor - > spr_statnum ) ;
do_insertsprite_at_headofsect ( spriteIndex , snapActor - > spr_sectnum ) ;
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
// [5]
else if ( gameSpriteIsNormalStat & & ( snapActor - > spr_statnum = = STAT_NETALLOC ) )
{
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d: Case 5 (normal to STAT_NETALLOC) \n " , spriteIndex ) ;
# endif
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
changespritestat ( spriteIndex , snapActor - > spr_statnum ) ;
Net_DeleteFromSect ( spriteIndex ) ;
2018-12-15 01:40:08 +00:00
2012-05-19 16:25:22 +00:00
}
2018-12-08 00:40:39 +00:00
// [6]
else if ( gameSpriteIsNormalStat & & snapActorIsNormalStat )
{
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d: Case 6 (normal stat to normal stat) \n " , spriteIndex ) ;
# endif
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// note that these functions handle cases where the game sprite already has that stat/sectnum
changespritestat ( spriteIndex , snapActor - > spr_statnum ) ;
changespritesect ( spriteIndex , snapActor - > spr_sectnum ) ;
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
# ifdef SPRLIST_PRINT
OSD_Printf ( " DEBUG: Sprite %d next is %d, prev is %d, head of stat %d is %d \n " , spriteIndex , nextspritestat [ spriteIndex ] , prevspritestat [ spriteIndex ] , snapActor - > spr_statnum , headspritestat [ snapActor - > spr_statnum ] ) ;
2010-08-02 08:13:51 +00:00
# endif
2018-12-08 00:40:39 +00:00
NET_DEBUG_VAR bool invalid = (
( nextspritesect [ spriteIndex ] = = spriteIndex )
& & ( nextspritestat [ spriteIndex ] = = spriteIndex )
) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Bassert ( ! invalid ) ;
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
# ifdef ITERATE_ON_UPDATE
Test_Iterate ( ) ;
# endif
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteFromNet ( const netactor_t * netActor , spritetype * gameSprite )
{
Bassert ( netActor ) ;
Bassert ( gameSprite ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// NOTE: Don't call Net_UpdateSpriteLinkedLists here, that should only be called
// if the sprite isn't already deleted in the game arrays.
gameSprite - > x = netActor - > spr_x ;
gameSprite - > y = netActor - > spr_y ;
gameSprite - > z = netActor - > spr_z ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// don't set statnum / sectnum here, that should be done in Net_UpdateSpriteLinkedLists,
2018-12-15 01:40:08 +00:00
// otherwise it's harder than it has to be because most of the engine functions
// asssume that if the sprite's stat / sectnum matches there's nothing to do as
2018-12-08 00:40:39 +00:00
// far as the linked lists are concerned
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > cstat = netActor - > spr_cstat ;
gameSprite - > picnum = netActor - > spr_picnum ;
gameSprite - > shade = netActor - > spr_shade ;
gameSprite - > pal = netActor - > spr_pal ;
gameSprite - > clipdist = netActor - > spr_clipdist ;
gameSprite - > blend = netActor - > spr_blend ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > xrepeat = netActor - > spr_xrepeat ;
gameSprite - > yrepeat = netActor - > spr_yrepeat ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > xoffset = netActor - > spr_xoffset ;
gameSprite - > yoffset = netActor - > spr_yoffset ;
2010-08-02 08:13:51 +00:00
2011-03-05 01:41:38 +00:00
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > ang = netActor - > spr_ang ;
gameSprite - > owner = netActor - > spr_owner ;
gameSprite - > xvel = netActor - > spr_xvel ;
gameSprite - > yvel = netActor - > spr_yvel ;
gameSprite - > zvel = netActor - > spr_zvel ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > lotag = netActor - > spr_lotag ;
gameSprite - > hitag = netActor - > spr_hitag ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > extra = netActor - > spr_extra ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_CopyActorFromNet ( const netactor_t * netActor , actor_t * gameActor )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
// (convert data from 32 bit integers)
Bassert ( netActor ) ;
Bassert ( gameActor ) ;
// This seemed to make enemy movements smoother.
bool aiIDChanged = ( gameActor - > t_data [ 5 ] ! = netActor - > t_data_5 ) ;
// If the sprite is a CON sprite, don't overwrite AC_Action_Count
bool isActor = G_HaveActor ( netActor - > spr_picnum ) ;
// Fixes ambient sound infinite sound replay glitch (stand in the outdoor area of E1L1, the "airplane noise" will get very loud and loop endlessly.
bool isSoundActor = ( DYNAMICTILEMAP ( netActor - > picnum ) = = MUSICANDSFX ) ;
if ( ! isSoundActor )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
gameActor - > t_data [ 0 ] = netActor - > t_data_0 ;
gameActor - > t_data [ 1 ] = netActor - > t_data_1 ;
gameActor - > t_data [ 4 ] = netActor - > t_data_4 ;
gameActor - > t_data [ 5 ] = netActor - > t_data_5 ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// Prevents:
// - Rotating sector stuttering
// - Trains running backwards
2018-12-15 01:40:08 +00:00
bool isSyncedSE = ( DYNAMICTILEMAP ( netActor - > picnum ) = = SECTOREFFECTOR ) & &
2018-12-08 00:40:39 +00:00
(
2018-12-15 01:40:08 +00:00
( netActor - > spr_lotag = = SE_0_ROTATING_SECTOR )
| | ( netActor - > spr_lotag = = SE_1_PIVOT )
2018-12-08 00:40:39 +00:00
| | ( netActor - > spr_lotag = = SE_6_SUBWAY )
| | ( netActor - > spr_lotag = = SE_11_SWINGING_DOOR )
| | ( netActor - > spr_lotag = = SE_14_SUBWAY_CAR )
| | ( netActor - > spr_lotag = = SE_30_TWO_WAY_TRAIN )
) ;
2012-10-14 21:52:51 +00:00
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( aiIDChanged | | isSyncedSE )
{
gameActor - > t_data [ 2 ] = netActor - > t_data_2 ;
}
2012-05-17 23:54:43 +00:00
2018-12-08 00:40:39 +00:00
if ( aiIDChanged | | ! isActor )
{
gameActor - > t_data [ 3 ] = netActor - > t_data_3 ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-15 01:40:08 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > t_data [ 6 ] = netActor - > t_data_6 ;
gameActor - > t_data [ 7 ] = netActor - > t_data_7 ;
gameActor - > t_data [ 8 ] = netActor - > t_data_8 ;
gameActor - > t_data [ 9 ] = netActor - > t_data_9 ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
# ifdef LUNATIC
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > mv . hvel = netActor - > hvel ;
gameActor - > mv . vvel = netActor - > vvel ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > ac . startframe = netActor - > startframe ;
gameActor - > ac . numframes = netActor - > numframes ;
gameActor - > ac . viewtype = netActor - > viewtype ;
gameActor - > ac . incval = netActor - > incval ;
gameActor - > ac . delay = netActor - > delay ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > actiontics = netActor - > actiontics ;
2013-10-07 10:03:23 +00:00
2018-12-08 00:40:39 +00:00
# endif
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > flags = netActor - > flags ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > bpos . x = netActor - > bpos_x ;
gameActor - > bpos . y = netActor - > bpos_y ;
gameActor - > bpos . z = netActor - > bpos_z ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > floorz = netActor - > floorz ;
gameActor - > ceilingz = netActor - > ceilingz ;
gameActor - > lastv . x = netActor - > lastvx ;
gameActor - > lastv . y = netActor - > lastvy ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > lasttransport = netActor - > lasttransport ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
//WARNING: both sprite and actor have these fields
gameActor - > picnum = netActor - > picnum ;
gameActor - > ang = netActor - > ang ;
gameActor - > extra = netActor - > extra ;
gameActor - > owner = netActor - > owner ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > movflag = netActor - > movflag ;
gameActor - > tempang = netActor - > tempang ;
gameActor - > timetosleep = netActor - > timetosleep ;
2013-10-07 10:03:23 +00:00
2018-12-08 00:40:39 +00:00
gameActor - > stayput = netActor - > stayput ;
gameActor - > dispicnum = netActor - > dispicnum ;
2014-02-10 11:00:25 +00:00
2018-12-08 00:40:39 +00:00
# if defined LUNATIC
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
//WARNING: NOT the same as movflag
gameActor - > movflags = netActor - > movflags ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
# endif
gameActor - > cgg = netActor - > cgg ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteExtFromNet ( const netactor_t * netActor , spriteext_t * gameSprExt )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( netActor ) ;
Bassert ( gameSprExt ) ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameSprExt - > mdanimtims = netActor - > ext_mdanimtims ;
2013-10-07 10:03:23 +00:00
2018-12-08 00:40:39 +00:00
gameSprExt - > mdanimcur = netActor - > ext_mdanimcur ;
gameSprExt - > angoff = netActor - > ext_angoff ;
gameSprExt - > pitch = netActor - > ext_pitch ;
gameSprExt - > roll = netActor - > ext_roll ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
gameSprExt - > offset . x = netActor - > ext_offset_x ;
gameSprExt - > offset . y = netActor - > ext_offset_y ;
gameSprExt - > offset . z = netActor - > ext_offset_z ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprExt - > flags = netActor - > ext_flags ;
gameSprExt - > xpanning = netActor - > ext_xpanning ;
gameSprExt - > ypanning = netActor - > ext_ypanning ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprExt - > alpha = netActor - > ext_alpha ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
}
2012-05-19 16:25:22 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteSmoothFromNet ( const netactor_t * netActor , spritesmooth_t * gameSprSmooth )
{
Bassert ( netActor ) ;
Bassert ( gameSprSmooth ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
gameSprSmooth - > smoothduration = netActor - > sm_smoothduration ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprSmooth - > mdcurframe = netActor - > sm_mdcurframe ;
gameSprSmooth - > mdoldframe = netActor - > sm_mdoldframe ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
gameSprSmooth - > mdsmooth = netActor - > sm_mdsmooth ;
2012-10-28 22:27:53 +00:00
}
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopyAllActorDataFromNet ( const netactor_t * netActor , spritetype * gameSprite , actor_t * gameActor , spriteext_t * gameSprExt , spritesmooth_t * gameSprSmooth )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Net_CopySpriteFromNet ( netActor , gameSprite ) ;
Net_CopyActorFromNet ( netActor , gameActor ) ;
Net_CopySpriteExtFromNet ( netActor , gameSprExt ) ;
Net_CopySpriteSmoothFromNet ( netActor , gameSprSmooth ) ;
2013-10-07 10:03:23 +00:00
2012-05-19 16:25:52 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// Clients only.
static void Net_CopyPlayerSpriteFromNet ( const netactor_t * netActor , spritetype * gameSprite )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( netActor ) ;
Bassert ( gameSprite ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// We don't need to synchronize player position, ang, or sectnum, because P_ProcessInput
// does that for all player sprites based on ps->pos and the player's input.
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > cstat = netActor - > spr_cstat ;
gameSprite - > picnum = netActor - > spr_picnum ;
gameSprite - > shade = netActor - > spr_shade ;
gameSprite - > pal = netActor - > spr_pal ;
gameSprite - > clipdist = netActor - > spr_clipdist ;
gameSprite - > blend = netActor - > spr_blend ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > xrepeat = netActor - > spr_xrepeat ;
gameSprite - > yrepeat = netActor - > spr_yrepeat ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > xoffset = netActor - > spr_xoffset ;
gameSprite - > yoffset = netActor - > spr_yoffset ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > owner = netActor - > spr_owner ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
//xvel for player sprites is used for weapon bobbing, and is just set to the euclidean distance between
//pos and bobpos, so there is no need to sync that.
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > yvel = netActor - > spr_yvel ; // player index
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
//zvel for player sprites is only used during the transition between air and water, I'm pretty sure that we don't think we need to sync this.
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
gameSprite - > lotag = netActor - > spr_lotag ;
gameSprite - > hitag = netActor - > spr_hitag ;
gameSprite - > extra = netActor - > spr_extra ;
2012-10-28 22:27:53 +00:00
}
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
// Similar to CopyAllActorData, but this one ignores some fields for the player sprite
static void Net_CopyPlayerActorDataFromNet ( const netactor_t * netActor , spritetype * gameSprite , actor_t * gameActor , spriteext_t * gameSprExt , spritesmooth_t * gameSprSmooth )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Net_CopyPlayerSpriteFromNet ( netActor , gameSprite ) ;
Net_CopyActorFromNet ( netActor , gameActor ) ;
Net_CopySpriteExtFromNet ( netActor , gameSprExt ) ;
Net_CopySpriteSmoothFromNet ( netActor , gameSprSmooth ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopyActorsToGameArrays ( const netmapstate_t * srv_snapshot , const netmapstate_t * cl_snapshot )
{
int32_t actorIndex = 0 ;
int32_t actorCount = 0 ;
2012-05-19 16:25:38 +00:00
2018-12-08 00:40:39 +00:00
// we need to clear out any sprites the client inserted between applying snapshots,
// so that there aren't any sprite index conflicts.
Net_DeleteAllScratchPadSprites ( ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( ( srv_snapshot - > maxActorIndex ) < 0 | | ( srv_snapshot - > maxActorIndex > MAXSPRITES ) )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Net_Error_Disconnect ( " Net_CopyActorsToGameArrays: Invalid number of actors in snapshot. " ) ;
2012-10-28 22:27:53 +00:00
}
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
for ( actorIndex = 0 ; actorIndex < MAXSPRITES ; actorIndex + + )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
const netactor_t * srvActor = & ( srv_snapshot - > actor [ actorIndex ] ) ;
const netactor_t * clActor = & ( cl_snapshot - > actor [ actorIndex ] ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
int status = memcmp ( srvActor , clActor , sizeof ( netactor_t ) ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( status = = 0 )
{
if ( g_enableClientInterpolationCheck )
{
continue ;
}
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
const netactor_t * snapshotActor = srvActor ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
spritetype * gameSprite = & ( sprite [ actorIndex ] ) ;
actor_t * gameActor = & ( actor [ actorIndex ] ) ;
spriteext_t * gameExt = & ( spriteext [ actorIndex ] ) ;
spritesmooth_t * gameSm = & ( spritesmooth [ actorIndex ] ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
NET_DEBUG_VAR int32_t DEBUG_GameSprOldStat = gameSprite - > statnum ;
NET_DEBUG_VAR int32_t DEBUG_NetSprOldStat = snapshotActor - > spr_statnum ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// NOTE: STAT_NETALLOC sprites ARE part if numsprites!We need to count STAT_NETALLOC sprites.
bool snapSpriteIsDeleted = ( snapshotActor - > spr_statnum = = cLocSprite_DeletedSpriteStat ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // Need to make sure this will not effect swimming legs, holodukes, or start points negatively
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
bool isAnyOtherPlayerSprite = ( snapshotActor - > spr_picnum = = APLAYER ) & & ( snapshotActor - > spr_yvel > 0 ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
bool isPlayer0Sprite = ( actorIndex = = g_player [ 0 ] . ps - > i ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// it's better to let P_ProcessInput update some fields of the player's sprite.
if ( isPlayer0Sprite | | isAnyOtherPlayerSprite )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // Net_CopyPlayerActorDataFromNet() may be a good place to handle checking the player's new position
// this will also need updating when we support a dynamic number of player sprites...
Net_CopyPlayerActorDataFromNet ( snapshotActor , gameSprite , gameActor , gameExt , gameSm ) ;
continue ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Net_CopyAllActorDataFromNet ( snapshotActor , gameSprite , gameActor , gameExt , gameSm ) ;
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
Net_UpdateSpriteLinkedLists ( actorIndex , snapshotActor ) ;
2018-12-15 01:40:08 +00:00
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( ! snapSpriteIsDeleted )
{
actorCount + + ;
}
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( actorCount > MAXSPRITES )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
Net_Error_Disconnect ( " Net_CopyActorsToGameArrays: Too many actors in snapshot. " ) ;
2012-12-12 02:53:13 +00:00
}
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
Numsprites = actorCount ;
2012-12-12 02:53:13 +00:00
}
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
//-------------------------------------------------------------------------------------
// Game -> Net
//-------------------------------------------------------------------------------------
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopyWallToNet ( const walltype * gameWall , netWall_t * netWall , int16_t netIndex )
{
Bassert ( gameWall ) ;
Bassert ( netWall ) ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
// (convert data to 32 bit integers)
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
netWall - > x = gameWall - > x ;
netWall - > y = gameWall - > y ;
netWall - > point2 = gameWall - > point2 ;
netWall - > nextwall = gameWall - > nextwall ;
netWall - > nextsector = gameWall - > nextsector ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netWall - > cstat = gameWall - > cstat ;
netWall - > picnum = gameWall - > picnum ;
netWall - > overpicnum = gameWall - > overpicnum ;
netWall - > shade = gameWall - > shade ;
netWall - > pal = gameWall - > pal ;
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
netWall - > xrepeat = gameWall - > xrepeat ;
netWall - > yrepeat = gameWall - > yrepeat ;
netWall - > xpanning = gameWall - > xpanning ;
netWall - > ypanning = gameWall - > ypanning ;
netWall - > lotag = gameWall - > lotag ;
2014-02-10 11:00:25 +00:00
2018-12-08 00:40:39 +00:00
netWall - > hitag = gameWall - > hitag ;
netWall - > extra = gameWall - > extra ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netWall - > netIndex = netIndex ;
}
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySectorToNet ( const sectortype * gameSector , netSector_t * netSector , int16_t netIndex )
{
Bassert ( gameSector ) ;
Bassert ( netSector ) ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
// (convert data to 32 bit integers)
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netSector - > wallptr = gameSector - > wallptr ;
netSector - > wallnum = gameSector - > wallnum ;
netSector - > ceilingz = gameSector - > ceilingz ;
netSector - > floorz = gameSector - > floorz ;
netSector - > ceilingstat = gameSector - > ceilingstat ;
2014-02-10 10:55:49 +00:00
2018-12-08 00:40:39 +00:00
netSector - > floorstat = gameSector - > floorstat ;
netSector - > ceilingpicnum = gameSector - > ceilingpicnum ;
netSector - > ceilingheinum = gameSector - > ceilingheinum ;
netSector - > ceilingshade = gameSector - > ceilingshade ;
netSector - > ceilingpal = gameSector - > ceilingpal ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netSector - > ceilingxpanning = gameSector - > ceilingxpanning ;
netSector - > ceilingypanning = gameSector - > ceilingypanning ;
netSector - > floorpicnum = gameSector - > floorpicnum ;
netSector - > floorheinum = gameSector - > floorheinum ;
netSector - > floorshade = gameSector - > floorshade ;
2014-02-10 10:55:49 +00:00
2018-12-08 00:40:39 +00:00
netSector - > floorpal = gameSector - > floorpal ;
netSector - > floorxpanning = gameSector - > floorxpanning ;
netSector - > floorypanning = gameSector - > floorypanning ;
netSector - > visibility = gameSector - > visibility ;
netSector - > fogpal = gameSector - > fogpal ;
2014-02-10 10:55:49 +00:00
2018-12-08 00:40:39 +00:00
netSector - > lotag = gameSector - > lotag ;
netSector - > hitag = gameSector - > hitag ;
netSector - > extra = gameSector - > extra ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netSector - > netIndex = netIndex ;
2012-12-12 02:53:13 +00:00
2012-10-28 22:27:53 +00:00
}
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteToNet ( const spritetype * gameSprite , netactor_t * netActor )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( netActor ) ;
Bassert ( gameSprite ) ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_x = gameSprite - > x ;
netActor - > spr_y = gameSprite - > y ;
netActor - > spr_z = gameSprite - > z ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_cstat = gameSprite - > cstat ;
netActor - > spr_picnum = gameSprite - > picnum ;
netActor - > spr_shade = gameSprite - > shade ;
netActor - > spr_pal = gameSprite - > pal ;
netActor - > spr_clipdist = gameSprite - > clipdist ;
netActor - > spr_blend = gameSprite - > blend ;
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_xrepeat = gameSprite - > xrepeat ;
netActor - > spr_yrepeat = gameSprite - > yrepeat ;
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_xoffset = gameSprite - > xoffset ;
netActor - > spr_yoffset = gameSprite - > yoffset ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_sectnum = gameSprite - > sectnum ;
netActor - > spr_statnum = gameSprite - > statnum ;
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_ang = gameSprite - > ang ;
netActor - > spr_owner = gameSprite - > owner ;
netActor - > spr_xvel = gameSprite - > xvel ;
netActor - > spr_yvel = gameSprite - > yvel ;
netActor - > spr_zvel = gameSprite - > zvel ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_lotag = gameSprite - > lotag ;
netActor - > spr_hitag = gameSprite - > hitag ;
2013-08-12 15:18:10 +00:00
2018-12-08 00:40:39 +00:00
netActor - > spr_extra = gameSprite - > extra ;
2010-08-02 08:13:51 +00:00
2012-12-12 02:53:13 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_CopyActorToNet ( const actor_t * gameActor , netactor_t * netActor )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
// (convert data from 32 bit integers)
2012-05-19 16:25:38 +00:00
2018-12-08 00:40:39 +00:00
Bassert ( gameActor ) ;
Bassert ( netActor ) ;
2012-05-19 16:25:38 +00:00
2018-12-08 00:40:39 +00:00
netActor - > t_data_0 = gameActor - > t_data [ 0 ] ;
netActor - > t_data_1 = gameActor - > t_data [ 1 ] ;
netActor - > t_data_2 = gameActor - > t_data [ 2 ] ;
netActor - > t_data_3 = gameActor - > t_data [ 3 ] ;
netActor - > t_data_4 = gameActor - > t_data [ 4 ] ;
netActor - > t_data_5 = gameActor - > t_data [ 5 ] ;
netActor - > t_data_6 = gameActor - > t_data [ 6 ] ;
netActor - > t_data_7 = gameActor - > t_data [ 7 ] ;
netActor - > t_data_8 = gameActor - > t_data [ 8 ] ;
netActor - > t_data_9 = gameActor - > t_data [ 9 ] ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
# ifdef LUNATIC
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > hvel = gameActor - > mv . hvel ;
netActor - > vvel = gameActor - > mv . vvel ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > startframe = gameActor - > ac . startframe ;
netActor - > numframes = gameActor - > ac . numframes ;
netActor - > viewtype = gameActor - > ac . viewtype ;
netActor - > incval = gameActor - > ac . incval ;
netActor - > delay = gameActor - > ac . delay ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > actiontics = gameActor - > actiontics ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
# endif
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
netActor - > flags = gameActor - > flags ;
2012-05-17 23:54:43 +00:00
2018-12-08 00:40:39 +00:00
netActor - > bpos_x = gameActor - > bpos . x ;
netActor - > bpos_y = gameActor - > bpos . y ;
netActor - > bpos_z = gameActor - > bpos . z ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
netActor - > floorz = gameActor - > floorz ;
netActor - > ceilingz = gameActor - > ceilingz ;
netActor - > lastvx = gameActor - > lastv . x ;
netActor - > lastvy = gameActor - > lastv . y ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
netActor - > lasttransport = gameActor - > lasttransport ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
//WARNING: both sprite and actor have these fields
netActor - > picnum = gameActor - > picnum ;
netActor - > ang = gameActor - > ang ;
netActor - > extra = gameActor - > extra ;
netActor - > owner = gameActor - > owner ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
netActor - > movflag = gameActor - > movflag ;
netActor - > tempang = gameActor - > tempang ;
netActor - > timetosleep = gameActor - > timetosleep ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
netActor - > stayput = gameActor - > stayput ;
netActor - > dispicnum = gameActor - > dispicnum ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
# if defined LUNATIC
//WARNING: NOT the same as movflag
netActor - > movflags = gameActor - > movflags ;
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
# endif
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
netActor - > cgg = gameActor - > cgg ;
2012-10-28 22:27:53 +00:00
}
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteExtToNet ( const spriteext_t * gameSpriteExt , netactor_t * netActor )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( gameSpriteExt ) ;
Bassert ( netActor ) ;
netActor - > ext_mdanimtims = gameSpriteExt - > mdanimtims ;
netActor - > ext_mdanimcur = gameSpriteExt - > mdanimcur ;
netActor - > ext_angoff = gameSpriteExt - > angoff ;
netActor - > ext_pitch = gameSpriteExt - > pitch ;
netActor - > ext_roll = gameSpriteExt - > roll ;
2013-01-28 22:00:37 +00:00
2018-12-08 00:40:39 +00:00
netActor - > ext_offset_x = gameSpriteExt - > offset . x ;
netActor - > ext_offset_y = gameSpriteExt - > offset . y ;
netActor - > ext_offset_z = gameSpriteExt - > offset . z ;
2013-01-28 22:00:37 +00:00
2018-12-08 00:40:39 +00:00
netActor - > ext_flags = gameSpriteExt - > flags ;
netActor - > ext_xpanning = gameSpriteExt - > xpanning ;
netActor - > ext_ypanning = gameSpriteExt - > ypanning ;
netActor - > ext_alpha = gameSpriteExt - > alpha ;
2012-12-12 02:53:13 +00:00
}
2011-03-04 08:50:58 +00:00
2018-12-08 00:40:39 +00:00
static void Net_CopySpriteSmoothToNet ( const spritesmooth_t * gameSprSmooth , netactor_t * netActor )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( gameSprSmooth ) ;
Bassert ( netActor ) ;
netActor - > sm_smoothduration = gameSprSmooth - > smoothduration ;
netActor - > sm_mdcurframe = gameSprSmooth - > mdcurframe ;
netActor - > sm_mdoldframe = gameSprSmooth - > mdoldframe ;
netActor - > sm_mdsmooth = gameSprSmooth - > mdsmooth ;
2012-12-12 02:53:13 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_CopyAllActorDataToNet ( int32_t spriteIndex , const spritetype * gameSprite , const actor_t * gameActor , const spriteext_t * gameSprExt , const spritesmooth_t * gameSprSmooth , netactor_t * netActor )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
Net_CopySpriteToNet ( gameSprite , netActor ) ;
Net_CopyActorToNet ( gameActor , netActor ) ;
Net_CopySpriteExtToNet ( gameSprExt , netActor ) ;
Net_CopySpriteSmoothToNet ( gameSprSmooth , netActor ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
netActor - > netIndex = spriteIndex ;
2012-12-12 02:53:13 +00:00
}
2018-12-08 00:40:39 +00:00
static void Net_AddActorsToSnapshot ( netmapstate_t * snapshot )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t gameIndex = 0 ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // we may want to only send over sprites that are visible, this might be a good optimization
// to do later.
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // Verify: Does the netcode need to worry about spriteext and spritesmooth beyond index (MAXSPRITES - 1)?
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // may be able to significantly improve performance by using headspritestat[] etc. lists
// i.e., replace with for(all stat) { for(all sprites in stat) { } }, ignoring net Non Relevant Stats,
// also then ioSnapshot->maxActorIndex could be something much less than MAXSPRITES
2013-01-28 22:00:37 +00:00
2018-12-08 00:40:39 +00:00
snapshot - > maxActorIndex = 0 ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
// note that Numsprites should NOT be the upper bound, if sprites are deleted in the middle
// the max index to check will be > than Numsprites.
for ( gameIndex = 0 ; gameIndex < ( MAXSPRITES ) ; gameIndex + + )
{
const spritetype * gameSpr = & sprite [ gameIndex ] ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
const actor_t * gameAct = & actor [ gameIndex ] ;
const spriteext_t * gameExt = & spriteext [ gameIndex ] ;
const spritesmooth_t * gameSmooth = & spritesmooth [ gameIndex ] ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
netactor_t * netSprite = & snapshot - > actor [ gameIndex ] ;
2012-10-28 22:27:53 +00:00
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Net_CopyAllActorDataToNet ( gameIndex , gameSpr , gameAct , gameExt , gameSmooth , netSprite ) ;
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
snapshot - > maxActorIndex = MAXSPRITES ;
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_AddWorldToSnapshot ( netmapstate_t * snapshot )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t index = 0 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
for ( index = 0 ; index < numwalls ; index + + )
{
// on the off chance that numwalls somehow gets set to higher than MAXWALLS... somehow...
Bassert ( index < MAXWALLS ) ;
const walltype * gameWall = & wall [ index ] ;
netWall_t * snapshotWall = & snapshot - > wall [ index ] ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Net_CopyWallToNet ( gameWall , snapshotWall , index ) ;
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
for ( index = 0 ; index < numsectors ; index + + )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Bassert ( index < MAXSECTORS ) ;
const sectortype * gameSector = & sector [ index ] ;
netSector_t * snapshotSector = & snapshot - > sector [ index ] ;
Net_CopySectorToNet ( gameSector , snapshotSector , index ) ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Net_AddActorsToSnapshot ( snapshot ) ;
}
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
//------------------------------------------------------------------------------------------------------------------------
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static void P_RemovePlayer ( int32_t p )
{
// server obviously can't leave the game, and index 0 shows up for disconnect events from
// players that haven't gotten far enough into the connection process to get a player ID
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( p < = 0 ) return ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
g_player [ p ] . playerquitflag = 0 ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Bsprintf ( recbuf , " %s^00 is history! " , g_player [ p ] . user_name ) ;
G_AddUserQuote ( recbuf ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( numplayers = = 1 )
S_PlaySound ( GENERIC_AMBIENCE17 ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
if ( screenpeek = = p )
screenpeek = myconnectindex ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
pub = NUMPAGES ;
pus = NUMPAGES ;
G_UpdateScreenArea ( ) ;
2012-05-19 16:25:52 +00:00
2018-12-08 00:40:39 +00:00
P_QuickKill ( g_player [ p ] . ps ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( voting = = p )
{
for ( p = 0 ; p < MAXPLAYERS ; p + + )
{
g_player [ p ] . vote = 0 ;
g_player [ p ] . gotvote = 0 ;
}
voting = - 1 ;
}
2012-10-28 22:27:53 +00:00
2019-12-03 23:28:28 +00:00
quoteMgr . InitializeQuote ( QUOTE_RESERVED2 , recbuf ) ;
2018-12-08 00:40:39 +00:00
g_player [ myconnectindex ] . ps - > ftq = QUOTE_RESERVED2 ;
g_player [ myconnectindex ] . ps - > fta = 180 ;
2012-10-28 22:27:53 +00:00
}
}
2012-10-14 21:52:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_SendNewPlayer ( int32_t newplayerindex )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
packbuf [ 0 ] = PACKET_NUM_PLAYERS ;
packbuf [ 1 ] = numplayers ;
packbuf [ 2 ] = g_mostConcurrentPlayers ;
packbuf [ 3 ] = ud . multimode ;
packbuf [ 4 ] = newplayerindex ;
packbuf [ 5 ] = g_networkMode ;
packbuf [ 6 ] = myconnectindex ;
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , 7 , ENET_PACKET_FLAG_RELIABLE ) ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Dbg_PacketSent ( PACKET_NUM_PLAYERS ) ;
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
static void Net_SendPlayerIndex ( int32_t index , ENetPeer * peer )
{
packbuf [ 0 ] = PACKET_PLAYER_INDEX ;
packbuf [ 1 ] = index ;
packbuf [ 2 ] = myconnectindex ;
enet_peer_send ( peer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , 3 , ENET_PACKET_FLAG_RELIABLE ) ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Dbg_PacketSent ( PACKET_PLAYER_INDEX ) ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
// sync a connecting player up with the current game state
static void Net_SyncPlayer ( ENetEvent * event )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t newPlayerIndex , j ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( numplayers + g_netPlayersWaiting > = MAXPLAYERS )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
enet_peer_disconnect_later ( event - > peer , DISC_SERVER_FULL ) ;
initprintf ( " Refused peer; server full. \n " ) ;
2012-05-19 16:25:52 +00:00
return ;
2012-10-28 22:27:53 +00:00
}
2011-03-05 01:41:38 +00:00
2018-12-08 00:40:39 +00:00
g_netPlayersWaiting + + ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
S_PlaySound ( DUKE_GETWEAPON2 ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// open a new slot if necessary and save off the resulting slot # for future reference
for ( TRAVERSE_CONNECT ( newPlayerIndex ) )
2014-02-10 11:00:25 +00:00
{
2018-12-08 00:40:39 +00:00
if ( g_player [ newPlayerIndex ] . playerquitflag = = 0 )
{
break ;
}
2014-02-10 11:00:25 +00:00
}
2018-12-08 00:40:39 +00:00
if ( newPlayerIndex = = - 1 )
{
newPlayerIndex = g_mostConcurrentPlayers + + ;
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // is it necessary to se event->peer->data to the new player index in Net_SyncPlayer?
event - > peer - > data = ( void * ) ( intptr_t ) newPlayerIndex ;
2010-08-02 08:13:51 +00:00
2019-08-27 13:39:54 +00:00
g_player [ newPlayerIndex ] . netsynctime = ( int32_t ) totalclock ;
2018-12-08 00:40:39 +00:00
g_player [ newPlayerIndex ] . playerquitflag = 1 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // Need to think of something better when finding a remaining slot for players.
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
for ( j = 0 ; j < g_mostConcurrentPlayers - 1 ; j + + )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
connectpoint2 [ j ] = j + 1 ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
connectpoint2 [ g_mostConcurrentPlayers - 1 ] = - 1 ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
G_MaybeAllocPlayer ( newPlayerIndex ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
g_netPlayersWaiting - - ;
+ + numplayers ;
+ + ud . multimode ;
Net_SendNewPlayer ( newPlayerIndex ) ;
Net_SendPlayerIndex ( newPlayerIndex , event - > peer ) ;
Net_SendClientInfo ( ) ;
Net_SendUserMapName ( ) ;
Net_SendNewGame ( 0 , event - > peer ) ; // newly connecting player (Net_SyncPlayer)
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void display_betascreen ( void )
{
rotatesprite_fs ( 160 < < 16 , 100 < < 16 , 65536 , 0 , BETASCREEN , 0 , 0 , 2 + 8 + 64 + BGSTRETCH ) ;
rotatesprite_fs ( 160 < < 16 , ( 104 ) < < 16 , 60 < < 10 , 0 , DUKENUKEM , 0 , 0 , 2 + 8 ) ;
rotatesprite_fs ( 160 < < 16 , ( 129 ) < < 16 , 30 < < 11 , 0 , THREEDEE , 0 , 0 , 2 + 8 ) ;
if ( PLUTOPAK ) // JBF 20030804
rotatesprite_fs ( 160 < < 16 , ( 151 ) < < 16 , 30 < < 11 , 0 , PLUTOPAKSPRITE + 1 , 0 , 0 , 2 + 8 ) ;
}
static void Net_Disconnect ( void )
{
if ( g_netClient )
{
ENetEvent event ;
if ( g_netClientPeer )
enet_peer_disconnect_later ( g_netClientPeer , 0 ) ;
while ( enet_host_service ( g_netClient , & event , 3000 ) > 0 )
{
switch ( event . type )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
case ENET_EVENT_TYPE_CONNECT :
case ENET_EVENT_TYPE_NONE :
case ENET_EVENT_TYPE_RECEIVE :
if ( event . packet )
enet_packet_destroy ( event . packet ) ;
break ;
case ENET_EVENT_TYPE_DISCONNECT :
numplayers = g_mostConcurrentPlayers = ud . multimode = 1 ;
myconnectindex = screenpeek = 0 ;
G_BackToMenu ( ) ;
break ;
2010-08-02 08:13:51 +00:00
}
}
2018-12-08 00:40:39 +00:00
enet_peer_reset ( g_netClientPeer ) ;
g_netClientPeer = NULL ;
enet_host_destroy ( g_netClient ) ;
g_netClient = NULL ;
return ;
}
if ( g_netServer )
{
int32_t peerIndex ;
ENetEvent event ;
for ( peerIndex = 0 ; peerIndex < ( signed ) g_netServer - > peerCount ; peerIndex + + )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
enet_peer_disconnect_later ( & g_netServer - > peers [ peerIndex ] , DISC_SERVER_QUIT ) ;
}
while ( enet_host_service ( g_netServer , & event , 3000 ) > 0 )
{
switch ( event . type )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
case ENET_EVENT_TYPE_CONNECT :
case ENET_EVENT_TYPE_NONE :
case ENET_EVENT_TYPE_RECEIVE :
case ENET_EVENT_TYPE_DISCONNECT :
if ( event . packet )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
enet_packet_destroy ( event . packet ) ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
break ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
}
enet_host_destroy ( g_netServer ) ;
g_netServer = NULL ;
}
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
// Only clients run this
static void Net_ReceiveDisconnect ( ENetEvent * event )
{
g_netDisconnect = 1 ;
numplayers = g_mostConcurrentPlayers = ud . multimode = 1 ;
myconnectindex = screenpeek = 0 ;
G_BackToMenu ( ) ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
switch ( event - > data )
{
case DISC_BAD_PASSWORD :
initprintf ( " Bad password. \n " ) ;
return ;
case DISC_VERSION_MISMATCH :
initprintf ( " Version mismatch. \n " ) ;
return ;
case DISC_INVALID :
initprintf ( " Invalid data detected. \n " ) ;
return ;
case DISC_SERVER_QUIT :
initprintf ( " The server is quitting. \n " ) ;
return ;
case DISC_SERVER_FULL :
initprintf ( " The server is full. \n " ) ;
return ;
case DISC_KICKED :
initprintf ( " You have been kicked from the server. \n " ) ;
return ;
case DISC_BANNED :
initprintf ( " You are banned from this server. \n " ) ;
return ;
default :
initprintf ( " Disconnected. \n " ) ;
return ;
}
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
static void Net_SendAcknowledge ( ENetPeer * client )
{
if ( ! g_netServer )
return ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
tempnetbuf [ 0 ] = PACKET_ACK ;
tempnetbuf [ 1 ] = myconnectindex ;
enet_peer_send ( client , CHAN_GAMESTATE , enet_packet_create ( & tempnetbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
Dbg_PacketSent ( PACKET_ACK ) ;
}
static void Net_ExtractPlayerUpdate ( playerupdate_t * update , int32_t type )
{
const int32_t playerindex = update - > playerindex ;
if ( playerindex ! = myconnectindex )
{
g_player [ playerindex ] . ps - > pos = update - > pos ;
g_player [ playerindex ] . ps - > opos = update - > opos ;
g_player [ playerindex ] . ps - > vel = update - > vel ;
g_player [ playerindex ] . ps - > q16ang = update - > q16ang ;
g_player [ playerindex ] . ps - > q16horiz = update - > q16horiz ;
g_player [ playerindex ] . ps - > q16horizoff = update - > q16horizoff ;
}
if ( type = = PACKET_MASTER_TO_SLAVE )
{
g_player [ playerindex ] . ping = update - > ping ;
g_player [ playerindex ] . ps - > dead_flag = update - > deadflag ;
g_player [ playerindex ] . playerquitflag = update - > playerquitflag ;
}
}
// Server only
static void Net_ReceiveClientUpdate ( ENetEvent * event )
{
int32_t playeridx ;
clientupdate_t update ;
if ( event - > packet - > dataLength ! = sizeof ( clientupdate_t ) )
{
return ;
}
Bmemcpy ( & update , ( char * ) event - > packet - > data , sizeof ( clientupdate_t ) ) ;
playeridx = ( int32_t ) ( intptr_t ) event - > peer - > data ;
if ( playeridx < 0 | | playeridx > = MAXPLAYERS )
{
return ;
}
g_player [ playeridx ] . revision = update . RevisionNumber ;
inputfifo [ 0 ] [ playeridx ] = update . nsyn ;
Net_ExtractPlayerUpdate ( & update . player , PACKET_SLAVE_TO_MASTER ) ;
}
static void Net_Server_SetupPlayer ( int playerindex )
{
int16_t playerspriteindex ;
playerspriteindex = g_player [ playerindex ] . ps - > i ;
Bmemcpy ( g_player [ playerindex ] . ps , g_player [ 0 ] . ps , sizeof ( DukePlayer_t ) ) ;
g_player [ playerindex ] . ps - > i = playerspriteindex ;
changespritestat ( playerspriteindex , STAT_PLAYER ) ;
g_player [ playerindex ] . ps - > last_extra = sprite [ g_player [ playerindex ] . ps - > i ] . extra = g_player [ playerindex ] . ps - > max_player_health ;
sprite [ g_player [ playerindex ] . ps - > i ] . cstat = 1 + 256 ;
actor [ g_player [ playerindex ] . ps - > i ] . t_data [ 2 ] = actor [ g_player [ playerindex ] . ps - > i ] . t_data [ 3 ]
= actor [ g_player [ playerindex ] . ps - > i ] . t_data [ 4 ] = 0 ;
P_ResetPlayer ( playerindex ) ;
Net_SpawnPlayer ( playerindex ) ;
}
static void Net_ReceiveChallenge ( uint8_t * pbuf , int32_t packbufleng , ENetEvent * event )
{
const uint16_t byteVersion = B_UNBUF16 ( & pbuf [ 1 ] ) ;
const uint16_t netVersion = B_UNBUF16 ( & pbuf [ 3 ] ) ;
const uint32_t crc = B_UNBUF32 ( & pbuf [ 5 ] ) ;
UNREFERENCED_PARAMETER ( packbufleng ) ; // remove when this variable is used
if ( byteVersion ! = BYTEVERSION | | netVersion ! = NETVERSION )
{
enet_peer_disconnect_later ( event - > peer , DISC_VERSION_MISMATCH ) ;
initprintf ( " Bad client protocol: version %u.%u \n " , byteVersion , netVersion ) ;
return ;
}
if ( crc ! = Bcrc32 ( ( uint8_t * ) g_netPassword , Bstrlen ( g_netPassword ) , 0 ) )
{
enet_peer_disconnect_later ( event - > peer , DISC_BAD_PASSWORD ) ;
initprintf ( " Bad password from client. \n " ) ;
return ;
}
Net_SyncPlayer ( event ) ;
}
static void Net_ReceiveMessage ( uint8_t * pbuf , int32_t packbufleng )
{
Bstrncpy ( recbuf , ( char * ) pbuf + 2 , packbufleng - 2 ) ;
recbuf [ packbufleng - 2 ] = 0 ;
G_AddUserQuote ( recbuf ) ;
S_PlaySound ( EXITMENUSOUND ) ;
pus = pub = NUMPAGES ;
}
static void Net_CheckForEnoughVotes ( )
{
// Only the server can decide map changes
if ( ! g_netServer | | numplayers < = 1 )
{
return ;
}
int32_t requiredvotes ;
// If there are just two players, both of them deserve a vote
if ( numplayers = = 2 )
{
requiredvotes = 2 ;
}
else
{
// If more than two players, we need at least 50% of the players to vote
// Which means that if there's an odd number of players, we'll need slightly more than 50% of the vote.
requiredvotes = numplayers / 2 ;
if ( numplayers % 2 = = 1 )
{
requiredvotes + + ;
2010-08-02 08:13:51 +00:00
}
}
2018-12-08 00:40:39 +00:00
int32_t numfor = 0 ;
int32_t numagainst = 0 ;
for ( int32_t playerIndex = 0 ; playerIndex < MAXPLAYERS ; playerIndex + + )
{
if ( g_player [ playerIndex ] . gotvote )
{
if ( g_player [ playerIndex ] . vote )
{
numfor + + ;
}
else
{
numagainst + + ;
}
}
}
if ( numfor > = requiredvotes )
{
Net_StartNewGame ( ) ;
Net_SendNewGame ( 1 , NULL ) ; // map vote
}
else if ( numagainst > = requiredvotes | | ( numfor + numagainst ) = = numplayers )
{
Net_SendMapVoteCancel ( 1 ) ;
}
}
static void Net_ReceiveMapVote ( uint8_t * pbuf )
{
if ( voting = = myconnectindex & & g_player [ ( uint8_t ) pbuf [ 1 ] ] . gotvote = = 0 )
{
Bsprintf ( tempbuf , " Confirmed vote from %s " , g_player [ ( uint8_t ) pbuf [ 1 ] ] . user_name ) ;
G_AddUserQuote ( tempbuf ) ;
}
if ( ! g_netServer )
{
return ;
}
g_player [ ( uint8_t ) pbuf [ 1 ] ] . gotvote = 1 ;
g_player [ ( uint8_t ) pbuf [ 1 ] ] . vote = pbuf [ 2 ] ;
Net_CheckForEnoughVotes ( ) ;
}
static void Net_ReceiveMapVoteCancel ( uint8_t * pbuf )
{
// Ignore if we're not voting
if ( voting = = - 1 )
{
return ;
}
// Ignore cancellations from clients that did not initiate the map vote
if ( voting ! = pbuf [ 1 ] & & voting ! = myconnectindex )
{
return ;
}
if ( voting = = myconnectindex | | voting ! = pbuf [ 1 ] )
{
Bsprintf ( tempbuf , " Vote Failed " ) ;
}
else if ( voting = = pbuf [ 1 ] )
{
Bsprintf ( tempbuf , " %s^00 has canceled the vote " , g_player [ voting ] . user_name ) ;
}
G_AddUserQuote ( tempbuf ) ;
if ( g_netServer )
{
Net_SendMapVoteCancel ( 0 ) ;
}
voting = - 1 ;
}
static void Net_ReceiveClientInfo ( uint8_t * pbuf , int32_t packbufleng , int32_t fromserver )
{
uint32_t byteIndex , j ;
int32_t other = pbuf [ packbufleng ] ;
for ( byteIndex = 1 ; pbuf [ byteIndex ] ; byteIndex + + )
{
g_player [ other ] . user_name [ byteIndex - 1 ] = pbuf [ byteIndex ] ;
}
g_player [ other ] . user_name [ byteIndex - 1 ] = 0 ;
byteIndex + + ;
g_player [ other ] . ps - > aim_mode = pbuf [ byteIndex + + ] ;
g_player [ other ] . ps - > auto_aim = pbuf [ byteIndex + + ] ;
g_player [ other ] . ps - > weaponswitch = pbuf [ byteIndex + + ] ;
g_player [ other ] . ps - > palookup = g_player [ other ] . pcolor = pbuf [ byteIndex + + ] ;
g_player [ other ] . pteam = pbuf [ byteIndex + + ] ;
for ( j = byteIndex ; byteIndex - j < 10 ; byteIndex + + )
{
g_player [ other ] . wchoice [ byteIndex - j ] = pbuf [ byteIndex ] ;
}
if ( fromserver )
{
g_player [ other ] . playerquitflag = 1 ;
}
}
static void Net_ReceiveUserMapName ( uint8_t * pbuf , int32_t packbufleng )
{
NET_75_CHECK + + ; // possible buffer bug here with a malicious client? if the file name didn't end in '\0', a hacker could "forget" to put '\0'
// at the end.
Bstrcpy ( boardfilename , ( char * ) pbuf + 1 ) ;
boardfilename [ packbufleng - 1 ] = 0 ;
Bcorrectfilename ( boardfilename , 0 ) ;
if ( boardfilename [ 0 ] ! = 0 )
{
2019-12-07 09:31:27 +00:00
if ( fileSystem . FileExists ( boardfilename ) )
2018-12-08 00:40:39 +00:00
{
Bmemset ( boardfilename , 0 , sizeof ( boardfilename ) ) ;
Net_SendUserMapName ( ) ;
}
}
2019-11-09 18:15:03 +00:00
if ( m_level_number = = 7 & & ud . m_volume_number = = 0 & & boardfilename [ 0 ] = = 0 )
m_level_number = 0 ;
2018-12-08 00:40:39 +00:00
}
static void Net_ExtractNewGame ( newgame_t * newgame , int32_t menuonly )
{
2019-11-09 18:15:03 +00:00
m_level_number = newgame - > level_number ;
2018-12-08 00:40:39 +00:00
ud . m_volume_number = newgame - > volume_number ;
ud . m_player_skill = newgame - > player_skill ;
ud . m_monsters_off = newgame - > monsters_off ;
ud . m_respawn_monsters = newgame - > respawn_monsters ;
ud . m_respawn_items = newgame - > respawn_items ;
ud . m_respawn_inventory = newgame - > respawn_inventory ;
2019-11-09 18:15:03 +00:00
m_ffire = newgame - > ffire ;
m_noexits = newgame - > noexits ;
m_coop = newgame - > coop ;
2018-12-08 00:40:39 +00:00
if ( ! menuonly )
{
ud . level_number = newgame - > level_number ;
ud . volume_number = newgame - > volume_number ;
ud . player_skill = newgame - > player_skill ;
ud . monsters_off = newgame - > monsters_off ;
ud . respawn_monsters = newgame - > respawn_monsters ;
ud . respawn_monsters = newgame - > respawn_items ;
ud . respawn_inventory = newgame - > respawn_inventory ;
ud . ffire = newgame - > ffire ;
ud . noexits = newgame - > noexits ;
ud . coop = newgame - > coop ;
}
}
static void Net_ReceiveMapVoteInitiate ( uint8_t * pbuf )
{
int32_t playerIndex ;
Bmemcpy ( & pendingnewgame , pbuf , sizeof ( newgame_t ) ) ;
Net_ExtractNewGame ( & pendingnewgame , 1 ) ;
voting = pendingnewgame . connection ;
vote_episode = pendingnewgame . volume_number ;
vote_map = pendingnewgame . level_number ;
Bsprintf ( tempbuf , " %s^00 has called a vote to change map to %s (E%dL%d) " , g_player [ voting ] . user_name ,
g_mapInfo [ ( uint8_t ) ( vote_episode * MAXLEVELS + vote_map ) ] . name , vote_episode + 1 , vote_map + 1 ) ;
G_AddUserQuote ( tempbuf ) ;
Bsprintf ( tempbuf , " Press F1 to Accept, F2 to Decline " ) ;
G_AddUserQuote ( tempbuf ) ;
for ( playerIndex = MAXPLAYERS - 1 ; playerIndex > = 0 ; playerIndex - - )
{
g_player [ playerIndex ] . vote = 0 ;
g_player [ playerIndex ] . gotvote = 0 ;
}
g_player [ voting ] . gotvote = g_player [ voting ] . vote = 1 ;
}
static void Net_ParsePacketCommon ( uint8_t * pbuf , int32_t packbufleng , int32_t serverpacketp )
{
switch ( pbuf [ 0 ] )
{
case PACKET_MESSAGE :
Net_ReceiveMessage ( pbuf , packbufleng ) ;
break ;
case PACKET_CLIENT_INFO :
Net_ReceiveClientInfo ( pbuf , packbufleng , serverpacketp ) ;
break ;
case PACKET_RTS :
G_StartRTS ( pbuf [ 1 ] , 0 ) ;
break ;
case PACKET_USER_MAP :
Net_ReceiveUserMapName ( pbuf , packbufleng ) ;
break ;
case PACKET_MAP_VOTE :
Net_ReceiveMapVote ( pbuf ) ;
break ;
case PACKET_MAP_VOTE_INITIATE : // call map vote
Net_ReceiveMapVoteInitiate ( pbuf ) ;
break ;
case PACKET_MAP_VOTE_CANCEL : // cancel map vote
Net_ReceiveMapVoteCancel ( pbuf ) ;
break ;
}
}
static void Net_ParseClientPacket ( ENetEvent * event )
{
uint8_t * pbuf = event - > packet - > data ;
int32_t packbufleng = event - > packet - > dataLength ;
int32_t other = pbuf [ - - packbufleng ] ;
NET_DEBUG_VAR enum DukePacket_t packetType = ( enum DukePacket_t ) pbuf [ 0 ] ;
# ifdef PACKET_RECV_PRINT
initprintf ( " Received Packet: type: %d : len %d \n " , pbuf [ 0 ] , packbufleng ) ;
# endif
switch ( pbuf [ 0 ] )
{
case PACKET_SLAVE_TO_MASTER : //[1] (receive slave sync buffer)
Net_ReceiveClientUpdate ( event ) ;
break ;
case PACKET_PLAYER_READY :
{
if ( ( other = = 0 ) | | ( other = = myconnectindex ) | | ( other > = MAXPLAYERS ) )
{
break ;
}
// At intermission, just set the ready flag and wait for the other players.
if ( g_player [ myconnectindex ] . ps - > gm & MODE_EOL )
{
g_player [ other ] . ready = 1 ;
}
else
{
// otherwise if the server's in a map now, set up the player immediately.
Net_Server_SetupPlayer ( other ) ;
}
break ;
}
case PACKET_PLAYER_PING :
if ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME )
{
packbuf [ 0 ] = PACKET_PLAYER_PING ;
packbuf [ 1 ] = myconnectindex ;
enet_peer_send ( event - > peer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
Dbg_PacketSent ( PACKET_PLAYER_PING ) ;
}
g_player [ other ] . pingcnt + + ;
break ;
2018-12-15 01:40:08 +00:00
case PACKET_AUTH :
Net_ReceiveChallenge ( pbuf , packbufleng , event ) ;
2018-12-08 00:40:39 +00:00
break ;
2018-12-15 01:40:08 +00:00
default :
Net_ParsePacketCommon ( pbuf , packbufleng , 0 ) ;
2018-12-08 00:40:39 +00:00
break ;
}
}
static void Net_HandleClientPackets ( void )
{
ENetEvent event ;
// pull events from the wire into the packet queue without dispatching them, once per Net_GetPackets() call
enet_host_service ( g_netServer , NULL , 0 ) ;
// dispatch any pending events from the local packet queue
while ( enet_host_check_events ( g_netServer , & event ) > 0 )
{
const intptr_t playeridx = ( intptr_t ) event . peer - > data ;
if ( playeridx < 0 | | playeridx > = MAXPLAYERS )
{
enet_peer_disconnect_later ( event . peer , DISC_INVALID ) ;
buildprint ( " Invalid player id ( " , playeridx , " ) from client. \n " ) ;
continue ;
}
switch ( event . type )
{
case ENET_EVENT_TYPE_CONNECT :
{
char ipaddr [ 32 ] ;
enet_address_get_host_ip ( & event . peer - > address , ipaddr , sizeof ( ipaddr ) ) ;
OSD_Printf ( " A new client connected from %s:%u. \n " , ipaddr , event . peer - > address . port ) ;
//[75] Temporary: For now the netcode can't handle more players connecting than there are player starts.
if ( g_playerSpawnCnt < = ud . multimode )
{
OSD_Printf ( " Connection dropped: No player spawn point available for this new player. " ) ;
break ;
}
Net_SendAcknowledge ( event . peer ) ;
break ;
}
case ENET_EVENT_TYPE_RECEIVE :
Net_ParseClientPacket ( & event ) ;
// broadcast takes care of enet_packet_destroy itself
// we set the state to disconnected so enet_host_broadcast
// doesn't send the player back his own packets
if ( ( event . channelID = = CHAN_GAMESTATE & & event . packet - > data [ 0 ] > PACKET_BROADCAST )
| | event . channelID = = CHAN_CHAT )
{
const ENetPacket * pak = event . packet ;
event . peer - > state = ENET_PEER_STATE_DISCONNECTED ;
enet_host_broadcast ( g_netServer , event . channelID ,
enet_packet_create ( pak - > data , pak - > dataLength , pak - > flags & ENET_PACKET_FLAG_RELIABLE ) ) ;
event . peer - > state = ENET_PEER_STATE_CONNECTED ;
}
enet_packet_destroy ( event . packet ) ;
g_player [ playeridx ] . ping = ( event . peer - > lastRoundTripTime + event . peer - > roundTripTime ) / 2 ;
break ;
case ENET_EVENT_TYPE_DISCONNECT :
numplayers - - ;
ud . multimode - - ;
P_RemovePlayer ( playeridx ) ;
g_player [ playeridx ] . revision = cInitialMapStateRevisionNumber ;
packbuf [ 0 ] = PACKET_PLAYER_DISCONNECTED ;
packbuf [ 1 ] = playeridx ;
packbuf [ 2 ] = numplayers ;
packbuf [ 3 ] = ud . multimode ;
packbuf [ 4 ] = g_mostConcurrentPlayers ;
packbuf [ 5 ] = myconnectindex ;
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE ,
enet_packet_create ( & packbuf [ 0 ] , 6 , ENET_PACKET_FLAG_RELIABLE ) ) ;
initprintf ( " %s disconnected. \n " , g_player [ playeridx ] . user_name ) ;
event . peer - > data = NULL ;
Dbg_PacketSent ( PACKET_PLAYER_DISCONNECTED ) ;
break ;
default :
break ;
}
}
}
static void Net_ReceiveServerUpdate ( ENetEvent * event )
{
serverupdate_t serverupdate ;
serverplayerupdate_t playerupdate ;
if ( ( ( event - > packet - > dataLength - sizeof ( serverupdate_t ) ) % sizeof ( serverplayerupdate_t ) ) ! = 0 )
{
return ;
}
uint8_t * updatebuf = ( uint8_t * ) event - > packet - > data ;
Bmemcpy ( & serverupdate , updatebuf , sizeof ( serverupdate_t ) ) ;
updatebuf + = sizeof ( serverupdate_t ) ;
inputfifo [ 0 ] [ 0 ] = serverupdate . nsyn ;
ud . pause_on = serverupdate . pause_on ;
ticrandomseed = serverupdate . seed ;
2019-05-19 03:56:13 +00:00
for ( int playerIndex = 0 ; playerIndex < serverupdate . numplayers ; + + playerIndex )
2018-12-08 00:40:39 +00:00
{
Bmemcpy ( & playerupdate , updatebuf , sizeof ( serverplayerupdate_t ) ) ;
updatebuf + = sizeof ( serverplayerupdate_t ) ;
Net_ExtractPlayerUpdate ( & playerupdate . player , PACKET_MASTER_TO_SLAVE ) ;
g_player [ playerIndex ] . ps - > gotweapon = playerupdate . gotweapon ;
g_player [ playerIndex ] . ps - > kickback_pic = playerupdate . kickback_pic ;
Bmemcpy ( g_player [ playerIndex ] . frags , playerupdate . frags , sizeof ( playerupdate . frags ) ) ;
Bmemcpy ( g_player [ playerIndex ] . ps - > inv_amount , playerupdate . inv_amount , sizeof ( playerupdate . inv_amount ) ) ;
Bmemcpy ( g_player [ playerIndex ] . ps - > ammo_amount , playerupdate . ammo_amount , sizeof ( playerupdate . ammo_amount ) ) ;
g_player [ playerIndex ] . ps - > curr_weapon = playerupdate . curr_weapon ;
g_player [ playerIndex ] . ps - > last_weapon = playerupdate . last_weapon ;
g_player [ playerIndex ] . ps - > wantweaponfire = playerupdate . wantweaponfire ;
g_player [ playerIndex ] . ps - > weapon_pos = playerupdate . weapon_pos ;
g_player [ playerIndex ] . ps - > frag_ps = playerupdate . frag_ps ;
g_player [ playerIndex ] . ps - > frag = playerupdate . frag ;
g_player [ playerIndex ] . ps - > fraggedself = playerupdate . fraggedself ;
g_player [ playerIndex ] . ps - > last_extra = playerupdate . last_extra ;
g_player [ playerIndex ] . ping = playerupdate . ping ;
g_player [ playerIndex ] . ps - > newowner = playerupdate . newowner ;
}
}
// sends the version and a simple crc32 of the current password, all verified by the server before the connection can continue
static void Net_SendChallenge ( )
{
if ( ! g_netClientPeer )
{
return ;
}
tempnetbuf [ 0 ] = PACKET_AUTH ;
B_BUF16 ( & tempnetbuf [ 1 ] , BYTEVERSION ) ;
B_BUF16 ( & tempnetbuf [ 3 ] , NETVERSION ) ;
B_BUF32 ( & tempnetbuf [ 5 ] , Bcrc32 ( ( uint8_t * ) g_netPassword , Bstrlen ( g_netPassword ) , 0 ) ) ;
tempnetbuf [ 9 ] = myconnectindex ;
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & tempnetbuf [ 0 ] , 10 , ENET_PACKET_FLAG_RELIABLE ) ) ;
Dbg_PacketSent ( PACKET_AUTH ) ;
}
static void Net_ReceiveAcknowledge ( uint8_t * pbuf , int32_t packbufleng )
{
UNREFERENCED_PARAMETER ( pbuf ) ; // remove when this variable is used
UNREFERENCED_PARAMETER ( packbufleng ) ; // remove when this variable is used
Net_SendChallenge ( ) ;
}
// client only
static void Net_ReceiveNewGame ( ENetEvent * event )
{
ClientPlayerReady = 0 ;
if ( ( vote_map + vote_episode + voting ) ! = - 3 )
G_AddUserQuote ( " Vote Succeeded " ) ;
Bmemcpy ( & pendingnewgame , event - > packet - > data , sizeof ( newgame_t ) ) ;
Net_StartNewGame ( ) ;
packbuf [ 0 ] = PACKET_PLAYER_READY ;
packbuf [ 1 ] = myconnectindex ;
if ( g_netClientPeer )
{
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
Dbg_PacketSent ( PACKET_PLAYER_READY ) ;
if ( g_netClient )
{
NET_75_CHECK + + ; // are these extra calls to create initial snapshots necessary or is it sufficient to just have it in
// G_EnterLevel?
/*
Net_InitMapStateHistory ( ) ;
Net_AddWorldToInitialSnapshot ( ) ;
*/
ClientPlayerReady = 1 ;
}
}
//[75] Note: DON'T set the initial map state or initialize the map state history in the packet code,
// The client didn't load the map until G_EnterLevel
g_player [ myconnectindex ] . ps - > gm = MODE_GAME ;
ready2send = 1 ;
}
static void Net_ReceiveNewPlayer ( uint8_t * pbuf , int32_t packbufleng )
{
int32_t i ;
UNREFERENCED_PARAMETER ( packbufleng ) ; // remove when this variable is used
numplayers = pbuf [ 1 ] ;
g_mostConcurrentPlayers = pbuf [ 2 ] ;
ud . multimode = pbuf [ 3 ] ;
if ( pbuf [ 4 ] ) // ID of new player
{
g_player [ pbuf [ 4 ] ] . playerquitflag = 1 ;
if ( ! g_player [ pbuf [ 4 ] ] . ps )
{
g_player [ pbuf [ 4 ] ] . ps = ( DukePlayer_t * ) Xcalloc ( 1 , sizeof ( DukePlayer_t ) ) ;
}
2019-07-08 00:41:17 +00:00
if ( ! g_player [ pbuf [ 4 ] ] . input )
2018-12-08 00:40:39 +00:00
{
2019-07-08 00:41:17 +00:00
g_player [ pbuf [ 4 ] ] . input = ( input_t * ) Xcalloc ( 1 , sizeof ( input_t ) ) ;
2018-12-08 00:40:39 +00:00
}
}
if ( pbuf [ 5 ] = = NET_DEDICATED_SERVER )
{
g_networkMode = NET_DEDICATED_CLIENT ;
}
for ( i = 0 ; i < g_mostConcurrentPlayers - 1 ; i + + )
{
connectpoint2 [ i ] = i + 1 ;
}
connectpoint2 [ g_mostConcurrentPlayers - 1 ] = - 1 ;
S_PlaySound ( DUKE_GETWEAPON2 ) ;
// myconnectindex is 0 until we get PACKET_PLAYER_INDEX
if ( myconnectindex ! = 0 )
{
Net_SendClientInfo ( ) ;
}
}
static void Net_ReceivePlayerIndex ( uint8_t * pbuf , int32_t packbufleng )
{
UNREFERENCED_PARAMETER ( packbufleng ) ; // remove when this variable is used
myconnectindex = pbuf [ 1 ] ;
g_player [ myconnectindex ] . playerquitflag = 1 ;
Net_SendClientInfo ( ) ;
}
static void Net_ParseServerPacket ( ENetEvent * event )
{
uint8_t * pbuf = event - > packet - > data ;
int32_t packbufleng = event - > packet - > dataLength ;
// input_t *nsyn;
NET_DEBUG_VAR enum DukePacket_t packetType = ( enum DukePacket_t ) pbuf [ 0 ] ;
- - packbufleng ;
# ifdef PACKET_RECV_PRINT
initprintf ( " Received Packet: type: %d : len %d \n " , pbuf [ 0 ] , packbufleng ) ;
# endif
switch ( pbuf [ 0 ] )
{
case PACKET_MASTER_TO_SLAVE :
if ( ! ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME ) )
{
return ;
}
Net_ReceiveServerUpdate ( event ) ;
break ;
case PACKET_NEW_GAME :
Net_ReceiveNewGame ( event ) ;
break ;
case PACKET_ACK :
Net_ReceiveAcknowledge ( pbuf , packbufleng ) ;
break ;
case PACKET_NUM_PLAYERS :
Net_ReceiveNewPlayer ( event - > packet - > data , event - > packet - > dataLength ) ;
break ;
case PACKET_PLAYER_INDEX :
Net_ReceivePlayerIndex ( event - > packet - > data , event - > packet - > dataLength ) ;
break ;
case PACKET_PLAYER_DISCONNECTED :
if ( ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME ) )
{
P_RemovePlayer ( pbuf [ 1 ] ) ;
}
numplayers = pbuf [ 2 ] ;
ud . multimode = pbuf [ 3 ] ;
g_mostConcurrentPlayers = pbuf [ 4 ] ;
break ;
case PACKET_PLAYER_SPAWN :
2018-12-15 01:40:08 +00:00
if ( ! ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME ) )
2018-12-08 00:40:39 +00:00
{
break ;
}
P_ResetPlayer ( pbuf [ 1 ] ) ;
Bmemcpy ( & g_player [ pbuf [ 1 ] ] . ps - > pos . x , & pbuf [ 2 ] , sizeof ( vec3_t ) * 2 ) ;
Bmemcpy ( & sprite [ g_player [ pbuf [ 1 ] ] . ps - > i ] , & pbuf [ 2 ] , sizeof ( vec3_t ) ) ;
break ;
case PACKET_PLAYER_PING :
g_player [ 0 ] . pingcnt + + ;
return ;
case PACKET_FRAG :
if ( ! ( g_player [ myconnectindex ] . ps - > gm & MODE_GAME ) )
{
break ;
}
g_player [ pbuf [ 1 ] ] . ps - > frag_ps = pbuf [ 2 ] ;
actor [ g_player [ pbuf [ 1 ] ] . ps - > i ] . picnum = pbuf [ 3 ] ;
ticrandomseed = B_UNBUF32 ( & pbuf [ 4 ] ) ;
P_FragPlayer ( pbuf [ 1 ] ) ;
break ;
// [75]
case PACKET_WORLD_UPDATE :
Net_ReadWorldUpdate ( pbuf , packbufleng ) ;
break ;
default :
Net_ParsePacketCommon ( pbuf , packbufleng , 1 ) ;
break ;
}
}
static void Net_HandleServerPackets ( void )
{
ENetEvent event ;
enet_host_service ( g_netClient , NULL , 0 ) ;
while ( enet_host_check_events ( g_netClient , & event ) > 0 )
{
if ( event . type = = ENET_EVENT_TYPE_DISCONNECT )
{
Net_ReceiveDisconnect ( & event ) ;
}
else if ( event . type = = ENET_EVENT_TYPE_RECEIVE )
{
Net_ParseServerPacket ( & event ) ;
}
enet_packet_destroy ( event . packet ) ;
}
}
////////////////////////////////////////////////////////////////////////////////
// Map Update Packets
//Insert a sprite from STAT_NETALLOC
static int32_t Net_DoInsertSprite ( int32_t sect , int32_t stat )
{
int32_t i = headspritestat [ STAT_NETALLOC ] ;
// This means that we've run out of server-side actors
if ( i < 0 )
{
Net_Error_Disconnect ( " Out of server side actors " ) ;
return i ;
}
changespritestat ( i , stat ) ;
do_insertsprite_at_headofsect ( i , sect ) ;
return i ;
}
////////////////////////////////////////////////////////////////////////////////
// Player Updates
static void Net_FillPlayerUpdate ( playerupdate_t * update , int32_t player )
{
update - > playerindex = player ;
update - > pos = g_player [ player ] . ps - > pos ;
update - > opos = g_player [ player ] . ps - > opos ;
update - > vel = g_player [ player ] . ps - > vel ;
update - > q16ang = g_player [ player ] . ps - > q16ang ;
update - > q16horiz = g_player [ player ] . ps - > q16horiz ;
update - > q16horizoff = g_player [ player ] . ps - > q16horizoff ;
update - > ping = g_player [ player ] . ping ;
update - > deadflag = g_player [ player ] . ps - > dead_flag ;
update - > playerquitflag = g_player [ player ] . playerquitflag ;
}
////////////////////////////////////////////////////////////////////////////////
// New Game Packets
// set all actors, walls, and sectors in a snapshot to their Null states.
static void Net_InitMapState ( netmapstate_t * mapState )
{
netactor_t sprDefault = cNullNetActor ;
int32_t index = 0 ;
sprDefault . netIndex = cSTOP_PARSING_CODE ;
mapState - > maxActorIndex = 0 ;
2018-12-15 01:40:08 +00:00
// this code is just a (slow) memset as it is now,
2018-12-08 00:40:39 +00:00
// it may be a good idea to use "baselines", which can reduce the amount
// of delta encoding when a sprite is first added. This
// could be a good optimization to consider later.
for ( index = 0 ; index < MAXSPRITES ; index + + )
{
mapState - > actor [ index ] = sprDefault ;
}
for ( index = 0 ; index < MAXWALLS ; index + + )
{
mapState - > wall [ index ] = cNullNetWall ;
}
for ( index = 0 ; index < MAXSECTORS ; index + + )
{
mapState - > sector [ index ] = cNullNetSector ;
}
// set the revision number to a valid but easy to identify number,
// this is just to make it easier to debug.
mapState - > revisionNumber = 8675309 ;
}
// Both client and server execute this
static void Net_ResetPlayers ( )
{
int32_t playerIndex ;
for ( TRAVERSE_CONNECT ( playerIndex ) )
{
P_ResetWeapons ( playerIndex ) ;
P_ResetInventory ( playerIndex ) ;
g_player [ playerIndex ] . revision = cInitialMapStateRevisionNumber ;
}
}
static void Net_ResetPlayerReady ( )
{
int32_t playerindex ;
for ( TRAVERSE_CONNECT ( playerindex ) )
{
g_player [ playerindex ] . ready = 0 ;
}
}
// Packet code
//---------------------------------------------------------------------------------------------------------------------------------
// enforce signed/unsigned and overflow behavior for WriteBits smaller than 32
// remember that all of the WriteX functions only take 32 bit integers as input...
# define PICKY_TYPECHECK 1
# define MAX_PACKET_STRING 1024
# define LOG_MESSAGE_BITS 1
2018-12-15 01:40:08 +00:00
# define NetNumberOfIndexes (1 << NETINDEX_BITS)
2018-12-08 00:40:39 +00:00
# define IndexesOK (MAXWALLS < NetNumberOfIndexes) && (MAXSPRITES < NetNumberOfIndexes) && (MAXSECTORS < NetNumberOfIndexes)
# if(!IndexesOK)
2019-03-21 02:24:19 +00:00
# error "network.cpp: game arrays are now too big to send over the network, please update NETINDEX_BITS to be the right length to store a wall, sprite, and sector index"
2018-12-08 00:40:39 +00:00
# endif
# define STRUCTINDEX_BITS 8 // hopefully no game structs ever have more than 255 fields....
# define DEBUG_CHANGEDINDEXES 1
# define FLOAT_INT_BITS 13
// remember that the minimum negative number is the sign bit + all zeros
const int32_t cTruncInt_Min = - ( 1 < < ( FLOAT_INT_BITS - 1 ) ) ;
const int32_t cTruncInt_Max = ( 1 < < ( FLOAT_INT_BITS - 1 ) ) - 1 ;
typedef struct NetBuffer_s
{
uint8_t * Data ;
int32_t Bit ;
int32_t ReadCurByte ;
int32_t CurSize ;
int32_t MaxSize ; // set in NetBuffer_Init to the size of the byte array that this struct referrs to in Data
} NetBuffer_t ;
// NOTE: does NOT fill byteArray with zeros
static void NetBuffer_Init ( NetBuffer_t * buffer , uint8_t * byteArray , int32_t arrayLength )
{
// fill all the entries of the netbuffer struct (reminder: the byte array is not part of the struct)
Bmemset ( buffer , 0 , sizeof ( * buffer ) ) ;
buffer - > Data = byteArray ;
buffer - > MaxSize = arrayLength ;
}
///<summary>
///guarantees that the destination string will be nul terminated
///</summary>
static void SafeStrncpyz ( char * dest , const char * src , int destsize )
{
if ( ! dest | | ! src | | ( destsize < = 0 ) )
{
Bassert ( dest ) ;
Bassert ( src ) ;
Bassert ( destsize > 0 ) ;
Net_Error_Disconnect ( " SafeStrncpyz: Invalid src, dest, or datasize. " ) ;
return ;
}
strncpy ( dest , src , destsize - 1 ) ;
dest [ destsize - 1 ] = 0 ;
}
// Write a bit (bitValue) to a byte array (dataBuffer) at bit offset bitOffset,
// this function increments bitOffset for you.
static void PutBit ( int32_t bitValue , uint8_t * dataBuffer , int32_t * bitOffset )
{
int32_t bitOffsetValue = * bitOffset ;
const int32_t byteIndex = bitOffsetValue > > 3 ;
const int32_t bitIndex = bitOffsetValue & 7 ;
// reset this byte to 0 on the first bit
if ( ( bitIndex ) = = 0 )
{
dataBuffer [ byteIndex ] = 0 ;
}
dataBuffer [ byteIndex ] | = ( bitValue < < ( bitIndex ) ) ;
bitOffsetValue + + ;
* bitOffset = bitOffsetValue ;
}
// Get a single bit (return value) from a byte array (dataBuffer) at bit offset bitOffset
// this function increments bitOffset for you.
static int32_t GetBit ( uint8_t * dataBuffer , int32_t * bitOffset )
{
int32_t bitValue = - 1 ;
int32_t bitOffsetValue = * bitOffset ;
const int32_t byteIndex = bitOffsetValue > > 3 ;
const int32_t bitIndex = bitOffsetValue & 7 ;
bitValue = ( dataBuffer [ byteIndex ] > > bitIndex ) & 0x1 ;
bitOffsetValue + + ;
* bitOffset = bitOffsetValue ;
return bitValue ;
}
//Note: This function increments bitOffset for you
static void PutBits ( int32_t value , uint8_t * dataBuffer , int32_t * bitOffset , int16_t numberOfBits )
{
int16_t bitIndex ;
if ( numberOfBits > 32 )
{
Net_Error_Disconnect ( " PutBits: Attempted to write more than 32 bits to the buffer. " ) ;
numberOfBits = 32 ;
}
for ( bitIndex = 0 ; bitIndex < numberOfBits ; bitIndex + + )
{
int32_t bitValue = value & ( 1 < < bitIndex ) ;
PutBit ( ( bitValue ! = 0 ) , dataBuffer , bitOffset ) ;
}
}
//Note: this function increments bitOffset for you
static int32_t GetBits ( uint8_t * dataBuffer , int32_t * bitOffset , int16_t numberOfBits )
{
int32_t value = 0 ;
int16_t bitIndex ;
if ( numberOfBits > 32 )
{
Net_Error_Disconnect ( " GetBits: Attempted to read more than 32 bits from the buffer. " ) ;
numberOfBits = 32 ;
}
for ( bitIndex = 0 ; bitIndex < numberOfBits ; bitIndex + + )
{
int32_t tBitValue = GetBit ( dataBuffer , bitOffset ) ;
value | = ( tBitValue < < bitIndex ) ;
}
return value ;
}
// only difference between this and GetBits is this one does not require you
// to pass in a pointer for the bit offset. Remember to not use semicolons in the watch list!
//
// use this to debug packets
//
// NOTE: Set to non-static just so the compiler won't optimize it out.
int32_t Dbg_GetBits ( NetBuffer_t * netBuffer , int32_t bitOffset , int16_t numberOfBits )
{
// I'm having GetBits modify a temp instead of the input parameter so that you can
// still see what the input parameter was at the end of the function.
int32_t bitOffsetValue = bitOffset ;
return GetBits ( netBuffer - > Data , & bitOffsetValue , numberOfBits ) ;
}
// intended for use in the debugger
// the netcode will return (32 bit) floats in a variable with type int32, though the data is actually
// floating point data. This function is here to make it easier to verify a (full) float encoded as int32
// in the debugger.
// note: floats that are actually truncated integers will usually look something like this: 3.922e-39#DEN
EDUKE32_UNUSED static float Dbg_RawIntToFloat ( NetChunk32 floatRawData )
{
float * floatPtr = ( float * ) & floatRawData ;
return * ( floatPtr ) ;
}
static void NetBuffer_WriteBits ( NetBuffer_t * netBuffer , int32_t data , int16_t numberOfBits )
{
if ( netBuffer - > CurSize > = netBuffer - > MaxSize )
{
Net_Error_Disconnect ( " NetBuffer_WriteBits: Buffer overrun. " ) ;
}
int32_t dataToWrite = data & ( 0xffffffff > > ( 32 - numberOfBits ) ) ;
PutBits ( dataToWrite , netBuffer - > Data , & netBuffer - > Bit , numberOfBits ) ;
netBuffer - > CurSize = ( netBuffer - > Bit > > 3 ) + 1 ;
}
EDUKE32_UNUSED static void NetBuffer_WriteUnsignedByte ( NetBuffer_t * netBuffer , int32_t byte )
{
# ifdef PICKY_TYPECHECK
if ( ( byte < 0 ) | | byte > 255 )
{
Net_Error_Disconnect ( " NetBuffer_WriteUnsignedByte: Value not valid. " ) ;
}
# endif
NetBuffer_WriteBits ( netBuffer , byte , 8 ) ;
}
EDUKE32_UNUSED static void NetBuffer_WriteSignedByte ( NetBuffer_t * netBuffer , int32_t byte )
{
# ifdef PICKY_TYPECHECK
if ( ( byte < - 128 ) | | ( byte > 127 ) )
{
Net_Error_Disconnect ( " NetBuffer_WriteSignedByte: Value not valid. " ) ;
}
# endif
NetBuffer_WriteBits ( netBuffer , byte , 8 ) ;
}
EDUKE32_UNUSED static void NetBuffer_WriteInt16 ( NetBuffer_t * netBuffer , int32_t word )
{
# ifdef PICKY_TYPECHECK
const int16_t cLowVal = ( int16_t ) 0x00008000 ;
const int16_t cHighVal = ( int16_t ) 0x00007fff ;
if ( ( word < cLowVal ) | | ( word > cHighVal ) )
{
Net_Error_Disconnect ( " NetBuffer_WriteInt16: Value not valid. " ) ;
}
# endif
NetBuffer_WriteBits ( netBuffer , word , 16 ) ;
}
EDUKE32_UNUSED static void NetBuffer_WriteUInt16 ( NetBuffer_t * netBuffer , int32_t word )
{
# ifdef PICKY_TYPECHECK
const uint16_t cLowVal = ( uint16_t ) 0x00000000 ;
const uint16_t cHighVal = ( uint16_t ) 0x0000ffff ;
if ( ( word < cLowVal ) | ( word > cHighVal ) )
{
Net_Error_Disconnect ( " NetBuffer_WriteUInt16: Value not valid. " ) ;
}
# endif
NetBuffer_WriteBits ( netBuffer , word , 16 ) ;
}
static void NetBuffer_WriteDword ( NetBuffer_t * netBuffer , int32_t dword )
{
// don't care about signed/unsigned since the bit width matches
NetBuffer_WriteBits ( netBuffer , dword , 32 ) ;
}
//where length is the size in bytes
static void NetBuffer_WriteData ( NetBuffer_t * netBuffer , const void * data , int16_t length )
{
int32_t byteIndex ;
for ( byteIndex = 0 ; byteIndex < length ; byteIndex + + )
{
NetBuffer_WriteUnsignedByte ( netBuffer , ( ( const int8_t * ) data ) [ byteIndex ] ) ;
}
}
EDUKE32_UNUSED static void NetBuffer_WriteString ( NetBuffer_t * netBuffer , const char * stringToWrite )
{
if ( ! stringToWrite )
{
// if stringToWrite is null, write a NUL ('\0') character
NetBuffer_WriteData ( netBuffer , " " , 1 ) ;
}
else
{
int32_t length ; // length of input string
char tempStringBuffer [ MAX_PACKET_STRING ] ; // temp storage
length = strlen ( stringToWrite ) ;
if ( length > = MAX_PACKET_STRING )
{
Net_Error_Disconnect ( " NetBuffer_WriteString: String too long. " ) ;
}
SafeStrncpyz ( tempStringBuffer , stringToWrite , sizeof ( tempStringBuffer ) ) ;
NetBuffer_WriteData ( netBuffer , tempStringBuffer , length + 1 ) ;
}
}
static void NetBuffer_WriteDeltaFloat ( NetBuffer_t * netBuffer , NetChunk32 rawFloatData )
{
if ( sizeof ( float ) ! = sizeof ( int32_t ) )
{
Net_Error_Disconnect ( " Can't write float to buffer: sizeof(float) must be 4 bytes " ) ;
}
// interpret the FOUR BYTES of raw float data as a float
float * floatDataPtr = ( float * ) & ( rawFloatData ) ;
float floatValue = * ( floatDataPtr ) ;
int32_t truncatedFloat = ( int32_t ) floatValue ;
if ( floatValue = = 0.0f )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // float is zeroed
}
else
{
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // float is not zeroed
// check if this float can be sent as a FLOAT_INT_BITS sized (*signed*) integer with no data loss
if (
( ( float ) truncatedFloat = = floatValue ) & &
( truncatedFloat > = cTruncInt_Min ) & &
( truncatedFloat < = cTruncInt_Max )
)
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // send float as small integer
NetBuffer_WriteBits ( netBuffer , truncatedFloat + cTruncInt_Max , FLOAT_INT_BITS ) ; // float as small integer data
}
else
{
// send as full floating point value
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // send full float
NetBuffer_WriteBits ( netBuffer , rawFloatData , 32 ) ; // full raw float data
}
}
}
//-------------------------------------------------------------------------------------
2018-12-15 01:40:08 +00:00
// low level buffer reading functions
2018-12-08 00:40:39 +00:00
static int32_t NetBuffer_ReadBits ( NetBuffer_t * netBuffer , int32_t numberOfBits )
{
int32_t value = GetBits ( netBuffer - > Data , & netBuffer - > Bit , numberOfBits ) ;
netBuffer - > ReadCurByte = ( netBuffer - > Bit > > 3 ) + 1 ;
return value ;
}
// returns -1 if no more characters are available
static int32_t NetBuffer_ReadByte ( NetBuffer_t * netBuffer )
{
int32_t byte ;
byte = ( int8_t ) NetBuffer_ReadBits ( netBuffer , 8 ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " NetBuffer_ReadByte: Attempted to read beyond end of buffer current size. " ) ;
}
return byte ;
}
EDUKE32_UNUSED static int32_t NetBuffer_ReadUnsignedByte ( NetBuffer_t * netBuffer )
{
int32_t byte ;
byte = ( uint8_t ) NetBuffer_ReadBits ( netBuffer , 8 ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " NetBuffer_ReadUnsignedByte: Attempted to read beyond end of buffer current size. " ) ;
}
return byte ;
}
EDUKE32_UNUSED static int32_t NetBuffer_ReadInt16 ( NetBuffer_t * netBuffer )
{
int32_t word ;
word = ( int16_t ) NetBuffer_ReadBits ( netBuffer , 16 ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " NetBuffer_ReadInt16: Attempted to read beyond end of buffer current size. " ) ;
}
return word ;
}
EDUKE32_UNUSED static int32_t NetBuffer_ReadUInt16 ( NetBuffer_t * netBuffer )
{
int32_t word ;
word = ( uint16_t ) NetBuffer_ReadBits ( netBuffer , 16 ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " NetBuffer_ReadUInt16: Attempted to read beyond end of buffer current size. " ) ;
}
return word ;
}
static int32_t NetBuffer_ReadDWord ( NetBuffer_t * netBuffer )
{
int32_t dword ;
dword = NetBuffer_ReadBits ( netBuffer , 32 ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " NetBuffer_ReadDword: Attempted to read beyond end of buffer current size. " ) ;
}
return dword ;
}
EDUKE32_UNUSED static void NetBuffer_ReadData ( NetBuffer_t * netBuffer , void * data , int32_t dataLength )
{
int32_t byteIndex ;
for ( byteIndex = 0 ; byteIndex < dataLength ; byteIndex + + )
{
( ( int8_t * ) data ) [ byteIndex ] = NetBuffer_ReadByte ( netBuffer ) ;
}
}
// net struct -> Buffer functions
//----------------------------------------------------------------------------------------------------------
static void NetBuffer_WriteDeltaNetWall ( NetBuffer_t * netBuffer , const netWall_t * from , const netWall_t * to )
{
const int32_t cMaxStructs = MAXWALLS ;
const int32_t cFieldsInStruct = ARRAY_SIZE ( WallFields ) ;
int32_t fieldIndex ,
maxChgIndex ;
netField_t * fieldPtr ;
const int32_t * fromField ,
* toField ;
// all fields should be 32 bits to avoid any compiler packing issues
// the "number" field is not part of the field list
//
// if this assert fails, check that all of the entries in net*_t are in the engine's type
Bassert ( cFieldsInStruct + 1 = = ( sizeof ( * from ) / 4 ) ) ;
Bassert ( to ) ;
Bassert ( from ) ;
if ( to - > netIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " Netbuffer_WriteDeltaNetWall: Invalid To NetIndex " ) ;
return ;
}
maxChgIndex = 0 ;
for ( fieldIndex = 0 , fieldPtr = WallFields ; fieldIndex < cFieldsInStruct ; fieldIndex + + , fieldPtr + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
if ( * fromField ! = * toField )
{
maxChgIndex = fieldIndex + 1 ;
}
}
if ( maxChgIndex = = 0 ) // no fields changed
{
return ; // write nothing at all
}
NetBuffer_WriteBits ( netBuffer , to - > netIndex , NETINDEX_BITS ) ;
NetBuffer_WriteBits ( netBuffer , maxChgIndex , STRUCTINDEX_BITS ) ;
for ( fieldIndex = 0 , fieldPtr = WallFields ; fieldIndex < maxChgIndex ; fieldIndex + + , fieldPtr + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
// // Bit(s) meaning
// //-------------------
if ( * fromField = = * toField )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // field not changed
continue ;
}
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // field changed
if ( * toField = = 0 )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // zero this field
}
else
{
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // don't zero this field
2018-12-15 01:40:08 +00:00
NetBuffer_WriteBits ( netBuffer , * toField , fieldPtr - > bits ) ; // new field value
2018-12-08 00:40:39 +00:00
}
}
}
static void NetBuffer_WriteDeltaNetSector ( NetBuffer_t * netBuffer , const netSector_t * from , const netSector_t * to )
{
const int32_t cMaxStructs = MAXSECTORS ;
const int32_t cFieldsInStruct = ARRAY_SIZE ( SectorFields ) ;
int32_t fieldIndex ,
maxChgIndex ;
netField_t * fieldPtr ;
const int32_t * fromField ,
* toField ;
// all fields should be 32 bits to avoid any compiler packing issues
// the "number" field is not part of the field list
//
// if this assert fails, check that all of the entries in net*_t are in the engine's type
Bassert ( cFieldsInStruct + 1 = = ( sizeof ( * from ) / 4 ) ) ;
Bassert ( to ) ;
Bassert ( from ) ;
if ( to - > netIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " Netbuffer_WriteDeltaNetSector: Invalid To NetIndex " ) ;
return ;
}
maxChgIndex = 0 ;
for ( fieldIndex = 0 , fieldPtr = SectorFields ; fieldIndex < cFieldsInStruct ; fieldIndex + + , fieldPtr + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
if ( * fromField ! = * toField )
{
maxChgIndex = fieldIndex + 1 ;
}
}
if ( maxChgIndex = = 0 ) // no fields changed
{
return ; // write nothing at all
}
NetBuffer_WriteBits ( netBuffer , to - > netIndex , NETINDEX_BITS ) ;
NetBuffer_WriteBits ( netBuffer , maxChgIndex , STRUCTINDEX_BITS ) ;
for ( fieldIndex = 0 , fieldPtr = SectorFields ; fieldIndex < maxChgIndex ; fieldIndex + + , fieldPtr + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
// // Bit(s) meaning
// //-------------------
if ( * fromField = = * toField )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // field not changed
continue ;
}
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // field changed
if ( * toField = = 0 )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // zero this field
}
else
{
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // don't zero this field
NetBuffer_WriteBits ( netBuffer , * toField , fieldPtr - > bits ) ; // new field value
}
}
}
static void NetBuffer_WriteDeltaNetActor ( NetBuffer_t * netBuffer , const netactor_t * from , const netactor_t * to , int8_t writeDeletedActors )
{
const int32_t cMaxStructs = MAXSPRITES ;
const int32_t cFieldsInStruct = ARRAY_SIZE ( ActorFields ) ;
int32_t fieldIndex ,
maxChgIndex ;
netField_t * fieldPtr ;
const int32_t * fromField ,
* toField ;
if ( ( from = = NULL ) & & ( to = = NULL ) )
{
// Actor was deleted in the "from" snapshot and it's still deleted in the "to" snasphot, don't write anything.
return ;
}
if ( from = = NULL )
{
// The actor was deleted in the "From" snapshot, but it's there in the "To" snapshot, so that means it has been inserted in the "To" snapshot.
from = & cNullNetActor ;
}
if ( to = = NULL )
{
// The actor was present in the "From" snapshot but it's now deleted in the "To" snapshot.
NetBuffer_WriteBits ( netBuffer , from - > netIndex , NETINDEX_BITS ) ; // {<NetIndex>} sprite index
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // {<NetIndex>, 1} sprite deleted
return ;
}
// all fields should be 32 bits to avoid any compiler packing issues
// the "number" field is not part of the field list
//
// if this assert fails, check that all of the entries in net*_t are in the engine's type
Bassert ( cFieldsInStruct + 1 = = ( sizeof ( * from ) / 4 ) ) ;
if ( to - > netIndex < 0 | | to - > netIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " Netbuffer_WriteDeltaNetActor: Invalid To NetIndex " ) ;
return ;
}
maxChgIndex = 0 ;
for ( fieldIndex = 0 , fieldPtr = ActorFields ; fieldIndex < cFieldsInStruct ; fieldIndex + + , fieldPtr + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
if ( * fromField ! = * toField )
{
maxChgIndex = fieldIndex + 1 ;
}
}
if ( maxChgIndex = = 0 )
{
if ( ! writeDeletedActors )
{
// no fields changed, if we're not forcing a write for each actor then we're done
return ;
}
// write two bits for no change
// as in, write {0,0} to indicate that the entity still exists,
// but has not changed.
NetBuffer_WriteBits ( netBuffer , to - > netIndex , NETINDEX_BITS ) ; // {<NetIndex>} sprite index
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // {<NetIndex>, 0} sprite NOT deleted
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // {<NetIndex>, 0,0} sprite has NOT changed.
return ;
}
// if we got to this point, the sprite / actor exists and has changed
NetBuffer_WriteBits ( netBuffer , to - > netIndex , NETINDEX_BITS ) ; // {<NetIndex>} sprite index
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // {<NetIndex>, 0} sprite/actor NOT deleted.
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // {<NetIndex>, 0,1} sprite/actor HAS changed.
//--------------------------------------------------
// then...
NetBuffer_WriteBits ( netBuffer , maxChgIndex , STRUCTINDEX_BITS ) ; // Write Max change index
// For each field in struct...
for ( fieldIndex = 0 , fieldPtr = ActorFields ; fieldIndex < maxChgIndex ; fieldIndex + + , fieldPtr + + )
{
const int8_t isFloatingPointField = ( fieldPtr - > bits = = 0 ) ;
fromField = ( int32_t const * ) ( ( int8_t const * ) from + fieldPtr - > offset ) ;
toField = ( int32_t const * ) ( ( int8_t const * ) to + fieldPtr - > offset ) ;
if ( * fromField = = * toField )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // {0} field not changed
continue ;
}
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // {1} field changed
if ( isFloatingPointField )
{
int32_t rawFloatData = * ( toField ) ;
NetBuffer_WriteDeltaFloat ( netBuffer , rawFloatData ) ; // {1, <special float behavior>}
}
else
{
if ( * toField = = 0 )
{
NetBuffer_WriteBits ( netBuffer , 0 , 1 ) ; // {1, 0} Zero this field
}
else
{
NetBuffer_WriteBits ( netBuffer , 1 , 1 ) ; // {1, 1} don't zero this int field
NetBuffer_WriteBits ( netBuffer , * toField , fieldPtr - > bits ) ; // {1, 1, <value>} new field value
}
}
}
}
static void Net_WriteNetActorsToBuffer ( NetBuffer_t * netBuffer , const netmapstate_t * from , const netmapstate_t * to )
{
const netactor_t * fromActor = NULL ;
const netactor_t * toActor = NULL ;
int16_t actorIndex = 0 ;
int16_t fromMaxIndex = 0 ;
if ( ! from )
{
fromMaxIndex = 0 ;
}
else
{
fromMaxIndex = from - > maxActorIndex ;
}
while (
actorIndex < to - > maxActorIndex
| |
actorIndex < fromMaxIndex
)
{
// load actor pointers using actor indexes
if ( actorIndex > = to - > maxActorIndex )
{
toActor = NULL ;
}
else
{
toActor = & to - > actor [ actorIndex ] ;
if ( toActor - > netIndex = = cSTOP_PARSING_CODE )
{
toActor = NULL ;
}
}
if ( actorIndex > = fromMaxIndex )
{
fromActor = NULL ;
}
else
{
fromActor = & from - > actor [ actorIndex ] ;
if ( fromActor - > netIndex = = cSTOP_PARSING_CODE )
{
fromActor = NULL ;
}
}
NetBuffer_WriteDeltaNetActor ( netBuffer , fromActor , toActor , 0 ) ;
actorIndex + + ;
}
}
static void Net_WriteWorldToBuffer ( NetBuffer_t * netBuffer , const netmapstate_t * fromSnapshot , const netmapstate_t * toSnapshot )
{
int32_t index = 0 ;
for ( index = 0 ; index < numwalls ; index + + )
{
Bassert ( index < MAXWALLS ) ;
const netWall_t * fromWall = & fromSnapshot - > wall [ index ] ;
const netWall_t * toWall = & toSnapshot - > wall [ index ] ;
NetBuffer_WriteDeltaNetWall ( netBuffer , fromWall , toWall ) ;
}
NetBuffer_WriteBits ( netBuffer , cSTOP_PARSING_CODE , NETINDEX_BITS ) ;
for ( index = 0 ; index < numsectors ; index + + )
{
Bassert ( index < MAXSECTORS ) ;
const netSector_t * fromSector = & fromSnapshot - > sector [ index ] ;
const netSector_t * toSector = & toSnapshot - > sector [ index ] ;
NetBuffer_WriteDeltaNetSector ( netBuffer , fromSector , toSector ) ;
}
NetBuffer_WriteBits ( netBuffer , cSTOP_PARSING_CODE , NETINDEX_BITS ) ;
Net_WriteNetActorsToBuffer ( netBuffer , fromSnapshot , toSnapshot ) ;
NetBuffer_WriteBits ( netBuffer , cSTOP_PARSING_CODE , NETINDEX_BITS ) ; // end of actors/sprites
}
// buffer -> net struct functions
//----------------------------------------------------------------------------------------------------------
2018-12-15 01:40:08 +00:00
// I agonized over whether to just make a more generic NetBuffer_ReadDeltaWorldEntry since there's so little
2018-12-08 00:40:39 +00:00
// difference between walls and sectors for this function but my attempts ended up being much harder
// to read and probably more error prone than using "clipboard" inheritance, I really hope I made the right call here...
static void NetBuffer_ReadDeltaWall ( NetBuffer_t * netBuffer , const netWall_t * from , netWall_t * to , uint16_t netIndex )
{
int32_t fieldIndex ,
maxChgIndex ; // the number of fields that changed in this delta struct from the server
const int32_t cMaxStructs = MAXWALLS ;
const int32_t cStructFields = ARRAY_SIZE ( WallFields ) ; // the number of fields in the full struct
netField_t * cFieldsArray = & ( WallFields [ 0 ] ) ;
netField_t * field ;
const NetChunk32 * fromField ;
NetChunk32 * toField ;
if ( netIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaWall: Bad Netindex to read. " ) ;
return ;
}
if ( from - > netIndex ! = netIndex )
{
Net_Error_Disconnect ( " Unexpected from struct netIndex. from struct netIndex should match the netIndex parameter. " ) ;
return ;
}
maxChgIndex = NetBuffer_ReadBits ( netBuffer , STRUCTINDEX_BITS ) ;
if ( maxChgIndex > cStructFields | | maxChgIndex < 0 )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaWall: Invalid delta field count from client. " ) ;
return ;
}
to - > netIndex = netIndex ;
// for each field of the delta struct
for ( fieldIndex = 0 , field = cFieldsArray ; fieldIndex < maxChgIndex ; fieldIndex + + , field + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( int32_t * ) ( ( int8_t * ) to + field - > offset ) ;
// no change to this field
if ( ! NetBuffer_ReadBits ( netBuffer , 1 ) )
{
* toField = * fromField ;
}
// field has changed
else
{
// zero the field
if ( NetBuffer_ReadBits ( netBuffer , 1 ) = = 0 )
{
* toField = 0 ;
}
// read the whole field
else
{
* toField = NetBuffer_ReadBits ( netBuffer , field - > bits ) ;
}
}
}
// if the delta for this struct doesn't include every field of the struct (likely)
// just set every field after the end of this delta struct to the "from" value
for ( fieldIndex = maxChgIndex , field = & cFieldsArray [ maxChgIndex ] ; fieldIndex < cStructFields ; fieldIndex + + , field + + )
{
fromField = ( NetChunk32 const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( NetChunk32 * ) ( ( int8_t * ) to + field - > offset ) ;
// no change on all the rest of the fields of the struct
* toField = * fromField ;
}
}
static void NetBuffer_ReadDeltaSector ( NetBuffer_t * netBuffer , const netSector_t * from , netSector_t * to , uint16_t netIndex )
{
int32_t fieldIndex ,
maxChgIndex ; // the number of fields that changed in this delta struct from the server
const int32_t cMaxStructs = MAXSECTORS ;
const int32_t cStructFields = ARRAY_SIZE ( SectorFields ) ; // the number of fields in the full struct
netField_t * cFieldsArray = & ( SectorFields [ 0 ] ) ;
netField_t * field ;
const NetChunk32 * fromField ;
NetChunk32 * toField ;
if ( netIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaSector: Bad Netindex to read. " ) ;
return ;
}
if ( from - > netIndex ! = netIndex )
{
Net_Error_Disconnect ( " Unexpected from struct netIndex. from struct netIndex should match the netIndex parameter. " ) ;
return ;
}
maxChgIndex = NetBuffer_ReadBits ( netBuffer , STRUCTINDEX_BITS ) ;
if ( maxChgIndex > cStructFields | | maxChgIndex < 0 )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaSector: Invalid delta field count from client. " ) ;
return ;
}
to - > netIndex = netIndex ;
// for each field of the delta struct
for ( fieldIndex = 0 , field = cFieldsArray ; fieldIndex < maxChgIndex ; fieldIndex + + , field + + )
{
fromField = ( int32_t const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( int32_t * ) ( ( int8_t * ) to + field - > offset ) ;
// no change to this field
if ( ! NetBuffer_ReadBits ( netBuffer , 1 ) )
{
* toField = * fromField ;
}
// field has changed
else
{
// zero the field
if ( NetBuffer_ReadBits ( netBuffer , 1 ) = = 0 )
{
* toField = 0 ;
}
// read the whole field
else
{
* toField = NetBuffer_ReadBits ( netBuffer , field - > bits ) ;
}
}
}
// if the delta for this struct doesn't include every field of the struct (likely)
// just set every field after the end of this delta struct to the "from" value
for ( fieldIndex = maxChgIndex , field = & cFieldsArray [ maxChgIndex ] ; fieldIndex < cStructFields ; fieldIndex + + , field + + )
{
// I am not sure if this is type punning or not, just to be safe
fromField = ( NetChunk32 const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( NetChunk32 * ) ( ( int8_t * ) to + field - > offset ) ;
// no change on all the rest of the fields of the struct
* toField = * fromField ;
}
}
static void NetBuffer_ReadDeltaActor ( NetBuffer_t * netBuffer , const netactor_t * from , netactor_t * to , uint16_t actorIndex )
{
int32_t fieldIndex ,
maxChgIndex ; // the number of fields that changed in this delta struct from the server
const int32_t cMaxStructs = MAXWALLS ;
const int32_t cStructFields = ARRAY_SIZE ( ActorFields ) ; // the number of fields in the full struct
netField_t * cFieldsArray = & ( ActorFields [ 0 ] ) ;
netField_t * field ;
const NetChunk32 * fromField ;
NetChunk32 * toField ;
int32_t removeActor ;
int32_t actorChanged ; // this is only used if the packet is sent with "force"
int32_t fromActorDeleted = ( from - > spr_sectnum = = MAXSECTORS ) ;
int32_t actorIndexOK = ( ( fromActorDeleted ) | | ( from - > netIndex = = actorIndex ) ) ;
if ( actorIndex > = cMaxStructs )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaActor: Bad Netindex to read. " ) ;
}
if ( ! actorIndexOK )
{
Net_Error_Disconnect ( " Unexpected from struct netIndex. from struct netIndex should match the netIndex parameter. " ) ;
return ;
}
removeActor = NetBuffer_ReadBits ( netBuffer , 1 ) ; // read actor deleted bit
// if this actor is being deleted, fill it with zeros and set its netIndex to STOP_PARSING_CODE
if ( removeActor = = 1 )
{
Net_InitNetActor ( to ) ;
return ;
}
actorChanged = NetBuffer_ReadBits ( netBuffer , 1 ) ; // read actor changed bit
to - > netIndex = actorIndex ;
if ( actorChanged = = 0 )
{
* to = * from ;
if ( to - > netIndex = = cSTOP_PARSING_CODE )
{
Net_Error_Disconnect ( " Read invalid netindex from delta actor buffer. " ) ;
}
return ;
}
maxChgIndex = NetBuffer_ReadBits ( netBuffer , STRUCTINDEX_BITS ) ; // max change index
if ( maxChgIndex > cStructFields | | maxChgIndex < 0 )
{
Net_Error_Disconnect ( " NetBuffer_ReadDeltaActor: Invalid delta field count from server. " ) ;
}
// for each field of the delta struct
for ( fieldIndex = 0 , field = cFieldsArray ; fieldIndex < maxChgIndex ; fieldIndex + + , field + + )
{
int8_t isFloatField = ( field - > bits = = 0 ) ;
fromField = ( int32_t const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( int32_t * ) ( ( int8_t * ) to + field - > offset ) ;
// no change to this field
if ( ! NetBuffer_ReadBits ( netBuffer , 1 ) )
{
* toField = * fromField ;
}
// field has changed
else
{
if ( isFloatField )
{
int32_t notZeroed = NetBuffer_ReadBits ( netBuffer , 1 ) ;
if ( notZeroed = = 0 )
{
* ( float * ) toField = 0.0f ;
}
else
{
int32_t sentAsRealFloat = NetBuffer_ReadBits ( netBuffer , 1 ) ;
if ( sentAsRealFloat = = 0 )
{
// float was written as a truncated FLOAT_INT_BITS integer
NetChunk32 truncated = NetBuffer_ReadBits ( netBuffer , FLOAT_INT_BITS ) ;
// remove bias to allow this to be signed
truncated - = cTruncInt_Max ;
* ( float * ) toField = ( float ) truncated ;
}
else
{
// read the raw float data directly from the buffer
* toField = NetBuffer_ReadBits ( netBuffer , 32 ) ;
}
}
}
// not a float field
else
{
// zero the field
if ( NetBuffer_ReadBits ( netBuffer , 1 ) = = 0 )
{
* toField = 0 ;
}
// read the whole field
else
{
* toField = NetBuffer_ReadBits ( netBuffer , field - > bits ) ;
}
}
}
}
// if the delta for this struct doesn't include every field of the struct (likely)
// just set every field after the end of this delta struct to the "from" value
for ( fieldIndex = maxChgIndex , field = & cFieldsArray [ maxChgIndex ] ; fieldIndex < cStructFields ; fieldIndex + + , field + + )
{
// I am really not 100% sure if this is type punning or not, just to be safe.
fromField = ( NetChunk32 const * ) ( ( int8_t const * ) from + field - > offset ) ;
toField = ( NetChunk32 * ) ( ( int8_t * ) to + field - > offset ) ;
// no change on all the rest of the fields of the struct
* toField = * fromField ;
}
if ( to - > netIndex = = cSTOP_PARSING_CODE )
{
Net_Error_Disconnect ( " Read invalid netindex from delta actor buffer. " ) ;
}
}
// Using oldSnapshot as the "From" snapshot, parse the data in netBuffer as a diff from
// oldSnapshot to make newSnapshot.
static void Net_ParseWalls ( NetBuffer_t * netBuffer , netmapstate_t * oldSnapshot , netmapstate_t * newSnapshot )
{
int32_t newSnapshotNetIndex = 0 ;
int32_t oldSnapshotNetIndex = 0 ;
netWall_t * oldSnapshotStruct = NULL ;
netWall_t * newSnapshotStruct = NULL ;
const int32_t cMaxStructIndex = numwalls - 1 ;
NET_DEBUG_VAR int32_t DEBUG_NumberOfStructsInPacket = 0 ;
NET_DEBUG_VAR int32_t DEBUG_FirstChangedIndex = - 1234 ;
NET_DEBUG_VAR int32_t DEBUG_LastChangedIndex = - 1234 ;
# if DEBUG_CHANGEDINDEXES
NET_DEBUG_VAR int32_t DEBUG_ChangedIndexes [ NetNumberOfIndexes ] ;
memset ( & DEBUG_ChangedIndexes , 0 , sizeof ( DEBUG_ChangedIndexes ) ) ;
# endif
//read each struct from the delta packet, until the NetIndex == the stop number
while ( 1 )
{
// read the netIndex of this struct
newSnapshotNetIndex = NetBuffer_ReadBits ( netBuffer , NETINDEX_BITS ) ;
if ( newSnapshotNetIndex < 0 )
{
Net_Error_Disconnect ( " Net_ParseWalls: Invalid netIndex from client. " ) ;
return ;
}
//================================================================
// DEBUG ONLY
if ( DEBUG_FirstChangedIndex < 0 )
{
DEBUG_FirstChangedIndex = newSnapshotNetIndex ;
}
if ( newSnapshotNetIndex ! = cSTOP_PARSING_CODE )
{
DEBUG_LastChangedIndex = newSnapshotNetIndex ;
}
//----------------------------------------------------------------
if ( newSnapshotNetIndex = = ( cSTOP_PARSING_CODE ) )
{
break ;
}
if ( newSnapshotNetIndex > cMaxStructIndex )
{
// this assert is only here to test the STOP_PARSING_CODE because we need that code to work for actors/sprites,
// since the number of walls/sectors does not change, it's safe to return here.
Bassert ( 0 ) ;
break ;
}
newSnapshotStruct = & ( newSnapshot - > wall [ newSnapshotNetIndex ] ) ;
oldSnapshotStruct = & ( oldSnapshot - > wall [ oldSnapshotNetIndex ] ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " Net_ParseWalls: Reached end of buffer without finding a stop code. " ) ;
return ;
}
// index up to the point where the changed structs start
while ( oldSnapshotNetIndex < newSnapshotNetIndex )
{
// if it's not in the delta entity set, just use the previous snapshot's value
newSnapshot - > wall [ oldSnapshotNetIndex ] = oldSnapshot - > wall [ oldSnapshotNetIndex ] ;
oldSnapshotNetIndex + + ;
oldSnapshotStruct = & ( oldSnapshot - > wall [ oldSnapshotNetIndex ] ) ;
}
// the struct referred to by oldindex is the same struct as the one referred to by newindex,
// compare the two structs
if ( oldSnapshotNetIndex = = newSnapshotNetIndex )
{
NetBuffer_ReadDeltaWall ( netBuffer , oldSnapshotStruct , newSnapshotStruct , newSnapshotNetIndex ) ;
oldSnapshotNetIndex + + ;
DEBUG_NumberOfStructsInPacket + + ;
# if DEBUG_CHANGEDINDEXES
DEBUG_ChangedIndexes [ newSnapshotNetIndex ] = 1 ;
# endif
}
}
// No more walls changed for the new snapshot, but there's still walls in the old snapshot.
//
// all of those old walls are unchanged. Copy all of these into the new snapshot.
while ( oldSnapshotNetIndex < numwalls )
{
newSnapshot - > wall [ oldSnapshotNetIndex ] = oldSnapshot - > wall [ oldSnapshotNetIndex ] ;
oldSnapshotNetIndex + + ;
}
}
static void Net_ParseSectors ( NetBuffer_t * netBuffer , netmapstate_t * oldSnapshot , netmapstate_t * newSnapshot )
{
int32_t newSnapshotNetIndex = 0 ;
int32_t oldSnapshotNetIndex = 0 ;
netSector_t * oldSnapshotStruct = NULL ;
netSector_t * newSnapshotStruct = NULL ;
const int32_t cMaxStructIndex = numsectors - 1 ;
NET_DEBUG_VAR int32_t DEBUG_PrevNewIndex = - 1 ;
NET_DEBUG_VAR int32_t DEBUG_FirstChangedIndex = - 1234 ;
NET_DEBUG_VAR int32_t DEBUG_LastChangedIndex = - 1234 ;
# if DEBUG_CHANGEDINDEXES
NET_DEBUG_VAR int32_t DEBUG_ChangedIndexes [ NetNumberOfIndexes ] ;
memset ( & DEBUG_ChangedIndexes , 0 , sizeof ( DEBUG_ChangedIndexes ) ) ;
# endif
//read each struct from the delta packet, until the NetIndex == the stop number
while ( 1 )
{
// read the netIndex of this struct
newSnapshotNetIndex = NetBuffer_ReadBits ( netBuffer , NETINDEX_BITS ) ;
//================================================================
// DEBUG ONLY
if ( DEBUG_FirstChangedIndex < 0 )
{
DEBUG_FirstChangedIndex = newSnapshotNetIndex ;
}
if ( newSnapshotNetIndex ! = cSTOP_PARSING_CODE )
{
DEBUG_LastChangedIndex = newSnapshotNetIndex ;
}
//----------------------------------------------------------------
if ( newSnapshotNetIndex < DEBUG_PrevNewIndex )
{
Net_Error_Disconnect ( " Error: Sectors were recieved out of order. " ) ;
}
DEBUG_PrevNewIndex = newSnapshotNetIndex ;
if ( newSnapshotNetIndex < 0 )
{
Net_Error_Disconnect ( " Net_ParseSectors: Invalid netIndex from client. " ) ;
return ;
}
if ( newSnapshotNetIndex = = ( cSTOP_PARSING_CODE ) )
{
break ;
}
if ( newSnapshotNetIndex > cMaxStructIndex )
{
// this assert is only here to test the STOP_PARSING_CODE because we need that code to work for actors/sprites,
// since the number of walls/sectors does not change, it's safe to return here.
//
// also this can help catch corrupted packets
Bassert ( 0 ) ;
break ;
}
newSnapshotStruct = & ( newSnapshot - > sector [ newSnapshotNetIndex ] ) ;
oldSnapshotStruct = & ( oldSnapshot - > sector [ oldSnapshotNetIndex ] ) ;
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " Net_ParseSectors: Reached end of buffer without finding a stop code. " ) ;
return ;
}
// index up to the point where the changed structs start
while ( oldSnapshotNetIndex < newSnapshotNetIndex )
{
// if it's not in the delta entity set, just use the previous snapshot's value
newSnapshot - > sector [ oldSnapshotNetIndex ] = oldSnapshot - > sector [ oldSnapshotNetIndex ] ;
oldSnapshotNetIndex + + ;
oldSnapshotStruct = & ( oldSnapshot - > sector [ oldSnapshotNetIndex ] ) ;
}
// the struct referred to by oldindex is the same struct as the one referred to by newindex,
// compare the two structs
if ( oldSnapshotNetIndex = = newSnapshotNetIndex )
{
NetBuffer_ReadDeltaSector ( netBuffer , oldSnapshotStruct , newSnapshotStruct , newSnapshotNetIndex ) ;
oldSnapshotNetIndex + + ;
# if DEBUG_CHANGEDINDEXES
DEBUG_ChangedIndexes [ newSnapshotNetIndex ] = 1 ;
# endif
}
}
// No more sectors changed for the new snapshot, but there's still sectors in the old snapshot.
//
// all of those old sectors are unchanged. Copy all of these into the new snapshot.
while ( oldSnapshotNetIndex < numsectors )
{
newSnapshot - > sector [ oldSnapshotNetIndex ] = oldSnapshot - > sector [ oldSnapshotNetIndex ] ;
oldSnapshotNetIndex + + ;
}
}
static void Net_ParseActors ( NetBuffer_t * netBuffer , const netmapstate_t * oldSnapshot , netmapstate_t * newSnapshot )
{
// FROM PACKET: index into sprite[] (game arrays)
int32_t newActorIndex = 0 ;
// INCREMENTED: index into sprite[] (game arrays)
int32_t oldActorIndex = 0 ;
const netactor_t * oldSnapshotStruct = NULL ;
netactor_t * newSnapshotStruct = NULL ;
const int32_t cActorIndex_OutOfOldFrameActors = 99999 ;
NET_DEBUG_VAR int32_t DEBUG_FirstChangedIndex = - 1234 ;
NET_DEBUG_VAR int32_t DEBUG_LastChangedIndex = - 1234 ;
# if DEBUG_CHANGEDINDEXES
NET_DEBUG_VAR int32_t DEBUG_ChangedIndexes [ NetNumberOfIndexes ] ;
memset ( & DEBUG_ChangedIndexes , 0 , sizeof ( DEBUG_ChangedIndexes ) ) ;
# endif
newSnapshot - > maxActorIndex = 0 ;
if ( ! oldSnapshot )
{
oldActorIndex = cActorIndex_OutOfOldFrameActors ;
}
else
{
oldSnapshotStruct = & oldSnapshot - > actor [ oldActorIndex ] ;
}
//read each struct from the delta packet, until the NetIndex == the stop number
//i.e., for each actor in the actors section of the packet...
while ( 1 )
{
newActorIndex = NetBuffer_ReadBits ( netBuffer , NETINDEX_BITS ) ;
//================================================================
// DEBUG ONLY
if ( DEBUG_FirstChangedIndex < 0 )
{
DEBUG_FirstChangedIndex = newActorIndex ;
}
if ( newActorIndex ! = cSTOP_PARSING_CODE )
{
DEBUG_LastChangedIndex = newActorIndex ;
}
//----------------------------------------------------------------
if ( newActorIndex = = cSTOP_PARSING_CODE )
{
//NOTE: This is okay, there is a while loop below to handle cases
// where there are unchanged actors after the last changed actor in the
// netbuffer.
break ;
}
if ( netBuffer - > ReadCurByte > netBuffer - > CurSize )
{
Net_Error_Disconnect ( " Net_ParseActors: Reached end of buffer without finding a stop code. " ) ;
}
if ( ( newActorIndex < 0 ) | | ( newActorIndex > MAXSPRITES ) )
{
Net_Error_Disconnect ( " Net_ParseActors: Invalid netIndex from client. " ) ;
}
// loop up to the point where the structs changed between the old frame and the new frame start
while ( oldActorIndex < newActorIndex )
{
//actor is unchanged in the new snapshot, just *copy* the previous snapshot's value
newSnapshot - > actor [ oldActorIndex ] = oldSnapshot - > actor [ oldActorIndex ] ;
oldActorIndex + + ;
}
// NOTE that actors deleted for the new snapshot will be processed *here*. New actors that "fill in gaps"
// (rather than being added to the end) in the actor list will be processed here too.
if ( oldActorIndex = = newActorIndex )
{
oldSnapshotStruct = & ( oldSnapshot - > actor [ oldActorIndex ] ) ;
newSnapshotStruct = & ( newSnapshot - > actor [ newActorIndex ] ) ;
NetBuffer_ReadDeltaActor ( netBuffer , oldSnapshotStruct , newSnapshotStruct , newActorIndex ) ;
oldActorIndex + + ;
# if DEBUG_CHANGEDINDEXES
DEBUG_ChangedIndexes [ newSnapshotStruct - > netIndex ] = 1 ;
# endif
continue ; // because we handled the newActorIndex read from the packet.
}
// This actor is a newly inserted actor, either to be put in a space between
// two actors in the old snapshot, or after the end of the old snapshot.
//
// Note that the "no more oldframe entities" index constant runs this
if ( oldActorIndex > newActorIndex )
{
newSnapshotStruct = & ( newSnapshot - > actor [ newActorIndex ] ) ;
NetBuffer_ReadDeltaActor ( netBuffer , & cNullNetActor , newSnapshotStruct , newActorIndex ) ;
}
}
// No more actors changed for the new snapshot, but there's still actors in the old snapshot.
//
// All of those old actors are unchanged. Copy all of these into the new snapshot.
// Remember that deleting an actor counts as a "change".
while ( oldActorIndex < MAXSPRITES )
{
newSnapshot - > actor [ oldActorIndex ] = oldSnapshot - > actor [ oldActorIndex ] ;
oldActorIndex + + ;
}
NET_75_CHECK + + ; // For now every snapshot will have MAXSPRITES entries
newSnapshot - > maxActorIndex = MAXSPRITES ;
}
static void NetBuffer_ReadWorldSnapshotFromBuffer ( NetBuffer_t * netBuffer , netmapstate_t * oldSnapshot , netmapstate_t * newSnapshot )
{
// note that the order these functions are called should match the order that these structs are written to
// by the server
Net_ParseWalls ( netBuffer , oldSnapshot , newSnapshot ) ;
Net_ParseSectors ( netBuffer , oldSnapshot , newSnapshot ) ;
Net_ParseActors ( netBuffer , oldSnapshot , newSnapshot ) ;
}
static void Net_SendWorldUpdate ( uint32_t fromRevisionNumber , uint32_t toRevisionNumber , int32_t sendToPlayerIndex )
{
if ( sendToPlayerIndex = = myconnectindex )
{
return ;
}
Bassert ( MAX_WORLDBUFFER = = ARRAY_SIZE ( tempnetbuf ) ) ;
Bassert ( NET_REVISIONS = = ARRAY_SIZE ( g_mapStateHistory ) ) ;
uint32_t playerRevisionIsTooOld = ( toRevisionNumber - fromRevisionNumber ) > NET_REVISIONS ;
// to avoid the client thinking that revision 2 is older than revision 0xFFFF_FFFF,
// send packets to take the client from the map's initial state until the client reports back
// that it's beyond that rollover threshold.
uint32_t revisionInRolloverState = ( fromRevisionNumber > toRevisionNumber ) ;
NetBuffer_t buffer ;
NetBuffer_t * bufferPtr = & buffer ;
// note: not enough stack memory to put the world data as a local variable
uint8_t * byteBuffer = & tempnetbuf [ 1 ] ;
uint32_t fromRevisionNumberToSend = 0x86753090 ;
netmapstate_t * toMapState = & g_mapStateHistory [ toRevisionNumber % NET_REVISIONS ] ;
netmapstate_t * fromMapState = NULL ;
2018-12-15 01:40:08 +00:00
NET_75_CHECK + + ; // during the rollover state it might be a good idea to init the map state history?
2018-12-08 00:40:39 +00:00
// maybe not? I do init map states before using them, so it might not be needed.
if ( playerRevisionIsTooOld | | revisionInRolloverState )
{
fromMapState = & g_mapStartState ;
fromRevisionNumberToSend = cInitialMapStateRevisionNumber ;
}
else
{
uint32_t tFromRevisionIndex = fromRevisionNumber % NET_REVISIONS ;
fromMapState = & g_mapStateHistory [ tFromRevisionIndex ] ;
fromRevisionNumberToSend = fromRevisionNumber ;
}
2019-01-12 00:21:44 +00:00
Bmemset ( byteBuffer , 0 , sizeof ( tempnetbuf ) - 1 ) ;
2018-12-08 00:40:39 +00:00
tempnetbuf [ 0 ] = PACKET_WORLD_UPDATE ;
NetBuffer_Init ( bufferPtr , byteBuffer , MAX_WORLDBUFFER ) ;
NetBuffer_WriteDword ( bufferPtr , fromRevisionNumberToSend ) ;
NetBuffer_WriteDword ( bufferPtr , toRevisionNumber ) ;
Net_WriteWorldToBuffer ( bufferPtr , fromMapState , toMapState ) ;
if ( sendToPlayerIndex > ( ( int32_t ) g_netServer - > peerCount ) )
{
Net_Error_Disconnect ( " No peer for player. " ) ;
return ;
}
// in the future we could probably use these flags for enet_peer_send, for the world updates
EDUKE32_UNUSED const ENetPacketFlag optimizedFlags = ( ENetPacketFlag ) ( ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT ) ;
NET_75_CHECK + + ; // HACK: I Really need to keep the peer with the player instead of assuming that the peer index is the same as the (player index - 1)
ENetPeer * const tCurrentPeer = & g_netServer - > peers [ sendToPlayerIndex - 1 ] ;
enet_peer_send ( tCurrentPeer , CHAN_GAMESTATE , enet_packet_create ( & tempnetbuf , bufferPtr - > CurSize + 1 , 0 ) ) ;
Dbg_PacketSent ( PACKET_WORLD_UPDATE ) ;
}
static void Net_CopySnapshotToGameArrays ( netmapstate_t * srv_snapshot , netmapstate_t * cl_snapshot )
{
int32_t index ;
2018-12-15 01:40:08 +00:00
// this isn't just copying the entries that "changed" into the game arrays,
2018-12-08 00:40:39 +00:00
// remember that we need to correct any incorrect guesses the client made.
// don't do memcpy either, e.g., sizeof(netWall_t) != sizeof(walltype)
for ( index = 0 ; index < numwalls ; index + + )
{
netWall_t * srvWall = & ( srv_snapshot - > wall [ index ] ) ;
netWall_t * clWall = & ( cl_snapshot - > wall [ index ] ) ;
int status = memcmp ( srvWall , clWall , sizeof ( netWall_t ) ) ;
if ( status = = 0 )
{
if ( g_enableClientInterpolationCheck )
{
// only copy in the server's (old) value if the client's interpolation up to that point was incorrect
continue ;
}
}
walltype * gameWall = & ( wall [ index ] ) ;
Net_CopyWallFromNet ( srvWall , gameWall ) ;
}
for ( index = 0 ; index < numsectors ; index + + )
{
netSector_t * srvSector = & ( srv_snapshot - > sector [ index ] ) ;
netSector_t * clSector = & ( cl_snapshot - > sector [ index ] ) ;
int status = memcmp ( srvSector , clSector , sizeof ( netSector_t ) ) ;
if ( status = = 0 )
{
if ( g_enableClientInterpolationCheck )
{
continue ;
}
}
sectortype * gameSector = & ( sector [ index ] ) ;
Net_CopySectorFromNet ( srvSector , gameSector ) ;
}
Net_CopyActorsToGameArrays ( srv_snapshot , cl_snapshot ) ;
}
// clients only
static void Net_ReadWorldUpdate ( uint8_t * packetData , int32_t packetSize )
{
if ( ! g_netClient )
{
return ;
}
NetBuffer_t buffer ;
NetBuffer_t * bufferPtr = & buffer ;
uint8_t * dataStartAddr = packetData + 1 ;
NET_DEBUG_VAR int16_t DEBUG_NoMapLoaded = ( ( numwalls < 1 ) | | ( numsectors < 1 ) ) ;
NET_DEBUG_VAR int32_t DEBUG_InitialSnapshotNotSet = ( g_mapStartState . sector [ 0 ] . wallnum < = 0 ) ;
if ( ! ClientPlayerReady )
{
return ;
}
NetBuffer_Init ( bufferPtr , dataStartAddr , MAX_WORLDBUFFER ) ;
bufferPtr - > CurSize = packetSize - 1 ;
uint32_t packetFromRevisionNumber = NetBuffer_ReadDWord ( bufferPtr ) ;
uint32_t packetToRevisionNumber = NetBuffer_ReadDWord ( bufferPtr ) ;
uint32_t from_IsInitialState = ( packetFromRevisionNumber = = cInitialMapStateRevisionNumber ) ;
uint32_t clientRevisionIsTooOld = ( packetToRevisionNumber - g_netMapRevisionNumber ) > NET_REVISIONS ;
netmapstate_t * fromMapState = NULL ;
if ( clientRevisionIsTooOld & & ! from_IsInitialState )
{
// this is actually not a major problem, an alternative way to handle this would be to just ignore the packet and hope the
// server sends a diff from the initial revision eventually.
//
// NOTE: If you get this bug on connect it really is a problem, the client never seems to get updated to the initial revision when
// this happens. It seems like if you don't wat enough time before connecting again after a crash you will get this to happen.
//
// actually I've seen that happen even when this error didn't happen.
Net_Error_Disconnect ( " Internal Error: Net_ReadWorldUpdate(): Client From map state too old, but server did not send snapshot from initial state. " ) ;
return ;
}
if ( from_IsInitialState )
{
// clients must always accept the initial map state as a snapshot,
2018-12-15 01:40:08 +00:00
// If a client's current revision is too old, the server will send them
// a snapshot to update them from the initial state instead of the
2018-12-08 00:40:39 +00:00
// client's last known state.
fromMapState = & g_mapStartState ;
}
else if ( packetToRevisionNumber < = g_netMapRevisionNumber )
{
// clients should just ignore revisions that are older than their current revision
// (packets can arrive out of order). Note that the server will send cInitialMapStateRevisionNumber
// as the "From" index when the revision counter rolls over.
return ;
}
else
{
fromMapState = & g_mapStateHistory [ packetFromRevisionNumber % NET_REVISIONS ] ;
}
netmapstate_t * toMapState = & g_mapStateHistory [ packetToRevisionNumber % NET_REVISIONS ] ;
netmapstate_t * clMapState = & g_cl_InterpolatedMapStateHistory [ packetToRevisionNumber % NET_REVISIONS ] ;
toMapState - > revisionNumber = packetToRevisionNumber ;
NET_DEBUG_VAR uint32_t DEBUG_OldClientRevision = g_netMapRevisionNumber ;
Bassert ( fromMapState ) ;
NetBuffer_ReadWorldSnapshotFromBuffer ( bufferPtr , fromMapState , toMapState ) ;
g_netMapRevisionNumber = packetToRevisionNumber ;
Net_CopySnapshotToGameArrays ( toMapState , clMapState ) ;
}
// handles revision rollover
static uint32_t Net_GetNextRevisionNumber ( uint32_t currentNumber )
{
if ( currentNumber = = UINT32_MAX )
{
// we should be able to recover from this
OSD_Printf ( " Net_SendMapUpdates(): [Note] Map state revision number overflow. " ) ;
return cStartingRevisionIndex ;
}
return ( currentNumber + 1 ) ;
}
// ---------------------------------------------------------------------------------------------------------------
// Externally accessible functions
// ---------------------------------------------------------------------------------------------------------------
void Net_WaitForInitialSnapshot ( )
{
while ( g_netMapRevisionNumber < cStartingRevisionIndex )
{
2019-10-19 23:41:40 +00:00
gameHandleEvents ( ) ;
2018-12-08 00:40:39 +00:00
}
}
// this is mostly just here to put a breakpoint on, use conditional breakpoints to stop only on particular types of packets
int32_t Dbg_PacketSent ( enum DukePacket_t iPacketType )
{
return iPacketType + 1 ;
}
// If failed is true, that means the vote lost. Otherwise it was cancelled by the client who initiated it.
void Net_SendMapVoteCancel ( int32_t failed )
{
// Only the server or the client that initiated the vote can cancel the vote
if ( g_netClient & & voting ! = myconnectindex )
{
return ;
}
tempbuf [ 0 ] = PACKET_MAP_VOTE_CANCEL ;
tempbuf [ 1 ] = myconnectindex ;
// If we're forwarding a cancelled message, change the connection index to the one who cancelled it.
if ( g_netServer & & ! failed )
{
tempbuf [ 1 ] = voting ;
}
voting = - 1 ;
Dbg_PacketSent ( PACKET_MAP_VOTE_CANCEL ) ;
if ( g_netClient )
{
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & tempbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
else if ( g_netServer )
{
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & tempbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
}
void Net_SendMapVote ( int32_t votefor )
{
voting = - 1 ;
g_player [ myconnectindex ] . gotvote = 1 ;
g_player [ myconnectindex ] . vote = votefor ;
tempbuf [ 0 ] = PACKET_MAP_VOTE ;
tempbuf [ 1 ] = myconnectindex ;
tempbuf [ 2 ] = votefor ;
tempbuf [ 3 ] = myconnectindex ;
Dbg_PacketSent ( PACKET_MAP_VOTE ) ;
if ( g_netClient )
{
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & tempbuf [ 0 ] , 4 , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
else if ( g_netServer )
{
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & tempbuf [ 0 ] , 4 , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
Net_CheckForEnoughVotes ( ) ;
}
void Net_SendMapVoteInitiate ( void )
{
newgame_t newgame ;
if ( ! g_netClient )
{
return ;
}
voting = myconnectindex ;
newgame . header = PACKET_MAP_VOTE_INITIATE ;
newgame . connection = myconnectindex ;
Net_FillNewGame ( & newgame , 1 ) ;
Dbg_PacketSent ( PACKET_MAP_VOTE_INITIATE ) ;
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & newgame , sizeof ( newgame_t ) , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
void Net_SendNewGame ( int32_t frommenu , ENetPeer * peer )
{
newgame_t newgame ;
newgame . header = PACKET_NEW_GAME ;
Net_FillNewGame ( & newgame , frommenu ) ;
Dbg_PacketSent ( PACKET_NEW_GAME ) ;
if ( peer )
{
enet_peer_send ( peer , CHAN_GAMESTATE , enet_packet_create ( & newgame , sizeof ( newgame_t ) , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
else
{
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & newgame , sizeof ( newgame_t ) , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
Net_ResetPlayerReady ( ) ;
}
void Net_FillNewGame ( newgame_t * newgame , int32_t frommenu )
{
if ( frommenu )
{
2019-11-09 18:15:03 +00:00
newgame - > level_number = m_level_number ;
2018-12-08 00:40:39 +00:00
newgame - > volume_number = ud . m_volume_number ;
newgame - > player_skill = ud . m_player_skill ;
newgame - > monsters_off = ud . m_monsters_off ;
newgame - > respawn_monsters = ud . m_respawn_monsters ;
newgame - > respawn_items = ud . m_respawn_items ;
newgame - > respawn_inventory = ud . m_respawn_inventory ;
2019-11-09 18:15:03 +00:00
newgame - > ffire = m_ffire ;
newgame - > noexits = m_noexits ;
newgame - > coop = m_coop ;
2018-12-08 00:40:39 +00:00
}
else
{
newgame - > level_number = ud . level_number ;
newgame - > volume_number = ud . volume_number ;
newgame - > player_skill = ud . player_skill ;
newgame - > monsters_off = ud . monsters_off ;
newgame - > respawn_monsters = ud . respawn_monsters ;
newgame - > respawn_items = ud . respawn_items ;
newgame - > respawn_inventory = ud . respawn_inventory ;
newgame - > ffire = ud . ffire ;
newgame - > noexits = ud . noexits ;
newgame - > coop = ud . coop ;
}
}
// store the client's history so that we can go back and look and see if the client interpolated properly
// (the server snapshots are always older than the client's current game tic)
void Net_StoreClientState ( void )
{
if ( ! g_netClient )
{
return ;
}
// don't store client revisions if we don't even have a server revision
if ( g_netMapRevisionNumber < cStartingRevisionIndex )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
return ;
}
2014-05-31 12:26:41 +00:00
2018-12-08 00:40:39 +00:00
g_cl_InterpolatedRevision = Net_GetNextRevisionNumber ( g_cl_InterpolatedRevision ) ;
2014-05-31 12:26:41 +00:00
2018-12-08 00:40:39 +00:00
netmapstate_t * currentMapState = & g_cl_InterpolatedMapStateHistory [ g_cl_InterpolatedRevision % NET_REVISIONS ] ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
Net_InitMapState ( currentMapState ) ;
Net_AddWorldToSnapshot ( currentMapState ) ;
currentMapState - > revisionNumber = g_cl_InterpolatedRevision ;
}
void Net_SendMapUpdate ( void )
{
if ( g_netClient | | ! g_netServer | | numplayers < 2 )
{
return ;
}
g_netMapRevisionNumber = Net_GetNextRevisionNumber ( g_netMapRevisionNumber ) ;
netmapstate_t * toMapState = & g_mapStateHistory [ g_netMapRevisionNumber % NET_REVISIONS ] ;
Net_InitMapState ( toMapState ) ;
Net_AddWorldToSnapshot ( toMapState ) ;
toMapState - > revisionNumber = g_netMapRevisionNumber ;
int32_t playerIndex = 0 ;
for ( TRAVERSE_CONNECT ( playerIndex ) )
{
if ( playerIndex = = myconnectindex )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
// there's no point in the server sending itself a snapshot.
continue ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
uint32_t playerRevisionNumber = g_player [ playerIndex ] . revision ;
Net_SendWorldUpdate ( playerRevisionNumber , g_netMapRevisionNumber , playerIndex ) ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
void DumpMapStateHistory ( )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
const char * fileName = NULL ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
if ( g_netClient )
{
fileName = " CL_MapStates.bin " ;
}
else
{
fileName = " SRV_MapStates.bin " ;
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
FILE * mapStatesFile = fopen ( fileName , " wb " ) ;
// write the null map state (it should never, ever be changed, but just for completeness sake
// fwrite(&NullMapState, sizeof(NullMapState), 1, mapStatesFile);
fwrite ( & g_mapStartState , sizeof ( g_mapStartState ) , 1 , mapStatesFile ) ;
fwrite ( & g_mapStateHistory [ 0 ] , sizeof ( g_mapStateHistory ) , 1 , mapStatesFile ) ;
OSD_Printf ( " Dumped map states to %s. \n " , fileName ) ;
fclose ( mapStatesFile ) ;
mapStatesFile = NULL ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
void Net_SpawnPlayer ( int32_t player )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t byteOffset = 0 ;
packbuf [ byteOffset + + ] = PACKET_PLAYER_SPAWN ;
packbuf [ byteOffset + + ] = player ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Bmemcpy ( & packbuf [ byteOffset ] , & g_player [ player ] . ps - > pos . x , sizeof ( vec3_t ) * 2 ) ;
byteOffset + = sizeof ( vec3_t ) * 2 ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
packbuf [ byteOffset + + ] = 0 ;
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , byteOffset , ENET_PACKET_FLAG_RELIABLE ) ) ;
2011-04-07 01:16:29 +00:00
2018-12-08 00:40:39 +00:00
Dbg_PacketSent ( PACKET_PLAYER_SPAWN ) ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
void Net_WaitForServer ( void )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t serverReady = g_player [ 0 ] . pingcnt ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
if ( numplayers < 2 | | g_netServer )
2012-12-12 02:53:13 +00:00
return ;
2018-12-08 00:40:39 +00:00
P_SetGamePalette ( g_player [ myconnectindex ] . ps , TITLEPAL , 8 + 2 + 1 ) ;
do
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
if ( G_FPSLimit ( ) )
2012-12-12 02:53:13 +00:00
{
2018-12-08 00:40:39 +00:00
display_betascreen ( ) ;
gametext_center_shade ( 170 , " Waiting for server " , 14 ) ;
2019-06-25 18:34:56 +00:00
videoNextPage ( ) ;
2012-12-12 02:53:13 +00:00
}
2018-12-08 00:40:39 +00:00
// XXX: this looks like something that should be rate limited...
packbuf [ 0 ] = PACKET_PLAYER_PING ;
packbuf [ 1 ] = myconnectindex ;
if ( g_netClientPeer )
{
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , 2 , ENET_PACKET_FLAG_RELIABLE ) ) ;
Dbg_PacketSent ( PACKET_PLAYER_PING ) ;
}
2019-10-19 23:41:40 +00:00
gameHandleEvents ( ) ;
2012-12-12 02:53:13 +00:00
2018-12-08 00:40:39 +00:00
if ( g_player [ 0 ] . pingcnt > serverReady )
{
P_SetGamePalette ( g_player [ myconnectindex ] . ps , BASEPAL , 8 + 2 + 1 ) ;
return ;
}
} while ( 1 ) ;
2012-12-12 02:53:13 +00:00
}
2018-12-08 00:40:39 +00:00
void Net_ResetPrediction ( void )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
NET_75_CHECK + + ; // this function is probably not needed
}
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
void Net_Connect ( const char * srvaddr )
{
ENetAddress address ;
ENetEvent event ;
char * addrstr = NULL ;
2010-08-02 08:13:51 +00:00
2018-12-08 00:40:39 +00:00
const int32_t cNumberOfRetries = 4 ;
int32_t connectCount = cNumberOfRetries ;
char * oursrvaddr = Xstrdup ( srvaddr ) ;
Net_Disconnect ( ) ;
g_netClient = enet_host_create ( NULL , 1 , CHAN_MAX , 0 , 0 ) ;
if ( g_netClient = = NULL )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
initprintf ( " An error occurred while trying to create an ENet client host. \n " ) ;
return ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
addrstr = strtok ( oursrvaddr , " : " ) ;
enet_address_set_host ( & address , addrstr ) ;
addrstr = strtok ( NULL , " : " ) ;
address . port = addrstr = = NULL ? g_netPort : Batoi ( addrstr ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
g_netClientPeer = enet_host_connect ( g_netClient , & address , CHAN_MAX , 0 ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( g_netClientPeer = = NULL )
{
initprintf ( " No available peers for initiating an ENet connection. \n " ) ;
return ;
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
for ( connectCount = cNumberOfRetries ; connectCount > 0 ; connectCount - - )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
/* Wait up to 5 seconds for the connection attempt to succeed. */
if ( enet_host_service ( g_netClient , & event , 5000 ) > 0 & & event . type = = ENET_EVENT_TYPE_CONNECT )
{
initprintf ( " Connection to %s:%d succeeded. \n " , oursrvaddr , address . port ) ;
2019-06-25 11:29:08 +00:00
Xfree ( oursrvaddr ) ;
2018-12-08 00:40:39 +00:00
return ;
}
else
{
/* Either the 5 seconds are up or a disconnect event was */
/* received. Reset the peer in the event the 5 seconds */
/* had run out without any significant event. */
enet_peer_reset ( g_netClientPeer ) ;
initprintf ( " Connection to %s:%d failed. \n " , oursrvaddr , address . port ) ;
}
initprintf ( connectCount ? " Retrying... \n " : " Giving up connection attempt. \n " ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
// [75] note: it only gets here if there was an error
2019-06-25 11:29:08 +00:00
Xfree ( oursrvaddr ) ;
2018-12-08 00:40:39 +00:00
Net_Disconnect ( ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
///< summary>
/// Record the current state of the game arrays to the initial snapshot
///</summary>
2018-12-15 01:40:08 +00:00
void Net_AddWorldToInitialSnapshot ( )
{
Net_AddWorldToSnapshot ( & g_mapStartState ) ;
2018-12-08 00:40:39 +00:00
}
void Net_SendClientInfo ( void )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t i , l ;
2012-10-28 22:27:53 +00:00
2019-10-27 13:09:56 +00:00
strncpy ( g_player [ myconnectindex ] . user_name , playername , 32 ) ;
2018-12-08 00:40:39 +00:00
if ( numplayers < 2 )
return ;
tempnetbuf [ 0 ] = PACKET_CLIENT_INFO ;
l = 1 ;
// null terminated player name to send
2019-10-27 13:09:56 +00:00
strncpy ( tempbuf + l , playername , 32 ) ;
l + = 32 ;
2018-12-08 00:40:39 +00:00
tempnetbuf [ l + + ] = 0 ;
2012-10-28 22:27:53 +00:00
2019-12-05 00:08:35 +00:00
tempnetbuf [ l + + ] = g_player [ myconnectindex ] . ps - > aim_mode = in_mousemode ;
2019-10-21 22:05:21 +00:00
tempnetbuf [ l + + ] = g_player [ myconnectindex ] . ps - > auto_aim = cl_autoaim ;
2019-10-22 00:31:14 +00:00
tempnetbuf [ l + + ] = g_player [ myconnectindex ] . ps - > weaponswitch = cl_weaponswitch ;
2019-12-04 00:38:51 +00:00
tempnetbuf [ l + + ] = g_player [ myconnectindex ] . ps - > palookup = g_player [ myconnectindex ] . pcolor = G_CheckPlayerColor ( playercolor ) ;
2012-10-28 22:27:53 +00:00
2019-11-09 18:15:03 +00:00
tempnetbuf [ l + + ] = g_player [ myconnectindex ] . pteam = playerteam ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
for ( i = 0 ; i < 10 ; i + + )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
g_player [ myconnectindex ] . wchoice [ i ] = g_player [ 0 ] . wchoice [ i ] ;
tempnetbuf [ l + + ] = ( uint8_t ) g_player [ 0 ] . wchoice [ i ] ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
tempnetbuf [ l + + ] = myconnectindex ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Dbg_PacketSent ( PACKET_CLIENT_INFO ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( g_netClient )
{
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & tempnetbuf [ 0 ] , l , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
else if ( g_netServer )
{
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & tempnetbuf [ 0 ] , l , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
void Net_SendUserMapName ( void )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
if ( numplayers < 2 )
return ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
packbuf [ 0 ] = PACKET_USER_MAP ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Bcorrectfilename ( boardfilename , 0 ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// user map name is sent with a NUL at the end
int32_t j = Bstrlen ( boardfilename ) + 1 ;
Bmemcpy ( & packbuf [ 1 ] , boardfilename , j ) ;
j + + ;
packbuf [ j + + ] = myconnectindex ;
Dbg_PacketSent ( PACKET_USER_MAP ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( g_netClient )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
enet_peer_send ( g_netClientPeer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , j , ENET_PACKET_FLAG_RELIABLE ) ) ;
}
else if ( g_netServer )
{
enet_host_broadcast ( g_netServer , CHAN_GAMESTATE , enet_packet_create ( & packbuf [ 0 ] , j , ENET_PACKET_FLAG_RELIABLE ) ) ;
2012-10-28 22:27:53 +00:00
}
}
2018-12-08 00:40:39 +00:00
// Insert a sprite from STAT_NETALLOC, and add it to the scratch pad list if we're a client.
int32_t Net_InsertSprite ( int32_t sect , int32_t stat )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
if ( ( ! g_netServer ) & & ( ! g_netClient ) )
{
return insertsprite ( sect , stat ) ;
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
int32_t i = Net_DoInsertSprite ( sect , stat ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
if ( i < 0 )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
return i ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
if ( g_netClient )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Net_InsertScratchPadSprite ( i ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
return i ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
void Net_DeleteSprite ( int32_t spritenum )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
// [75] later on, real clientside sprites (not scratchpad or server side) can be just deleted
// using deletesprite without worrying about linked lists or sprite indexes going out of sync.
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
// [75] for most cases, only the server deletes sprites, clients just set their sprites invisible and to
// the null picnum so no CON gets executed
2018-12-15 01:40:20 +00:00
if ( ( ! g_netServer ) & & ( ! g_netClient ) )
{
deletesprite ( spritenum ) ;
return ;
}
2018-12-08 00:40:39 +00:00
if ( g_netClient )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
sprite [ spritenum ] . cstat = 32768 ;
sprite [ spritenum ] . picnum = 0 ;
2012-10-28 22:27:53 +00:00
return ;
}
2018-12-08 00:40:39 +00:00
Net_DoDeleteSprite ( spritenum ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
// Send various player related updates (server -> client)
void Net_SendServerUpdates ( void )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int16_t i ;
uint8_t * updatebuf ;
serverupdate_t serverupdate ;
serverplayerupdate_t playerupdate ;
input_t * osyn = ( input_t * ) & inputfifo [ 1 ] [ 0 ] ;
input_t * nsyn = ( input_t * ) & inputfifo [ 0 ] [ 0 ] ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
ticrandomseed = randomseed ;
if ( g_netServer )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Bmemcpy ( & osyn [ 0 ] , & nsyn [ 0 ] , sizeof ( input_t ) ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
if ( ! g_netServer | | numplayers < 2 )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
return ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
serverupdate . header = PACKET_MASTER_TO_SLAVE ;
serverupdate . seed = ticrandomseed ;
serverupdate . nsyn = * nsyn ;
serverupdate . pause_on = ud . pause_on ;
serverupdate . numplayers = 0 ;
updatebuf = tempnetbuf + sizeof ( serverupdate_t ) ;
for ( TRAVERSE_CONNECT ( i ) )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
if ( g_player [ i ] . playerquitflag = = 0 )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
continue ;
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
Net_FillPlayerUpdate ( & playerupdate . player , i ) ;
playerupdate . gotweapon = g_player [ i ] . ps - > gotweapon ;
playerupdate . kickback_pic = g_player [ i ] . ps - > kickback_pic ;
Bmemcpy ( playerupdate . frags , g_player [ i ] . frags , sizeof ( playerupdate . frags ) ) ;
Bmemcpy ( playerupdate . inv_amount , g_player [ i ] . ps - > inv_amount , sizeof ( playerupdate . inv_amount ) ) ;
Bmemcpy ( playerupdate . ammo_amount , g_player [ i ] . ps - > ammo_amount , sizeof ( playerupdate . ammo_amount ) ) ;
playerupdate . curr_weapon = g_player [ i ] . ps - > curr_weapon ;
playerupdate . last_weapon = g_player [ i ] . ps - > last_weapon ;
playerupdate . wantweaponfire = g_player [ i ] . ps - > wantweaponfire ;
playerupdate . weapon_pos = g_player [ i ] . ps - > weapon_pos ;
playerupdate . frag_ps = g_player [ i ] . ps - > frag_ps ;
playerupdate . frag = g_player [ i ] . ps - > frag ;
playerupdate . fraggedself = g_player [ i ] . ps - > fraggedself ;
playerupdate . last_extra = g_player [ i ] . ps - > last_extra ;
playerupdate . ping = g_player [ i ] . ping ;
playerupdate . newowner = g_player [ i ] . ps - > newowner ;
Bmemcpy ( updatebuf , & playerupdate , sizeof ( serverplayerupdate_t ) ) ;
updatebuf + = sizeof ( serverplayerupdate_t ) ;
serverupdate . numplayers + + ;
}
if ( serverupdate . numplayers = = 0 )
{
return ;
2010-08-02 08:13:51 +00:00
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Bmemcpy ( tempnetbuf , & serverupdate , sizeof ( serverupdate_t ) ) ;
enet_host_broadcast (
g_netServer , CHAN_MOVE ,
enet_packet_create ( & tempnetbuf [ 0 ] , sizeof ( serverupdate_t ) + ( serverupdate . numplayers * sizeof ( serverplayerupdate_t ) ) , 0 ) ) ;
Dbg_PacketSent ( PACKET_MASTER_TO_SLAVE ) ;
}
void Net_SendClientUpdate ( void )
{
clientupdate_t update ;
update . header = PACKET_SLAVE_TO_MASTER ;
update . RevisionNumber = g_netMapRevisionNumber ;
update . nsyn = inputfifo [ 0 ] [ myconnectindex ] ;
Net_FillPlayerUpdate ( & update . player , myconnectindex ) ;
enet_peer_send ( g_netClientPeer , CHAN_MOVE , enet_packet_create ( & update , sizeof ( clientupdate_t ) , 0 ) ) ;
Dbg_PacketSent ( PACKET_SLAVE_TO_MASTER ) ;
}
void Net_SendMessage ( void )
{
if ( g_player [ myconnectindex ] . ps - > gm & MODE_SENDTOWHOM )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t i , j ;
if ( g_chatPlayer ! = - 1 | | ud . multimode < 3 )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
tempbuf [ 0 ] = PACKET_MESSAGE ;
tempbuf [ 2 ] = 0 ;
recbuf [ 0 ] = 0 ;
if ( ud . multimode < 3 )
g_chatPlayer = 2 ;
if ( typebuf [ 0 ] = = ' / ' & & Btoupper ( typebuf [ 1 ] ) = = ' M ' & & Btoupper ( typebuf [ 2 ] ) = = ' E ' )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Bstrcat ( recbuf , " * " ) ;
i = 3 , j = Bstrlen ( typebuf ) ;
Bstrcpy ( tempbuf , typebuf ) ;
while ( i < j )
{
typebuf [ i - 3 ] = tempbuf [ i ] ;
i + + ;
}
typebuf [ i - 3 ] = ' \0 ' ;
Bstrcat ( recbuf , g_player [ myconnectindex ] . user_name ) ;
2012-10-28 22:27:53 +00:00
}
else
{
2018-12-08 00:40:39 +00:00
Bstrcat ( recbuf , g_player [ myconnectindex ] . user_name ) ;
Bstrcat ( recbuf , " : " ) ;
}
Bstrcat ( recbuf , " ^00 " ) ;
Bstrcat ( recbuf , typebuf ) ;
j = Bstrlen ( recbuf ) ;
recbuf [ j ] = 0 ;
Bstrcat ( tempbuf + 2 , recbuf ) ;
if ( g_chatPlayer > = ud . multimode )
{
tempbuf [ 1 ] = 255 ;
tempbuf [ j + 2 ] = myconnectindex ;
j + + ;
if ( g_netServer )
enet_host_broadcast ( g_netServer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , j + 2 , 0 ) ) ;
else if ( g_netClient )
enet_peer_send ( g_netClientPeer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , j + 2 , 0 ) ) ;
G_AddUserQuote ( recbuf ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
g_chatPlayer = - 1 ;
g_player [ myconnectindex ] . ps - > gm & = ~ ( MODE_TYPE | MODE_SENDTOWHOM ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
else if ( g_chatPlayer = = - 1 )
{
j = 50 ;
gametext_center ( j , " Send message to... " ) ;
j + = 8 ;
for ( TRAVERSE_CONNECT ( i ) )
{
if ( i = = myconnectindex )
{
minitextshade ( ( 320 > > 1 ) - 40 + 1 , j + 1 , " A/ENTER - ALL " , 26 , 0 , 2 + 8 + 16 ) ;
minitext ( ( 320 > > 1 ) - 40 , j , " A/ENTER - ALL " , 0 , 2 + 8 + 16 ) ;
j + = 7 ;
}
else
{
Bsprintf ( recbuf , " %d - %s " , i + 1 , g_player [ i ] . user_name ) ;
minitextshade ( ( 320 > > 1 ) - 40 - 6 + 1 , j + 1 , recbuf , 26 , 0 , 2 + 8 + 16 ) ;
minitext ( ( 320 > > 1 ) - 40 - 6 , j , recbuf , 0 , 2 + 8 + 16 ) ;
j + = 7 ;
}
}
minitextshade ( ( 320 > > 1 ) - 40 - 4 + 1 , j + 1 , " ESC - Abort " , 26 , 0 , 2 + 8 + 16 ) ;
minitext ( ( 320 > > 1 ) - 40 - 4 , j , " ESC - Abort " , 0 , 2 + 8 + 16 ) ;
j + = 7 ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
mpgametext ( mpgametext_x , ud . screen_size > 0 ? ( 200 - 45 ) < < 16 : ( 200 - 8 ) < < 16 , typebuf , 0 , 0 , 0 , 0 ) ;
2019-11-03 23:53:55 +00:00
if ( inputState . keyBufferWaiting ( ) )
2018-12-08 00:40:39 +00:00
{
2019-11-03 23:53:55 +00:00
i = inputState . keyGetChar ( ) ;
2018-12-08 00:40:39 +00:00
if ( i = = ' A ' | | i = = ' a ' | | i = = 13 )
g_chatPlayer = ud . multimode ;
else if ( i > = ' 1 ' | | i < = ( ud . multimode + ' 1 ' ) )
g_chatPlayer = i - ' 1 ' ;
else
{
g_chatPlayer = ud . multimode ;
if ( i = = 27 )
{
g_player [ myconnectindex ] . ps - > gm & = ~ ( MODE_TYPE | MODE_SENDTOWHOM ) ;
g_chatPlayer = - 1 ;
}
else
typebuf [ 0 ] = 0 ;
}
2019-11-03 23:53:55 +00:00
inputState . ClearKeyStatus ( sc_1 ) ;
inputState . ClearKeyStatus ( sc_2 ) ;
inputState . ClearKeyStatus ( sc_3 ) ;
inputState . ClearKeyStatus ( sc_4 ) ;
inputState . ClearKeyStatus ( sc_5 ) ;
inputState . ClearKeyStatus ( sc_6 ) ;
inputState . ClearKeyStatus ( sc_7 ) ;
inputState . ClearKeyStatus ( sc_8 ) ;
inputState . ClearKeyStatus ( sc_A ) ;
inputState . ClearKeyStatus ( sc_Escape ) ;
inputState . ClearKeyStatus ( sc_Enter ) ;
2018-12-08 00:40:39 +00:00
}
}
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
else
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t const hitstate = I_EnterText ( typebuf , 120 , 0 ) ;
int32_t const y = ud . screen_size > 1 ? ( 200 - 58 ) < < 16 : ( 200 - 35 ) < < 16 ;
int32_t const width = mpgametextsize ( typebuf , TEXT_LITERALESCAPE ) . x ;
int32_t const fullwidth = width + textsc ( ( tilesiz [ SPINNINGNUKEICON ] . x < < 15 ) + ( 2 < < 16 ) ) ;
int32_t const text_x = fullwidth > = ( 320 < < 16 ) ? ( 320 < < 16 ) - fullwidth : mpgametext_x ;
mpgametext ( text_x , y , typebuf , 1 , 2 | 8 | 16 | ROTATESPRITE_FULL16 , 0 , TEXT_YCENTER | TEXT_LITERALESCAPE ) ;
int32_t const cursor_x = text_x + width + textsc ( ( tilesiz [ SPINNINGNUKEICON ] . x < < 14 ) + ( 1 < < 16 ) ) ;
2019-08-27 13:39:54 +00:00
rotatesprite_fs ( cursor_x , y , textsc ( 32768 ) , 0 , SPINNINGNUKEICON + ( ( ( int32_t ) totalclock > > 3 ) % 7 ) ,
4 - ( sintable [ ( ( int32_t ) totalclock < < 4 ) & 2047 ] > > 11 ) , 0 , 2 | 8 ) ;
2018-12-08 00:40:39 +00:00
if ( hitstate = = 1 )
{
2019-11-03 23:53:55 +00:00
inputState . ClearKeyStatus ( sc_Enter ) ;
2018-12-08 00:40:39 +00:00
if ( Bstrlen ( typebuf ) = = 0 )
{
g_player [ myconnectindex ] . ps - > gm & = ~ ( MODE_TYPE | MODE_SENDTOWHOM ) ;
return ;
}
2019-10-21 22:14:39 +00:00
if ( cl_automsg )
2018-12-08 00:40:39 +00:00
{
if ( SHIFTS_IS_PRESSED )
g_chatPlayer = - 1 ;
else
g_chatPlayer = ud . multimode ;
}
g_player [ myconnectindex ] . ps - > gm | = MODE_SENDTOWHOM ;
}
else if ( hitstate = = - 1 )
g_player [ myconnectindex ] . ps - > gm & = ~ ( MODE_TYPE | MODE_SENDTOWHOM ) ;
else
pub = NUMPAGES ;
2012-10-28 22:27:53 +00:00
}
2010-08-02 08:13:51 +00:00
}
2018-12-08 00:40:39 +00:00
void Net_InitMapStateHistory ( )
2010-08-02 08:13:51 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t mapStateIndex = 0 ;
for ( mapStateIndex = 0 ; mapStateIndex < NET_REVISIONS ; mapStateIndex + + )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
netmapstate_t * mapState = & g_mapStateHistory [ mapStateIndex ] ;
netmapstate_t * clState = & g_cl_InterpolatedMapStateHistory [ mapStateIndex ] ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Net_InitMapState ( mapState ) ;
Net_InitMapState ( clState ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
Net_InitMapState ( & g_mapStartState ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
g_mapStartState . revisionNumber = cInitialMapStateRevisionNumber ;
g_netMapRevisionNumber = cInitialMapStateRevisionNumber ; // Net_InitMapStateHistory()
g_cl_InterpolatedRevision = cInitialMapStateRevisionNumber ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
void Net_StartNewGame ( )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
Net_ResetPlayers ( ) ;
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
Net_ExtractNewGame ( & pendingnewgame , 0 ) ;
G_NewGame ( ud . volume_number , ud . level_number , ud . player_skill ) ;
2019-11-09 18:15:03 +00:00
ud . coop = m_coop ;
2018-12-08 00:40:39 +00:00
if ( G_EnterLevel ( MODE_GAME ) )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
G_BackToMenu ( ) ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
}
2012-10-28 22:27:53 +00:00
2018-12-08 00:40:39 +00:00
void Net_NotifyNewGame ( )
{
int32_t spriteIndex ;
int32_t statIndex ;
int32_t numSprites = 0 ;
int32_t numSpritesToNetAlloc = 0 ;
if ( ! g_netServer & & ! g_netClient )
2012-10-28 22:27:53 +00:00
{
return ;
}
2018-12-08 00:40:39 +00:00
// Grab the total number of sprites at level load
for ( statIndex = 0 ; statIndex < MAXSTATUS ; + + statIndex )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
spriteIndex = headspritestat [ statIndex ] ;
for ( ; spriteIndex > = 0 ; spriteIndex = nextspritestat [ spriteIndex ] )
{
numSprites + + ;
}
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
// Take half of the leftover sprites and allocate them for the network's nefarious purposes.
numSpritesToNetAlloc = ( MAXSPRITES - numSprites ) / 2 ;
for ( spriteIndex = 0 ; spriteIndex < numSpritesToNetAlloc ; + + spriteIndex )
2012-10-28 22:27:53 +00:00
{
2018-12-08 00:40:39 +00:00
int32_t newSprite = insertspritestat ( STAT_NETALLOC ) ;
sprite [ newSprite ] . sectnum = MAXSECTORS ;
Numsprites + + ;
2012-10-28 22:27:53 +00:00
}
2018-12-08 00:40:39 +00:00
//[75] Note: DON'T set the initial map state or initialize the map state history in the packet code,
// The client didn't load the map until G_EnterLevel
2010-08-02 08:13:51 +00:00
}
2019-11-01 06:26:49 +00:00
void Net_SendTaunt ( int ridiculeNum )
{
tempbuf [ 0 ] = PACKET_MESSAGE ;
tempbuf [ 1 ] = 255 ;
tempbuf [ 2 ] = 0 ;
Bstrcat ( tempbuf + 2 , * CombatMacros [ ridiculeNum - 1 ] ) ;
ridiculeNum = 2 + strlen ( * CombatMacros [ ridiculeNum - 1 ] ) ;
tempbuf [ ridiculeNum + + ] = myconnectindex ;
if ( g_netClient )
enet_peer_send ( g_netClientPeer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , ridiculeNum , 0 ) ) ;
else if ( g_netServer )
enet_host_broadcast ( g_netServer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , ridiculeNum , 0 ) ) ;
}
void Net_SendRTS ( int ridiculeNum )
{
if ( ( g_netServer | | ud . multimode > 1 ) )
{
tempbuf [ 0 ] = PACKET_RTS ;
tempbuf [ 1 ] = ridiculeNum ;
tempbuf [ 2 ] = myconnectindex ;
if ( g_netClient )
enet_peer_send ( g_netClientPeer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , 3 , 0 ) ) ;
else if ( g_netServer )
enet_host_broadcast ( g_netServer , CHAN_CHAT , enet_packet_create ( & tempbuf [ 0 ] , 3 , 0 ) ) ;
}
}
2018-12-08 00:40:39 +00:00
# endif
//-------------------------------------------------------------------------------------------------
2019-09-21 20:53:00 +00:00
END_DUKE_NS