2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
2019-12-06 18:49:42 +00:00
// Copyright (C) 1999-2019 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file d_clisrv.c
/// \brief SRB2 Network game communication and protocol, all OS independent parts.
# include <time.h>
# ifdef __GNUC__
# include <unistd.h> //for unlink
# endif
# include "i_net.h"
# include "i_system.h"
# include "i_video.h"
# include "d_net.h"
# include "d_main.h"
# include "g_game.h"
# include "hu_stuff.h"
# include "keys.h"
2018-12-03 17:51:18 +00:00
# include "g_input.h" // JOY1
2014-03-15 16:59:03 +00:00
# include "m_menu.h"
# include "console.h"
# include "d_netfil.h"
# include "byteptr.h"
# include "p_saveg.h"
# include "z_zone.h"
# include "p_local.h"
# include "m_misc.h"
# include "am_map.h"
# include "m_random.h"
# include "mserv.h"
# include "y_inter.h"
# include "r_local.h"
# include "m_argv.h"
# include "p_setup.h"
# include "lzf.h"
# include "lua_script.h"
# include "lua_hook.h"
2019-07-15 22:39:58 +00:00
# include "md5.h"
2014-03-15 16:59:03 +00:00
# ifdef CLIENT_LOADINGSCREEN
// cl loading screen
# include "v_video.h"
# include "f_finale.h"
# endif
//
// NETWORKING
//
// gametic is the tic about to (or currently being) run
2016-12-31 18:26:33 +00:00
// Server:
// maketic is the tic that hasn't had control made for it yet
2014-03-15 16:59:03 +00:00
// nettics is the tic for each node
// firstticstosend is the lowest value of nettics
2016-12-31 18:26:33 +00:00
// Client:
// neededtic is the tic needed by the client to run the game
2014-03-15 16:59:03 +00:00
// firstticstosend is used to optimize a condition
2016-12-31 18:26:33 +00:00
// Normally maketic >= gametic > 0
2014-03-15 16:59:03 +00:00
# define PREDICTIONQUEUE BACKUPTICS
# define PREDICTIONMASK (PREDICTIONQUEUE-1)
# define MAX_REASONLENGTH 30
boolean server = true ; // true or false but !server == client
2017-01-13 19:53:52 +00:00
# define client (!server)
2014-03-15 16:59:03 +00:00
boolean nodownload = false ;
2019-12-18 23:34:55 +00:00
boolean serverrunning = false ;
2014-03-15 16:59:03 +00:00
INT32 serverplayer = 0 ;
char motd [ 254 ] , server_context [ 8 ] ; // Message of the Day, Unique Context (even without Mumble support)
2017-01-13 19:53:52 +00:00
// Server specific vars
2014-03-15 16:59:03 +00:00
UINT8 playernode [ MAXPLAYERS ] ;
2017-01-13 19:53:52 +00:00
// Minimum timeout for sending the savegame
// The actual timeout will be longer depending on the savegame length
tic_t jointimeout = ( 10 * TICRATE ) ;
static boolean sendingsavegame [ MAXNETNODES ] ; // Are we sending the savegame?
static tic_t freezetimeout [ MAXNETNODES ] ; // Until when can this node freeze the server before getting a timeout?
2014-03-15 16:59:03 +00:00
UINT16 pingmeasurecount = 1 ;
UINT32 realpingtable [ MAXPLAYERS ] ; //the base table of ping where an average will be sent to everyone.
UINT32 playerpingtable [ MAXPLAYERS ] ; //table of player latency values.
SINT8 nodetoplayer [ MAXNETNODES ] ;
SINT8 nodetoplayer2 [ MAXNETNODES ] ; // say the numplayer for this node if any (splitscreen)
UINT8 playerpernode [ MAXNETNODES ] ; // used specialy for scplitscreen
boolean nodeingame [ MAXNETNODES ] ; // set false as nodes leave game
2019-11-18 01:22:47 +00:00
tic_t servermaxping = 800 ; // server's max ping. Defaults to 800
2014-03-15 16:59:03 +00:00
static tic_t nettics [ MAXNETNODES ] ; // what tic the client have received
static tic_t supposedtics [ MAXNETNODES ] ; // nettics prevision for smaller packet
static UINT8 nodewaiting [ MAXNETNODES ] ;
static tic_t firstticstosend ; // min of the nettics
static tic_t tictoclear = 0 ; // optimize d_clearticcmd
static tic_t maketic ;
2014-03-21 18:42:55 +00:00
static INT16 consistancy [ BACKUPTICS ] ;
// Resynching shit!
static UINT32 resynch_score [ MAXNETNODES ] ; // "score" for kicking -- if this gets too high then cfail kick
static UINT16 resynch_delay [ MAXNETNODES ] ; // delay time before the player can be considered to have desynched
static UINT32 resynch_status [ MAXNETNODES ] ; // 0 bit means synched for that player, 1 means possibly desynched
2017-12-12 22:08:18 +00:00
static UINT8 resynch_sent [ MAXNETNODES ] [ MAXPLAYERS ] ; // what synch packets have we attempted to send to the player
2014-03-21 18:42:55 +00:00
static UINT8 resynch_inprogress [ MAXNETNODES ] ;
static UINT8 resynch_local_inprogress = false ; // WE are desynched and getting packets to fix it.
static UINT8 player_joining = false ;
UINT8 hu_resynching = 0 ;
2019-08-17 17:33:14 +00:00
UINT8 adminpassmd5 [ 16 ] ;
2019-07-15 22:39:58 +00:00
boolean adminpasswordset = false ;
2017-01-13 19:53:52 +00:00
// Client specific
2014-03-15 16:59:03 +00:00
static ticcmd_t localcmds ;
static ticcmd_t localcmds2 ;
static boolean cl_packetmissed ;
// here it is for the secondary local player (splitscreen)
static UINT8 mynode ; // my address pointofview server
static UINT8 localtextcmd [ MAXTEXTCMD ] ;
static UINT8 localtextcmd2 [ MAXTEXTCMD ] ; // splitscreen
static tic_t neededtic ;
SINT8 servernode = 0 ; // the number of the server node
/// \brief do we accept new players?
/// \todo WORK!
boolean acceptnewnode = true ;
// engine
// Must be a power of two
# define TEXTCMD_HASH_SIZE 4
typedef struct textcmdplayer_s
{
INT32 playernum ;
UINT8 cmd [ MAXTEXTCMD ] ;
struct textcmdplayer_s * next ;
} textcmdplayer_t ;
typedef struct textcmdtic_s
{
tic_t tic ;
textcmdplayer_t * playercmds [ TEXTCMD_HASH_SIZE ] ;
struct textcmdtic_s * next ;
} textcmdtic_t ;
ticcmd_t netcmds [ BACKUPTICS ] [ MAXPLAYERS ] ;
static textcmdtic_t * textcmds [ TEXTCMD_HASH_SIZE ] = { NULL } ;
2019-08-27 00:38:32 +00:00
consvar_t cv_showjoinaddress = { " showjoinaddress " , " On " , CV_SAVE , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
2014-03-15 16:59:03 +00:00
static CV_PossibleValue_t playbackspeed_cons_t [ ] = { { 1 , " MIN " } , { 10 , " MAX " } , { 0 , NULL } } ;
consvar_t cv_playbackspeed = { " playbackspeed " , " 1 " , 0 , playbackspeed_cons_t , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
static inline void * G_DcpyTiccmd ( void * dest , const ticcmd_t * src , const size_t n )
{
const size_t d = n / sizeof ( ticcmd_t ) ;
const size_t r = n % sizeof ( ticcmd_t ) ;
UINT8 * ret = dest ;
if ( r )
M_Memcpy ( dest , src , n ) ;
else if ( d )
G_MoveTiccmd ( dest , src , d ) ;
return ret + n ;
}
static inline void * G_ScpyTiccmd ( ticcmd_t * dest , void * src , const size_t n )
{
const size_t d = n / sizeof ( ticcmd_t ) ;
const size_t r = n % sizeof ( ticcmd_t ) ;
UINT8 * ret = src ;
if ( r )
M_Memcpy ( dest , src , n ) ;
else if ( d )
G_MoveTiccmd ( dest , src , d ) ;
return ret + n ;
}
2016-12-31 18:26:33 +00:00
// Some software don't support largest packet
// (original sersetup, not exactely, but the probability of sending a packet
// of 512 bytes is like 0.1)
2014-03-15 16:59:03 +00:00
UINT16 software_MAXPACKETLENGTH ;
2016-12-31 18:26:33 +00:00
/** Guesses the value of a tic from its lowest byte and from maketic
*
* \ param low The lowest byte of the tic value
* \ return The full tic value
*
*/
2014-03-15 16:59:03 +00:00
tic_t ExpandTics ( INT32 low )
{
INT32 delta ;
delta = low - ( maketic & UINT8_MAX ) ;
if ( delta > = - 64 & & delta < = 64 )
return ( maketic & ~ UINT8_MAX ) + low ;
else if ( delta > 64 )
return ( maketic & ~ UINT8_MAX ) - 256 + low ;
else //if (delta < -64)
return ( maketic & ~ UINT8_MAX ) + 256 + low ;
}
// -----------------------------------------------------------------
// Some extra data function for handle textcmd buffer
// -----------------------------------------------------------------
static void ( * listnetxcmd [ MAXNETXCMD ] ) ( UINT8 * * p , INT32 playernum ) ;
void RegisterNetXCmd ( netxcmd_t id , void ( * cmd_f ) ( UINT8 * * p , INT32 playernum ) )
{
# ifdef PARANOIA
if ( id > = MAXNETXCMD )
2016-12-31 18:26:33 +00:00
I_Error ( " Command id %d too big " , id ) ;
2014-03-15 16:59:03 +00:00
if ( listnetxcmd [ id ] ! = 0 )
I_Error ( " Command id %d already used " , id ) ;
# endif
listnetxcmd [ id ] = cmd_f ;
}
void SendNetXCmd ( netxcmd_t id , const void * param , size_t nparam )
{
if ( localtextcmd [ 0 ] + 2 + nparam > MAXTEXTCMD )
{
// for future reference: if (cv_debug) != debug disabled.
CONS_Alert ( CONS_ERROR , M_GetText ( " NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s) \n " ) , id , localtextcmd [ 0 ] , sizeu1 ( nparam ) ) ;
return ;
}
localtextcmd [ 0 ] + + ;
localtextcmd [ localtextcmd [ 0 ] ] = ( UINT8 ) id ;
if ( param & & nparam )
{
M_Memcpy ( & localtextcmd [ localtextcmd [ 0 ] + 1 ] , param , nparam ) ;
localtextcmd [ 0 ] = ( UINT8 ) ( localtextcmd [ 0 ] + ( UINT8 ) nparam ) ;
}
}
// splitscreen player
void SendNetXCmd2 ( netxcmd_t id , const void * param , size_t nparam )
{
if ( localtextcmd2 [ 0 ] + 2 + nparam > MAXTEXTCMD )
{
I_Error ( " No more place in the buffer for netcmd %d \n " , id ) ;
return ;
}
localtextcmd2 [ 0 ] + + ;
localtextcmd2 [ localtextcmd2 [ 0 ] ] = ( UINT8 ) id ;
if ( param & & nparam )
{
M_Memcpy ( & localtextcmd2 [ localtextcmd2 [ 0 ] + 1 ] , param , nparam ) ;
localtextcmd2 [ 0 ] = ( UINT8 ) ( localtextcmd2 [ 0 ] + ( UINT8 ) nparam ) ;
}
}
UINT8 GetFreeXCmdSize ( void )
{
// -1 for the size and another -1 for the ID.
return ( UINT8 ) ( localtextcmd [ 0 ] - 2 ) ;
}
// Frees all textcmd memory for the specified tic
static void D_FreeTextcmd ( tic_t tic )
{
textcmdtic_t * * tctprev = & textcmds [ tic & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
textcmdtic_t * textcmdtic = * tctprev ;
while ( textcmdtic & & textcmdtic - > tic ! = tic )
{
tctprev = & textcmdtic - > next ;
textcmdtic = textcmdtic - > next ;
}
if ( textcmdtic )
{
INT32 i ;
// Remove this tic from the list.
* tctprev = textcmdtic - > next ;
// Free all players.
for ( i = 0 ; i < TEXTCMD_HASH_SIZE ; i + + )
{
textcmdplayer_t * textcmdplayer = textcmdtic - > playercmds [ i ] ;
while ( textcmdplayer )
{
textcmdplayer_t * tcpnext = textcmdplayer - > next ;
Z_Free ( textcmdplayer ) ;
textcmdplayer = tcpnext ;
}
}
// Free this tic's own memory.
Z_Free ( textcmdtic ) ;
}
}
// Gets the buffer for the specified ticcmd, or NULL if there isn't one
static UINT8 * D_GetExistingTextcmd ( tic_t tic , INT32 playernum )
{
textcmdtic_t * textcmdtic = textcmds [ tic & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
while ( textcmdtic & & textcmdtic - > tic ! = tic ) textcmdtic = textcmdtic - > next ;
// Do we have an entry for the tic? If so, look for player.
if ( textcmdtic )
{
textcmdplayer_t * textcmdplayer = textcmdtic - > playercmds [ playernum & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
while ( textcmdplayer & & textcmdplayer - > playernum ! = playernum ) textcmdplayer = textcmdplayer - > next ;
if ( textcmdplayer ) return textcmdplayer - > cmd ;
}
return NULL ;
}
// Gets the buffer for the specified ticcmd, creating one if necessary
static UINT8 * D_GetTextcmd ( tic_t tic , INT32 playernum )
{
textcmdtic_t * textcmdtic = textcmds [ tic & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
textcmdtic_t * * tctprev = & textcmds [ tic & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
textcmdplayer_t * textcmdplayer , * * tcpprev ;
// Look for the tic.
while ( textcmdtic & & textcmdtic - > tic ! = tic )
{
tctprev = & textcmdtic - > next ;
textcmdtic = textcmdtic - > next ;
}
// If we don't have an entry for the tic, make it.
if ( ! textcmdtic )
{
textcmdtic = * tctprev = Z_Calloc ( sizeof ( textcmdtic_t ) , PU_STATIC , NULL ) ;
textcmdtic - > tic = tic ;
}
tcpprev = & textcmdtic - > playercmds [ playernum & ( TEXTCMD_HASH_SIZE - 1 ) ] ;
textcmdplayer = * tcpprev ;
// Look for the player.
while ( textcmdplayer & & textcmdplayer - > playernum ! = playernum )
{
tcpprev = & textcmdplayer - > next ;
textcmdplayer = textcmdplayer - > next ;
}
// If we don't have an entry for the player, make it.
if ( ! textcmdplayer )
{
textcmdplayer = * tcpprev = Z_Calloc ( sizeof ( textcmdplayer_t ) , PU_STATIC , NULL ) ;
textcmdplayer - > playernum = playernum ;
}
return textcmdplayer - > cmd ;
}
static void ExtraDataTicker ( void )
{
INT32 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] | | i = = 0 )
{
UINT8 * bufferstart = D_GetExistingTextcmd ( gametic , i ) ;
if ( bufferstart )
{
UINT8 * curpos = bufferstart ;
UINT8 * bufferend = & curpos [ curpos [ 0 ] + 1 ] ;
curpos + + ;
while ( curpos < bufferend )
{
if ( * curpos < MAXNETXCMD & & listnetxcmd [ * curpos ] )
{
const UINT8 id = * curpos ;
curpos + + ;
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " executing x_cmd %s ply %u " , netxcmdnames [ id - 1 ] , i ) ) ;
2014-03-15 16:59:03 +00:00
( listnetxcmd [ id ] ) ( & curpos , i ) ;
DEBFILE ( " done \n " ) ;
}
else
{
if ( server )
{
2020-01-12 18:43:04 +00:00
SendKick ( i , KICK_MSG_CON_FAIL ) ;
2014-03-15 16:59:03 +00:00
DEBFILE ( va ( " player %d kicked [gametic=%u] reason as follows: \n " , i , gametic ) ) ;
}
CONS_Alert ( CONS_WARNING , M_GetText ( " Got unknown net command [%s]=%d (max %d) \n " ) , sizeu1 ( curpos - bufferstart ) , * curpos , bufferstart [ 0 ] ) ;
2018-02-13 16:53:18 +00:00
break ;
2014-03-15 16:59:03 +00:00
}
}
}
}
2017-01-02 19:02:49 +00:00
// If you are a client, you can safely forget the net commands for this tic
// If you are the server, you need to remember them until every client has been aknowledged,
// because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it
2017-01-13 19:53:52 +00:00
if ( client )
2017-01-02 19:02:49 +00:00
D_FreeTextcmd ( gametic ) ;
2014-03-15 16:59:03 +00:00
}
static void D_Clearticcmd ( tic_t tic )
{
INT32 i ;
D_FreeTextcmd ( tic ) ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
netcmds [ tic % BACKUPTICS ] [ i ] . angleturn = 0 ;
DEBFILE ( va ( " clear tic %5u (%2u) \n " , tic , tic % BACKUPTICS ) ) ;
}
2017-01-02 19:02:49 +00:00
void D_ResetTiccmds ( void )
{
INT32 i ;
memset ( & localcmds , 0 , sizeof ( ticcmd_t ) ) ;
memset ( & localcmds2 , 0 , sizeof ( ticcmd_t ) ) ;
// Reset the net command list
for ( i = 0 ; i < TEXTCMD_HASH_SIZE ; i + + )
while ( textcmds [ i ] )
D_Clearticcmd ( textcmds [ i ] - > tic ) ;
}
2020-01-12 18:43:04 +00:00
void SendKick ( UINT8 playernum , UINT8 msg )
{
UINT8 buf [ 2 ] ;
buf [ 0 ] = playernum ;
buf [ 1 ] = msg ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
2014-03-15 16:59:03 +00:00
// -----------------------------------------------------------------
// end of extra data function
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// extra data function for lmps
// -----------------------------------------------------------------
// if extradatabit is set, after the ziped tic you find this:
//
// type | description
// ---------+--------------
// byte | size of the extradata
// byte | the extradata (xd) bits: see XD_...
// with this byte you know what parameter folow
// if (xd & XDNAMEANDCOLOR)
// byte | color
// char[MAXPLAYERNAME] | name of the player
// endif
// if (xd & XD_WEAPON_PREF)
// byte | original weapon switch: boolean, true if use the old
// | weapon switch methode
// char[NUMWEAPONS] | the weapon switch priority
// byte | autoaim: true if use the old autoaim system
// endif
/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum)
{
UINT8 * textcmd = D_GetExistingTextcmd ( gametic , playernum ) ;
if ( ! textcmd )
return false ;
M_Memcpy ( * demo_point , textcmd , textcmd [ 0 ] + 1 ) ;
* demo_point + = textcmd [ 0 ] + 1 ;
return true ;
}
void ReadLmpExtraData ( UINT8 * * demo_pointer , INT32 playernum )
{
UINT8 nextra ;
UINT8 * textcmd ;
if ( ! demo_pointer )
return ;
textcmd = D_GetTextcmd ( gametic , playernum ) ;
nextra = * * demo_pointer ;
M_Memcpy ( textcmd , * demo_pointer , nextra + 1 ) ;
// increment demo pointer
* demo_pointer + = nextra + 1 ;
} */
// -----------------------------------------------------------------
// end extra data function for lmps
// -----------------------------------------------------------------
2014-03-21 18:42:55 +00:00
// -----------------------------------------------------------------
// resynch player data
// -----------------------------------------------------------------
static inline void resynch_write_player ( resynch_pak * rsp , const size_t i )
{
size_t j ;
rsp - > playernum = ( UINT8 ) i ;
// Do not send anything visual related.
// Only send data that we need to know for physics.
rsp - > playerstate = ( UINT8 ) players [ i ] . playerstate ; //playerstate_t
rsp - > pflags = ( UINT32 ) LONG ( players [ i ] . pflags ) ; //pflags_t
rsp - > panim = ( UINT8 ) players [ i ] . panim ; //panim_t
rsp - > aiming = ( angle_t ) LONG ( players [ i ] . aiming ) ;
rsp - > currentweapon = LONG ( players [ i ] . currentweapon ) ;
rsp - > ringweapons = LONG ( players [ i ] . ringweapons ) ;
2019-08-26 23:07:17 +00:00
rsp - > ammoremoval = ( UINT16 ) SHORT ( players [ i ] . ammoremoval ) ;
rsp - > ammoremovaltimer = ( tic_t ) LONG ( players [ i ] . ammoremovaltimer ) ;
rsp - > ammoremovalweapon = LONG ( players [ i ] . ammoremovalweapon ) ;
2014-03-21 18:42:55 +00:00
for ( j = 0 ; j < NUMPOWERS ; + + j )
rsp - > powers [ j ] = ( UINT16 ) SHORT ( players [ i ] . powers [ j ] ) ;
// Score is resynched in the rspfirm resync packet
2018-06-03 21:41:54 +00:00
rsp - > rings = SHORT ( players [ i ] . rings ) ;
rsp - > spheres = SHORT ( players [ i ] . spheres ) ;
2014-03-21 18:42:55 +00:00
rsp - > lives = players [ i ] . lives ;
rsp - > continues = players [ i ] . continues ;
rsp - > scoreadd = players [ i ] . scoreadd ;
rsp - > xtralife = players [ i ] . xtralife ;
rsp - > pity = players [ i ] . pity ;
rsp - > skincolor = players [ i ] . skincolor ;
rsp - > skin = LONG ( players [ i ] . skin ) ;
2017-03-16 20:55:41 +00:00
rsp - > availabilities = LONG ( players [ i ] . availabilities ) ;
2014-03-21 18:42:55 +00:00
// Just in case Lua does something like
// modify these at runtime
2016-09-25 17:21:52 +00:00
rsp - > camerascale = ( fixed_t ) LONG ( players [ i ] . camerascale ) ;
rsp - > shieldscale = ( fixed_t ) LONG ( players [ i ] . shieldscale ) ;
2014-03-21 18:42:55 +00:00
rsp - > normalspeed = ( fixed_t ) LONG ( players [ i ] . normalspeed ) ;
rsp - > runspeed = ( fixed_t ) LONG ( players [ i ] . runspeed ) ;
rsp - > thrustfactor = players [ i ] . thrustfactor ;
rsp - > accelstart = players [ i ] . accelstart ;
rsp - > acceleration = players [ i ] . acceleration ;
rsp - > charability = players [ i ] . charability ;
rsp - > charability2 = players [ i ] . charability2 ;
rsp - > charflags = ( UINT32 ) LONG ( players [ i ] . charflags ) ;
rsp - > thokitem = ( UINT32 ) LONG ( players [ i ] . thokitem ) ; //mobjtype_t
rsp - > spinitem = ( UINT32 ) LONG ( players [ i ] . spinitem ) ; //mobjtype_t
rsp - > revitem = ( UINT32 ) LONG ( players [ i ] . revitem ) ; //mobjtype_t
2017-10-02 13:08:58 +00:00
rsp - > followitem = ( UINT32 ) LONG ( players [ i ] . followitem ) ; //mobjtype_t
2016-04-16 17:40:14 +00:00
rsp - > actionspd = ( fixed_t ) LONG ( players [ i ] . actionspd ) ;
rsp - > mindash = ( fixed_t ) LONG ( players [ i ] . mindash ) ;
rsp - > maxdash = ( fixed_t ) LONG ( players [ i ] . maxdash ) ;
2014-03-21 18:42:55 +00:00
rsp - > jumpfactor = ( fixed_t ) LONG ( players [ i ] . jumpfactor ) ;
2016-09-25 17:21:52 +00:00
rsp - > playerheight = ( fixed_t ) LONG ( players [ i ] . height ) ;
rsp - > playerspinheight = ( fixed_t ) LONG ( players [ i ] . spinheight ) ;
2014-03-21 18:42:55 +00:00
rsp - > speed = ( fixed_t ) LONG ( players [ i ] . speed ) ;
rsp - > secondjump = players [ i ] . secondjump ;
rsp - > fly1 = players [ i ] . fly1 ;
rsp - > glidetime = ( tic_t ) LONG ( players [ i ] . glidetime ) ;
rsp - > climbing = players [ i ] . climbing ;
rsp - > deadtimer = players [ i ] . deadtimer ;
rsp - > exiting = ( tic_t ) LONG ( players [ i ] . exiting ) ;
rsp - > homing = players [ i ] . homing ;
2016-01-25 11:47:33 +00:00
rsp - > dashmode = ( tic_t ) LONG ( players [ i ] . dashmode ) ;
2016-04-16 17:40:14 +00:00
rsp - > skidtime = ( tic_t ) LONG ( players [ i ] . skidtime ) ;
2014-03-21 18:42:55 +00:00
rsp - > cmomx = ( fixed_t ) LONG ( players [ i ] . cmomx ) ;
rsp - > cmomy = ( fixed_t ) LONG ( players [ i ] . cmomy ) ;
rsp - > rmomx = ( fixed_t ) LONG ( players [ i ] . rmomx ) ;
rsp - > rmomy = ( fixed_t ) LONG ( players [ i ] . rmomy ) ;
rsp - > weapondelay = LONG ( players [ i ] . weapondelay ) ;
rsp - > tossdelay = LONG ( players [ i ] . tossdelay ) ;
rsp - > starpostx = SHORT ( players [ i ] . starpostx ) ;
rsp - > starposty = SHORT ( players [ i ] . starposty ) ;
rsp - > starpostz = SHORT ( players [ i ] . starpostz ) ;
rsp - > starpostnum = LONG ( players [ i ] . starpostnum ) ;
rsp - > starposttime = ( tic_t ) LONG ( players [ i ] . starposttime ) ;
rsp - > starpostangle = ( angle_t ) LONG ( players [ i ] . starpostangle ) ;
2019-09-07 21:11:33 +00:00
rsp - > starpostscale = ( fixed_t ) LONG ( players [ i ] . starpostscale ) ;
2014-03-21 18:42:55 +00:00
rsp - > maxlink = LONG ( players [ i ] . maxlink ) ;
rsp - > dashspeed = ( fixed_t ) LONG ( players [ i ] . dashspeed ) ;
rsp - > angle_pos = ( angle_t ) LONG ( players [ i ] . angle_pos ) ;
rsp - > old_angle_pos = ( angle_t ) LONG ( players [ i ] . old_angle_pos ) ;
rsp - > bumpertime = ( tic_t ) LONG ( players [ i ] . bumpertime ) ;
rsp - > flyangle = LONG ( players [ i ] . flyangle ) ;
rsp - > drilltimer = ( tic_t ) LONG ( players [ i ] . drilltimer ) ;
rsp - > linkcount = LONG ( players [ i ] . linkcount ) ;
rsp - > linktimer = ( tic_t ) LONG ( players [ i ] . linktimer ) ;
rsp - > anotherflyangle = LONG ( players [ i ] . anotherflyangle ) ;
rsp - > nightstime = ( tic_t ) LONG ( players [ i ] . nightstime ) ;
rsp - > drillmeter = LONG ( players [ i ] . drillmeter ) ;
rsp - > drilldelay = players [ i ] . drilldelay ;
rsp - > bonustime = players [ i ] . bonustime ;
rsp - > mare = players [ i ] . mare ;
rsp - > lastsidehit = SHORT ( players [ i ] . lastsidehit ) ;
rsp - > lastlinehit = SHORT ( players [ i ] . lastlinehit ) ;
rsp - > losstime = ( tic_t ) LONG ( players [ i ] . losstime ) ;
rsp - > timeshit = players [ i ] . timeshit ;
rsp - > onconveyor = LONG ( players [ i ] . onconveyor ) ;
rsp - > hasmo = false ;
//Transfer important mo information if the player has a body.
//This lets us resync players even if they are dead.
if ( ! players [ i ] . mo )
return ;
rsp - > hasmo = true ;
rsp - > health = LONG ( players [ i ] . mo - > health ) ;
rsp - > angle = ( angle_t ) LONG ( players [ i ] . mo - > angle ) ;
2019-11-15 01:14:40 +00:00
rsp - > rollangle = ( angle_t ) LONG ( players [ i ] . mo - > rollangle ) ;
2014-03-21 18:42:55 +00:00
rsp - > x = LONG ( players [ i ] . mo - > x ) ;
rsp - > y = LONG ( players [ i ] . mo - > y ) ;
rsp - > z = LONG ( players [ i ] . mo - > z ) ;
rsp - > momx = LONG ( players [ i ] . mo - > momx ) ;
rsp - > momy = LONG ( players [ i ] . mo - > momy ) ;
rsp - > momz = LONG ( players [ i ] . mo - > momz ) ;
rsp - > friction = LONG ( players [ i ] . mo - > friction ) ;
rsp - > movefactor = LONG ( players [ i ] . mo - > movefactor ) ;
2019-10-17 17:22:06 +00:00
rsp - > sprite = ( spritenum_t ) LONG ( players [ i ] . mo - > sprite ) ;
rsp - > frame = LONG ( players [ i ] . mo - > frame ) ;
rsp - > sprite2 = players [ i ] . mo - > sprite2 ;
rsp - > anim_duration = SHORT ( players [ i ] . mo - > anim_duration ) ;
2014-03-21 18:42:55 +00:00
rsp - > tics = LONG ( players [ i ] . mo - > tics ) ;
rsp - > statenum = ( statenum_t ) LONG ( players [ i ] . mo - > state - states ) ; // :(
2016-04-16 17:40:14 +00:00
rsp - > eflags = ( UINT16 ) SHORT ( players [ i ] . mo - > eflags ) ;
2014-03-21 18:42:55 +00:00
rsp - > flags = LONG ( players [ i ] . mo - > flags ) ;
rsp - > flags2 = LONG ( players [ i ] . mo - > flags2 ) ;
rsp - > radius = LONG ( players [ i ] . mo - > radius ) ;
rsp - > height = LONG ( players [ i ] . mo - > height ) ;
rsp - > scale = LONG ( players [ i ] . mo - > scale ) ;
rsp - > destscale = LONG ( players [ i ] . mo - > destscale ) ;
rsp - > scalespeed = LONG ( players [ i ] . mo - > scalespeed ) ;
}
static void resynch_read_player ( resynch_pak * rsp )
{
INT32 i = rsp - > playernum , j ;
mobj_t * savedmo = players [ i ] . mo ;
// Do not send anything visual related.
// Only send data that we need to know for physics.
players [ i ] . playerstate = ( UINT8 ) rsp - > playerstate ; //playerstate_t
players [ i ] . pflags = ( UINT32 ) LONG ( rsp - > pflags ) ; //pflags_t
players [ i ] . panim = ( UINT8 ) rsp - > panim ; //panim_t
players [ i ] . aiming = ( angle_t ) LONG ( rsp - > aiming ) ;
players [ i ] . currentweapon = LONG ( rsp - > currentweapon ) ;
players [ i ] . ringweapons = LONG ( rsp - > ringweapons ) ;
2019-08-26 23:07:17 +00:00
players [ i ] . ammoremoval = ( UINT16 ) SHORT ( rsp - > ammoremoval ) ;
players [ i ] . ammoremovaltimer = ( tic_t ) LONG ( rsp - > ammoremovaltimer ) ;
players [ i ] . ammoremovalweapon = LONG ( rsp - > ammoremovalweapon ) ;
2014-03-21 18:42:55 +00:00
for ( j = 0 ; j < NUMPOWERS ; + + j )
players [ i ] . powers [ j ] = ( UINT16 ) SHORT ( rsp - > powers [ j ] ) ;
// Score is resynched in the rspfirm resync packet
2018-06-03 21:41:54 +00:00
players [ i ] . rings = SHORT ( rsp - > rings ) ;
players [ i ] . spheres = SHORT ( rsp - > spheres ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . lives = rsp - > lives ;
players [ i ] . continues = rsp - > continues ;
players [ i ] . scoreadd = rsp - > scoreadd ;
players [ i ] . xtralife = rsp - > xtralife ;
players [ i ] . pity = rsp - > pity ;
players [ i ] . skincolor = rsp - > skincolor ;
players [ i ] . skin = LONG ( rsp - > skin ) ;
2017-03-16 20:55:41 +00:00
players [ i ] . availabilities = LONG ( rsp - > availabilities ) ;
2014-03-21 18:42:55 +00:00
// Just in case Lua does something like
// modify these at runtime
2016-09-25 17:21:52 +00:00
players [ i ] . camerascale = ( fixed_t ) LONG ( rsp - > camerascale ) ;
players [ i ] . shieldscale = ( fixed_t ) LONG ( rsp - > shieldscale ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . normalspeed = ( fixed_t ) LONG ( rsp - > normalspeed ) ;
players [ i ] . runspeed = ( fixed_t ) LONG ( rsp - > runspeed ) ;
players [ i ] . thrustfactor = rsp - > thrustfactor ;
players [ i ] . accelstart = rsp - > accelstart ;
players [ i ] . acceleration = rsp - > acceleration ;
players [ i ] . charability = rsp - > charability ;
players [ i ] . charability2 = rsp - > charability2 ;
players [ i ] . charflags = ( UINT32 ) LONG ( rsp - > charflags ) ;
players [ i ] . thokitem = ( UINT32 ) LONG ( rsp - > thokitem ) ; //mobjtype_t
players [ i ] . spinitem = ( UINT32 ) LONG ( rsp - > spinitem ) ; //mobjtype_t
players [ i ] . revitem = ( UINT32 ) LONG ( rsp - > revitem ) ; //mobjtype_t
2017-10-02 13:08:58 +00:00
players [ i ] . followitem = ( UINT32 ) LONG ( rsp - > followitem ) ; //mobjtype_t
2016-04-16 17:40:14 +00:00
players [ i ] . actionspd = ( fixed_t ) LONG ( rsp - > actionspd ) ;
players [ i ] . mindash = ( fixed_t ) LONG ( rsp - > mindash ) ;
players [ i ] . maxdash = ( fixed_t ) LONG ( rsp - > maxdash ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . jumpfactor = ( fixed_t ) LONG ( rsp - > jumpfactor ) ;
2016-09-25 17:21:52 +00:00
players [ i ] . height = ( fixed_t ) LONG ( rsp - > playerheight ) ;
players [ i ] . spinheight = ( fixed_t ) LONG ( rsp - > playerspinheight ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . speed = ( fixed_t ) LONG ( rsp - > speed ) ;
players [ i ] . secondjump = rsp - > secondjump ;
players [ i ] . fly1 = rsp - > fly1 ;
players [ i ] . glidetime = ( tic_t ) LONG ( rsp - > glidetime ) ;
players [ i ] . climbing = rsp - > climbing ;
players [ i ] . deadtimer = rsp - > deadtimer ;
players [ i ] . exiting = ( tic_t ) LONG ( rsp - > exiting ) ;
players [ i ] . homing = rsp - > homing ;
2016-01-25 11:47:33 +00:00
players [ i ] . dashmode = ( tic_t ) LONG ( rsp - > dashmode ) ;
2016-04-16 17:40:14 +00:00
players [ i ] . skidtime = ( tic_t ) LONG ( rsp - > skidtime ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . cmomx = ( fixed_t ) LONG ( rsp - > cmomx ) ;
players [ i ] . cmomy = ( fixed_t ) LONG ( rsp - > cmomy ) ;
players [ i ] . rmomx = ( fixed_t ) LONG ( rsp - > rmomx ) ;
players [ i ] . rmomy = ( fixed_t ) LONG ( rsp - > rmomy ) ;
players [ i ] . weapondelay = LONG ( rsp - > weapondelay ) ;
players [ i ] . tossdelay = LONG ( rsp - > tossdelay ) ;
players [ i ] . starpostx = SHORT ( rsp - > starpostx ) ;
players [ i ] . starposty = SHORT ( rsp - > starposty ) ;
players [ i ] . starpostz = SHORT ( rsp - > starpostz ) ;
players [ i ] . starpostnum = LONG ( rsp - > starpostnum ) ;
players [ i ] . starposttime = ( tic_t ) LONG ( rsp - > starposttime ) ;
players [ i ] . starpostangle = ( angle_t ) LONG ( rsp - > starpostangle ) ;
2019-09-07 21:11:33 +00:00
players [ i ] . starpostscale = ( fixed_t ) LONG ( rsp - > starpostscale ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . maxlink = LONG ( rsp - > maxlink ) ;
players [ i ] . dashspeed = ( fixed_t ) LONG ( rsp - > dashspeed ) ;
players [ i ] . angle_pos = ( angle_t ) LONG ( rsp - > angle_pos ) ;
players [ i ] . old_angle_pos = ( angle_t ) LONG ( rsp - > old_angle_pos ) ;
players [ i ] . bumpertime = ( tic_t ) LONG ( rsp - > bumpertime ) ;
players [ i ] . flyangle = LONG ( rsp - > flyangle ) ;
players [ i ] . drilltimer = ( tic_t ) LONG ( rsp - > drilltimer ) ;
players [ i ] . linkcount = LONG ( rsp - > linkcount ) ;
players [ i ] . linktimer = ( tic_t ) LONG ( rsp - > linktimer ) ;
players [ i ] . anotherflyangle = LONG ( rsp - > anotherflyangle ) ;
players [ i ] . nightstime = ( tic_t ) LONG ( rsp - > nightstime ) ;
players [ i ] . drillmeter = LONG ( rsp - > drillmeter ) ;
players [ i ] . drilldelay = rsp - > drilldelay ;
players [ i ] . bonustime = rsp - > bonustime ;
players [ i ] . mare = rsp - > mare ;
players [ i ] . lastsidehit = SHORT ( rsp - > lastsidehit ) ;
players [ i ] . lastlinehit = SHORT ( rsp - > lastlinehit ) ;
players [ i ] . losstime = ( tic_t ) LONG ( rsp - > losstime ) ;
players [ i ] . timeshit = rsp - > timeshit ;
players [ i ] . onconveyor = LONG ( rsp - > onconveyor ) ;
//We get a packet for each player in game.
if ( ! playeringame [ i ] )
return ;
//...but keep old mo even if it is corrupt or null!
players [ i ] . mo = savedmo ;
//Transfer important mo information if they have a valid mo.
if ( ! rsp - > hasmo )
return ;
//server thinks player has a body.
//Give them a new body that can be then manipulated by the server's info.
if ( ! players [ i ] . mo ) //client thinks it has no body.
P_SpawnPlayer ( i ) ;
//At this point, the player should have a body, whether they were respawned or not.
P_UnsetThingPosition ( players [ i ] . mo ) ;
players [ i ] . mo - > angle = ( angle_t ) LONG ( rsp - > angle ) ;
2019-11-15 01:14:40 +00:00
players [ i ] . mo - > rollangle = ( angle_t ) LONG ( rsp - > rollangle ) ;
2016-04-16 17:40:14 +00:00
players [ i ] . mo - > eflags = ( UINT16 ) SHORT ( rsp - > eflags ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . mo - > flags = LONG ( rsp - > flags ) ;
players [ i ] . mo - > flags2 = LONG ( rsp - > flags2 ) ;
players [ i ] . mo - > friction = LONG ( rsp - > friction ) ;
players [ i ] . mo - > health = LONG ( rsp - > health ) ;
players [ i ] . mo - > momx = LONG ( rsp - > momx ) ;
players [ i ] . mo - > momy = LONG ( rsp - > momy ) ;
players [ i ] . mo - > momz = LONG ( rsp - > momz ) ;
players [ i ] . mo - > movefactor = LONG ( rsp - > movefactor ) ;
2019-10-17 17:22:06 +00:00
// Don't use P_SetMobjStateNF to restore state, write/read all the values manually!
// This should stop those stupid console errors, hopefully.
// -- Monster Iestyn
players [ i ] . mo - > sprite = ( spritenum_t ) LONG ( rsp - > sprite ) ;
players [ i ] . mo - > frame = LONG ( rsp - > frame ) ;
players [ i ] . mo - > sprite2 = rsp - > sprite2 ;
players [ i ] . mo - > anim_duration = SHORT ( rsp - > anim_duration ) ;
2014-03-21 18:42:55 +00:00
players [ i ] . mo - > tics = LONG ( rsp - > tics ) ;
2019-10-18 12:37:42 +00:00
players [ i ] . mo - > state = & states [ LONG ( rsp - > statenum ) ] ;
2019-10-17 17:22:06 +00:00
2014-03-21 18:42:55 +00:00
players [ i ] . mo - > x = LONG ( rsp - > x ) ;
players [ i ] . mo - > y = LONG ( rsp - > y ) ;
players [ i ] . mo - > z = LONG ( rsp - > z ) ;
players [ i ] . mo - > radius = LONG ( rsp - > radius ) ;
players [ i ] . mo - > height = LONG ( rsp - > height ) ;
// P_SetScale is redundant for this, as all related variables are already restored properly.
players [ i ] . mo - > scale = LONG ( rsp - > scale ) ;
players [ i ] . mo - > destscale = LONG ( rsp - > destscale ) ;
players [ i ] . mo - > scalespeed = LONG ( rsp - > scalespeed ) ;
// And finally, SET THE MOBJ SKIN damn it.
2019-11-14 16:37:03 +00:00
if ( ( players [ i ] . powers [ pw_carry ] = = CR_NIGHTSMODE ) & & ( skins [ players [ i ] . skin ] . sprites [ SPR2_NFLY ] . numframes = = 0 ) )
2017-06-01 21:20:36 +00:00
{
players [ i ] . mo - > skin = & skins [ DEFAULTNIGHTSSKIN ] ;
players [ i ] . mo - > color = skins [ DEFAULTNIGHTSSKIN ] . prefcolor ; // this will be corrected by thinker to super flash
}
else
{
players [ i ] . mo - > skin = & skins [ players [ i ] . skin ] ;
players [ i ] . mo - > color = players [ i ] . skincolor ; // this will be corrected by thinker to super flash/mario star
}
2014-03-21 18:42:55 +00:00
P_SetThingPosition ( players [ i ] . mo ) ;
}
static inline void resynch_write_ctf ( resynchend_pak * rst )
{
mobj_t * mflag ;
UINT8 i , j ;
for ( i = 0 , mflag = redflag ; i < 2 ; + + i , mflag = blueflag )
{
rst - > flagx [ i ] = rst - > flagy [ i ] = rst - > flagz [ i ] = 0 ;
rst - > flagloose [ i ] = rst - > flagflags [ i ] = 0 ;
rst - > flagplayer [ i ] = - 1 ;
if ( ! mflag )
{
// Should be held by a player
for ( j = 0 ; j < MAXPLAYERS ; + + j )
{
2014-03-23 16:00:29 +00:00
// GF_REDFLAG is 1, GF_BLUEFLAG is 2
// redflag handling is i=0, blueflag is i=1
// so check for gotflag == (i+1)
if ( ! playeringame [ j ] | | players [ j ] . gotflag ! = ( i + 1 ) )
2014-03-21 18:42:55 +00:00
continue ;
rst - > flagplayer [ i ] = ( SINT8 ) j ;
break ;
}
2014-03-25 02:17:59 +00:00
if ( j = = MAXPLAYERS ) // fine, no I_Error
{
2014-08-05 23:59:40 +00:00
CONS_Alert ( CONS_ERROR , " One of the flags has gone completely missing... \n " ) ;
2014-03-25 02:17:59 +00:00
rst - > flagplayer [ i ] = - 2 ;
}
2014-03-21 18:42:55 +00:00
continue ;
}
rst - > flagx [ i ] = ( fixed_t ) LONG ( mflag - > x ) ;
rst - > flagy [ i ] = ( fixed_t ) LONG ( mflag - > y ) ;
rst - > flagz [ i ] = ( fixed_t ) LONG ( mflag - > z ) ;
rst - > flagflags [ i ] = LONG ( mflag - > flags2 ) ;
rst - > flagloose [ i ] = LONG ( mflag - > fuse ) ; // Dropped or not?
}
}
static inline void resynch_read_ctf ( resynchend_pak * p )
{
UINT8 i ;
for ( i = 0 ; i < MAXPLAYERS ; + + i )
players [ i ] . gotflag = 0 ;
// Red flag
2014-03-25 02:17:59 +00:00
if ( p - > flagplayer [ 0 ] = = - 2 )
; // The server doesn't even know what happened to it...
else if ( p - > flagplayer [ 0 ] ! = - 1 ) // Held by a player
2014-03-21 18:42:55 +00:00
{
if ( ! playeringame [ p - > flagplayer [ 0 ] ] )
I_Error ( " Invalid red flag player %d who isn't in the game! " , ( INT32 ) p - > flagplayer [ 0 ] ) ;
players [ p - > flagplayer [ 0 ] ] . gotflag = GF_REDFLAG ;
if ( redflag )
{
P_RemoveMobj ( redflag ) ;
redflag = NULL ;
}
}
else
{
if ( ! redflag )
redflag = P_SpawnMobj ( 0 , 0 , 0 , MT_REDFLAG ) ;
P_UnsetThingPosition ( redflag ) ;
redflag - > x = ( fixed_t ) LONG ( p - > flagx [ 0 ] ) ;
redflag - > y = ( fixed_t ) LONG ( p - > flagy [ 0 ] ) ;
redflag - > z = ( fixed_t ) LONG ( p - > flagz [ 0 ] ) ;
redflag - > flags2 = LONG ( p - > flagflags [ 0 ] ) ;
redflag - > fuse = LONG ( p - > flagloose [ 0 ] ) ;
P_SetThingPosition ( redflag ) ;
}
// Blue flag
2014-03-25 02:17:59 +00:00
if ( p - > flagplayer [ 1 ] = = - 2 )
; // The server doesn't even know what happened to it...
else if ( p - > flagplayer [ 1 ] ! = - 1 ) // Held by a player
2014-03-21 18:42:55 +00:00
{
if ( ! playeringame [ p - > flagplayer [ 1 ] ] )
I_Error ( " Invalid blue flag player %d who isn't in the game! " , ( INT32 ) p - > flagplayer [ 1 ] ) ;
2015-01-01 19:50:31 +00:00
players [ p - > flagplayer [ 1 ] ] . gotflag = GF_BLUEFLAG ;
2014-03-21 18:42:55 +00:00
if ( blueflag )
{
P_RemoveMobj ( blueflag ) ;
blueflag = NULL ;
}
}
else
{
if ( ! blueflag )
blueflag = P_SpawnMobj ( 0 , 0 , 0 , MT_BLUEFLAG ) ;
P_UnsetThingPosition ( blueflag ) ;
2014-03-25 02:17:59 +00:00
blueflag - > x = ( fixed_t ) LONG ( p - > flagx [ 1 ] ) ;
blueflag - > y = ( fixed_t ) LONG ( p - > flagy [ 1 ] ) ;
blueflag - > z = ( fixed_t ) LONG ( p - > flagz [ 1 ] ) ;
blueflag - > flags2 = LONG ( p - > flagflags [ 1 ] ) ;
blueflag - > fuse = LONG ( p - > flagloose [ 1 ] ) ;
2014-03-21 18:42:55 +00:00
P_SetThingPosition ( blueflag ) ;
}
}
static inline void resynch_write_others ( resynchend_pak * rst )
{
UINT8 i ;
2017-01-03 22:43:46 +00:00
rst - > ingame = 0 ;
2017-07-03 14:43:29 +00:00
rst - > outofcoop = 0 ;
2014-03-21 18:42:55 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
if ( ! playeringame [ i ] )
{
2017-01-03 22:43:46 +00:00
rst - > ctfteam [ i ] = 0 ;
2014-03-21 18:42:55 +00:00
rst - > score [ i ] = 0 ;
rst - > numboxes [ i ] = 0 ;
rst - > totalring [ i ] = 0 ;
rst - > realtime [ i ] = 0 ;
rst - > laps [ i ] = 0 ;
continue ;
}
if ( ! players [ i ] . spectator )
rst - > ingame | = ( 1 < < i ) ;
2017-07-03 14:43:29 +00:00
if ( players [ i ] . outofcoop )
rst - > outofcoop | = ( 1 < < i ) ;
2017-01-03 22:43:46 +00:00
rst - > ctfteam [ i ] = ( INT32 ) LONG ( players [ i ] . ctfteam ) ;
2014-03-21 18:42:55 +00:00
rst - > score [ i ] = ( UINT32 ) LONG ( players [ i ] . score ) ;
rst - > numboxes [ i ] = SHORT ( players [ i ] . numboxes ) ;
rst - > totalring [ i ] = SHORT ( players [ i ] . totalring ) ;
rst - > realtime [ i ] = ( tic_t ) LONG ( players [ i ] . realtime ) ;
rst - > laps [ i ] = players [ i ] . laps ;
}
// endian safeness
rst - > ingame = ( UINT32 ) LONG ( rst - > ingame ) ;
}
static inline void resynch_read_others ( resynchend_pak * p )
{
UINT8 i ;
UINT32 loc_ingame = ( UINT32 ) LONG ( p - > ingame ) ;
2017-07-03 14:43:29 +00:00
UINT32 loc_outofcoop = ( UINT32 ) LONG ( p - > outofcoop ) ;
2014-03-21 18:42:55 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
// We don't care if they're in the game or not, just write all the data.
2017-01-15 22:24:38 +00:00
players [ i ] . spectator = ! ( loc_ingame & ( 1 < < i ) ) ;
2017-07-03 14:43:29 +00:00
players [ i ] . outofcoop = ( loc_outofcoop & ( 1 < < i ) ) ;
2017-01-03 22:43:46 +00:00
players [ i ] . ctfteam = ( INT32 ) LONG ( p - > ctfteam [ i ] ) ; // no, 0 does not mean spectator, at least not in Match
2014-03-21 18:42:55 +00:00
players [ i ] . score = ( UINT32 ) LONG ( p - > score [ i ] ) ;
players [ i ] . numboxes = SHORT ( p - > numboxes [ i ] ) ;
players [ i ] . totalring = SHORT ( p - > totalring [ i ] ) ;
players [ i ] . realtime = ( tic_t ) LONG ( p - > realtime [ i ] ) ;
players [ i ] . laps = p - > laps [ i ] ;
}
}
static void SV_InitResynchVars ( INT32 node )
{
resynch_delay [ node ] = TICRATE ; // initial one second delay
resynch_score [ node ] = 0 ; // clean slate
resynch_status [ node ] = 0x00 ;
resynch_inprogress [ node ] = false ;
2017-12-12 22:08:18 +00:00
memset ( resynch_sent [ node ] , 0 , MAXPLAYERS ) ;
2014-03-21 18:42:55 +00:00
}
static void SV_RequireResynch ( INT32 node )
{
INT32 i ;
resynch_delay [ node ] = 10 ; // Delay before you can fail sync again
resynch_score [ node ] + = 200 ; // Add score for initial desynch
2017-12-12 21:48:08 +00:00
resynch_status [ node ] = 0xFFFFFFFF ; // No players assumed synched
2014-03-21 18:42:55 +00:00
resynch_inprogress [ node ] = true ; // so we know to send a PT_RESYNCHEND after sync
// Initial setup
2017-12-12 22:08:18 +00:00
memset ( resynch_sent [ node ] , 0 , MAXPLAYERS ) ;
2014-03-21 18:42:55 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
if ( ! playeringame [ i ] ) // Player not in game so just drop it from required synch
resynch_status [ node ] & = ~ ( 1 < < i ) ;
2017-12-12 22:08:18 +00:00
else if ( playernode [ i ] = = node ) ; // instantly update THEIR position
2014-03-21 18:42:55 +00:00
else // Send at random times based on num players
resynch_sent [ node ] [ i ] = M_RandomKey ( D_NumPlayers ( ) > > 1 ) + 1 ;
}
}
static void SV_SendResynch ( INT32 node )
{
INT32 i , j ;
2014-08-27 03:56:30 +00:00
if ( ! nodeingame [ node ] )
{
// player left during resynch
// so obviously we don't need to do any of this anymore
resynch_inprogress [ node ] = false ;
return ;
}
2014-03-21 18:42:55 +00:00
// resynched?
if ( ! resynch_status [ node ] )
{
// you are now synched
resynch_inprogress [ node ] = false ;
netbuffer - > packettype = PT_RESYNCHEND ;
netbuffer - > u . resynchend . randomseed = P_GetRandSeed ( ) ;
2019-12-18 14:43:50 +00:00
if ( gametyperules & GTR_TEAMFLAGS )
2014-03-21 18:42:55 +00:00
resynch_write_ctf ( & netbuffer - > u . resynchend ) ;
resynch_write_others ( & netbuffer - > u . resynchend ) ;
HSendPacket ( node , true , 0 , ( sizeof ( resynchend_pak ) ) ) ;
return ;
}
netbuffer - > packettype = PT_RESYNCHING ;
for ( i = 0 , j = 0 ; i < MAXPLAYERS ; + + i )
{
// if already synched don't bother
if ( ! ( resynch_status [ node ] & 1 < < i ) )
continue ;
// waiting for a reply or just waiting in general
if ( resynch_sent [ node ] [ i ] )
{
- - resynch_sent [ node ] [ i ] ;
continue ;
}
resynch_write_player ( & netbuffer - > u . resynchpak , i ) ;
HSendPacket ( node , false , 0 , ( sizeof ( resynch_pak ) ) ) ;
resynch_sent [ node ] [ i ] = TICRATE ;
resynch_score [ node ] + = 2 ; // penalty for send
if ( + + j > 3 )
break ;
}
if ( resynch_score [ node ] > ( unsigned ) cv_resynchattempts . value * 250 )
{
2020-01-12 18:43:04 +00:00
SendKick ( nodetoplayer [ node ] , KICK_MSG_CON_FAIL ) ;
2014-03-21 18:42:55 +00:00
resynch_score [ node ] = 0 ;
}
}
static void CL_AcknowledgeResynch ( resynch_pak * rsp )
{
resynch_read_player ( rsp ) ;
netbuffer - > packettype = PT_RESYNCHGET ;
netbuffer - > u . resynchgot = rsp - > playernum ;
HSendPacket ( servernode , true , 0 , sizeof ( UINT8 ) ) ;
}
static void SV_AcknowledgeResynchAck ( INT32 node , UINT8 rsg )
{
if ( rsg > = MAXPLAYERS )
resynch_score [ node ] + = 16384 ; // lol.
else
{
resynch_status [ node ] & = ~ ( 1 < < rsg ) ;
- - resynch_score [ node ] ; // unpenalize
}
2017-01-13 19:53:52 +00:00
// Don't let resynch cause a timeout
freezetimeout [ node ] = I_GetTime ( ) + connectiontimeout ;
2014-03-21 18:42:55 +00:00
}
// -----------------------------------------------------------------
// end resynch
// -----------------------------------------------------------------
2014-03-15 16:59:03 +00:00
static INT16 Consistancy ( void ) ;
# ifndef NONET
# define JOININGAME
# endif
typedef enum
{
2016-12-31 18:26:33 +00:00
CL_SEARCHING ,
CL_DOWNLOADFILES ,
CL_ASKJOIN ,
CL_WAITJOINRESPONSE ,
2014-03-15 16:59:03 +00:00
# ifdef JOININGAME
2016-12-31 18:26:33 +00:00
CL_DOWNLOADSAVEGAME ,
2014-03-15 16:59:03 +00:00
# endif
2016-12-31 18:26:33 +00:00
CL_CONNECTED ,
CL_ABORTED
2014-03-15 16:59:03 +00:00
} cl_mode_t ;
static void GetPackets ( void ) ;
2016-12-31 18:26:33 +00:00
static cl_mode_t cl_mode = CL_SEARCHING ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
// Player name send/load
static void CV_SavePlayerNames ( UINT8 * * p )
{
INT32 i = 0 ;
// Players in game only.
for ( ; i < MAXPLAYERS ; + + i )
{
if ( ! playeringame [ i ] )
{
WRITEUINT8 ( * p , 0 ) ;
continue ;
}
WRITESTRING ( * p , player_names [ i ] ) ;
}
}
static void CV_LoadPlayerNames ( UINT8 * * p )
{
INT32 i = 0 ;
char tmp_name [ MAXPLAYERNAME + 1 ] ;
tmp_name [ MAXPLAYERNAME ] = 0 ;
for ( ; i < MAXPLAYERS ; + + i )
{
READSTRING ( * p , tmp_name ) ;
if ( tmp_name [ 0 ] = = 0 )
continue ;
if ( tmp_name [ MAXPLAYERNAME ] ) // overflow detected
I_Error ( " Received bad server config packet when trying to join " ) ;
memcpy ( player_names [ i ] , tmp_name , MAXPLAYERNAME + 1 ) ;
}
}
2014-03-15 16:59:03 +00:00
# ifdef CLIENT_LOADINGSCREEN
//
// CL_DrawConnectionStatus
//
// Keep the local client informed of our status.
//
static inline void CL_DrawConnectionStatus ( void )
{
INT32 ccstime = I_GetTime ( ) ;
// Draw background fade
2018-11-20 22:28:26 +00:00
if ( ! menuactive ) // menu already draws its own fade
2018-11-20 23:13:18 +00:00
V_DrawFadeScreen ( 0xFF00 , 16 ) ; // force default
2014-03-15 16:59:03 +00:00
// Draw the bottom box.
M_DrawTextBox ( BASEVIDWIDTH / 2 - 128 - 8 , BASEVIDHEIGHT - 24 - 8 , 32 , 1 ) ;
V_DrawCenteredString ( BASEVIDWIDTH / 2 , BASEVIDHEIGHT - 24 - 24 , V_YELLOWMAP , " Press ESC to abort " ) ;
2016-12-31 18:26:33 +00:00
if ( cl_mode ! = CL_DOWNLOADFILES )
2014-03-15 16:59:03 +00:00
{
INT32 i , animtime = ( ( ccstime / 4 ) & 15 ) + 16 ;
2017-01-07 15:03:59 +00:00
UINT8 palstart = ( cl_mode = = CL_SEARCHING ) ? 32 : 96 ;
2014-03-15 16:59:03 +00:00
// 15 pal entries total.
const char * cltext ;
for ( i = 0 ; i < 16 ; + + i )
V_DrawFill ( ( BASEVIDWIDTH / 2 - 128 ) + ( i * 16 ) , BASEVIDHEIGHT - 24 , 16 , 8 , palstart + ( ( animtime - i ) & 15 ) ) ;
switch ( cl_mode )
{
2014-08-04 03:49:33 +00:00
# ifdef JOININGAME
2016-12-31 18:26:33 +00:00
case CL_DOWNLOADSAVEGAME :
2017-01-13 19:53:52 +00:00
if ( lastfilenum ! = - 1 )
{
cltext = M_GetText ( " Downloading game state... " ) ;
Net_GetNetStat ( ) ;
V_DrawString ( BASEVIDWIDTH / 2 - 128 , BASEVIDHEIGHT - 24 , V_20TRANS | V_MONOSPACE ,
va ( " %4uK " , fileneeded [ lastfilenum ] . currentsize > > 10 ) ) ;
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 128 , BASEVIDHEIGHT - 24 , V_20TRANS | V_MONOSPACE ,
va ( " %3.1fK/s " , ( ( double ) getbps ) / 1024 ) ) ;
}
else
cltext = M_GetText ( " Waiting to download game state... " ) ;
2014-03-15 16:59:03 +00:00
break ;
2014-08-04 03:49:33 +00:00
# endif
2016-12-31 18:26:33 +00:00
case CL_ASKJOIN :
case CL_WAITJOINRESPONSE :
2014-03-15 16:59:03 +00:00
cltext = M_GetText ( " Requesting to join... " ) ;
break ;
default :
cltext = M_GetText ( " Connecting to server... " ) ;
break ;
}
V_DrawCenteredString ( BASEVIDWIDTH / 2 , BASEVIDHEIGHT - 24 - 32 , V_YELLOWMAP , cltext ) ;
}
else
{
2017-01-13 19:53:52 +00:00
if ( lastfilenum ! = - 1 )
{
INT32 dldlength ;
2018-03-06 20:52:55 +00:00
static char tempname [ 28 ] ;
2018-03-05 22:24:03 +00:00
fileneeded_t * file = & fileneeded [ lastfilenum ] ;
char * filename = file - > filename ;
2017-01-13 19:53:52 +00:00
Net_GetNetStat ( ) ;
2018-03-05 22:24:03 +00:00
dldlength = ( INT32 ) ( ( file - > currentsize / ( double ) file - > totalsize ) * 256 ) ;
2017-01-13 19:53:52 +00:00
if ( dldlength > 256 )
dldlength = 256 ;
2017-01-19 16:54:47 +00:00
V_DrawFill ( BASEVIDWIDTH / 2 - 128 , BASEVIDHEIGHT - 24 , 256 , 8 , 111 ) ;
V_DrawFill ( BASEVIDWIDTH / 2 - 128 , BASEVIDHEIGHT - 24 , dldlength , 8 , 96 ) ;
2017-01-13 19:53:52 +00:00
memset ( tempname , 0 , sizeof ( tempname ) ) ;
2018-03-05 22:24:03 +00:00
// offset filename to just the name only part
filename + = strlen ( filename ) - nameonlylength ( filename ) ;
2018-03-06 20:52:55 +00:00
if ( strlen ( filename ) > sizeof ( tempname ) - 1 ) // too long to display fully
2018-03-05 22:24:03 +00:00
{
2018-03-06 20:52:55 +00:00
size_t endhalfpos = strlen ( filename ) - 10 ;
// display as first 14 chars + ... + last 10 chars
// which should add up to 27 if our math(s) is correct
snprintf ( tempname , sizeof ( tempname ) , " %.14s...%.10s " , filename , filename + endhalfpos ) ;
2018-03-05 22:24:03 +00:00
}
else // we can copy the whole thing in safely
{
2018-03-06 20:52:55 +00:00
strncpy ( tempname , filename , sizeof ( tempname ) - 1 ) ;
2018-03-05 22:24:03 +00:00
}
2017-01-13 19:53:52 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 , BASEVIDHEIGHT - 24 - 32 , V_YELLOWMAP ,
va ( M_GetText ( " Downloading \" %s \" " ) , tempname ) ) ;
V_DrawString ( BASEVIDWIDTH / 2 - 128 , BASEVIDHEIGHT - 24 , V_20TRANS | V_MONOSPACE ,
2018-03-05 22:24:03 +00:00
va ( " %4uK/%4uK " , fileneeded [ lastfilenum ] . currentsize > > 10 , file - > totalsize > > 10 ) ) ;
2017-01-13 19:53:52 +00:00
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 128 , BASEVIDHEIGHT - 24 , V_20TRANS | V_MONOSPACE ,
va ( " %3.1fK/s " , ( ( double ) getbps ) / 1024 ) ) ;
}
else
V_DrawCenteredString ( BASEVIDWIDTH / 2 , BASEVIDHEIGHT - 24 - 32 , V_YELLOWMAP ,
M_GetText ( " Waiting to download files... " ) ) ;
2014-03-15 16:59:03 +00:00
}
}
# endif
2016-12-31 18:26:33 +00:00
/** Sends a special packet to declare how many players in local
* Used only in arbitratrenetstart ( )
* Sends a PT_CLIENTJOIN packet to the server
*
* \ return True if the packet was successfully sent
* \ todo Improve the description . . .
* Because to be honest , I have no idea what arbitratrenetstart is . . .
* Is it even used . . . ?
*
*/
2014-03-15 16:59:03 +00:00
static boolean CL_SendJoin ( void )
{
UINT8 localplayers = 1 ;
if ( netgame )
CONS_Printf ( M_GetText ( " Sending join request... \n " ) ) ;
netbuffer - > packettype = PT_CLIENTJOIN ;
if ( splitscreen | | botingame )
localplayers + + ;
netbuffer - > u . clientcfg . localplayers = localplayers ;
2019-12-26 04:44:25 +00:00
netbuffer - > u . clientcfg . _255 = 255 ;
netbuffer - > u . clientcfg . packetversion = PACKETVERSION ;
2014-03-15 16:59:03 +00:00
netbuffer - > u . clientcfg . version = VERSION ;
netbuffer - > u . clientcfg . subversion = SUBVERSION ;
2019-12-26 04:44:25 +00:00
strncpy ( netbuffer - > u . clientcfg . application , SRB2APPLICATION ,
sizeof netbuffer - > u . clientcfg . application ) ;
2019-10-19 15:09:18 +00:00
strncpy ( netbuffer - > u . clientcfg . names [ 0 ] , cv_playername . zstring , MAXPLAYERNAME ) ;
strncpy ( netbuffer - > u . clientcfg . names [ 1 ] , cv_playername2 . zstring , MAXPLAYERNAME ) ;
2014-03-15 16:59:03 +00:00
return HSendPacket ( servernode , true , 0 , sizeof ( clientconfig_pak ) ) ;
}
static void SV_SendServerInfo ( INT32 node , tic_t servertime )
{
UINT8 * p ;
netbuffer - > packettype = PT_SERVERINFO ;
2019-12-26 04:44:25 +00:00
netbuffer - > u . serverinfo . _255 = 255 ;
netbuffer - > u . serverinfo . packetversion = PACKETVERSION ;
2014-03-15 16:59:03 +00:00
netbuffer - > u . serverinfo . version = VERSION ;
netbuffer - > u . serverinfo . subversion = SUBVERSION ;
2019-12-26 04:44:25 +00:00
strncpy ( netbuffer - > u . serverinfo . application , SRB2APPLICATION ,
sizeof netbuffer - > u . serverinfo . application ) ;
2014-03-15 16:59:03 +00:00
// return back the time value so client can compute their ping
netbuffer - > u . serverinfo . time = ( tic_t ) LONG ( servertime ) ;
netbuffer - > u . serverinfo . leveltime = ( tic_t ) LONG ( leveltime ) ;
netbuffer - > u . serverinfo . numberofplayer = ( UINT8 ) D_NumPlayers ( ) ;
netbuffer - > u . serverinfo . maxplayer = ( UINT8 ) cv_maxplayers . value ;
2019-12-31 06:18:55 +00:00
strncpy ( netbuffer - > u . serverinfo . gametypename , Gametype_Names [ gametype ] ,
sizeof netbuffer - > u . serverinfo . gametypename ) ;
2014-03-15 16:59:03 +00:00
netbuffer - > u . serverinfo . modifiedgame = ( UINT8 ) modifiedgame ;
netbuffer - > u . serverinfo . cheatsenabled = CV_CheatsEnabled ( ) ;
netbuffer - > u . serverinfo . isdedicated = ( UINT8 ) dedicated ;
strncpy ( netbuffer - > u . serverinfo . servername , cv_servername . string ,
MAXSERVERNAME ) ;
strncpy ( netbuffer - > u . serverinfo . mapname , G_BuildMapName ( gamemap ) , 7 ) ;
M_Memcpy ( netbuffer - > u . serverinfo . mapmd5 , mapmd5 , 16 ) ;
2019-12-07 23:47:18 +00:00
memset ( netbuffer - > u . serverinfo . maptitle , 0 , sizeof netbuffer - > u . serverinfo . maptitle ) ;
2019-12-05 13:42:08 +00:00
if ( mapheaderinfo [ gamemap - 1 ] & & * mapheaderinfo [ gamemap - 1 ] - > lvlttl )
2019-11-19 13:36:33 +00:00
{
char * read = mapheaderinfo [ gamemap - 1 ] - > lvlttl , * writ = netbuffer - > u . serverinfo . maptitle ;
2019-11-19 20:24:44 +00:00
while ( writ < ( netbuffer - > u . serverinfo . maptitle + 32 ) & & * read ! = ' \0 ' )
2019-11-19 13:36:33 +00:00
{
if ( ! ( * read & 0x80 ) )
{
* writ = toupper ( * read ) ;
writ + + ;
}
read + + ;
}
2019-11-19 13:40:07 +00:00
* writ = ' \0 ' ;
2019-11-19 13:36:33 +00:00
//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
}
2014-03-15 16:59:03 +00:00
else
2019-11-19 20:24:44 +00:00
strncpy ( netbuffer - > u . serverinfo . maptitle , " UNKNOWN " , 32 ) ;
2014-03-15 16:59:03 +00:00
2019-12-05 13:42:08 +00:00
if ( mapheaderinfo [ gamemap - 1 ] & & ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_NOZONE ) )
2014-03-15 16:59:03 +00:00
netbuffer - > u . serverinfo . iszone = 1 ;
else
netbuffer - > u . serverinfo . iszone = 0 ;
2019-12-05 13:42:08 +00:00
if ( mapheaderinfo [ gamemap - 1 ] )
netbuffer - > u . serverinfo . actnum = mapheaderinfo [ gamemap - 1 ] - > actnum ;
2014-03-15 16:59:03 +00:00
p = PutFileNeeded ( ) ;
HSendPacket ( node , false , 0 , p - ( ( UINT8 * ) & netbuffer - > u ) ) ;
}
static void SV_SendPlayerInfo ( INT32 node )
{
UINT8 i ;
netbuffer - > packettype = PT_PLAYERINFO ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
{
2020-01-12 17:34:08 +00:00
netbuffer - > u . playerinfo [ i ] . num = 255 ; // This slot is empty.
2014-03-15 16:59:03 +00:00
continue ;
}
2020-01-12 17:34:08 +00:00
netbuffer - > u . playerinfo [ i ] . num = i ;
2014-03-15 16:59:03 +00:00
strncpy ( netbuffer - > u . playerinfo [ i ] . name , ( const char * ) & player_names [ i ] , MAXPLAYERNAME + 1 ) ;
netbuffer - > u . playerinfo [ i ] . name [ MAXPLAYERNAME ] = ' \0 ' ;
//fetch IP address
2019-04-15 04:14:01 +00:00
//No, don't do that, you fuckface.
memset ( netbuffer - > u . playerinfo [ i ] . address , 0 , 4 ) ;
2014-03-15 16:59:03 +00:00
if ( G_GametypeHasTeams ( ) )
{
if ( ! players [ i ] . ctfteam )
netbuffer - > u . playerinfo [ i ] . team = 255 ;
else
netbuffer - > u . playerinfo [ i ] . team = ( UINT8 ) players [ i ] . ctfteam ;
}
else
{
if ( players [ i ] . spectator )
netbuffer - > u . playerinfo [ i ] . team = 255 ;
else
netbuffer - > u . playerinfo [ i ] . team = 0 ;
}
netbuffer - > u . playerinfo [ i ] . score = LONG ( players [ i ] . score ) ;
netbuffer - > u . playerinfo [ i ] . timeinserver = SHORT ( ( UINT16 ) ( players [ i ] . jointime / TICRATE ) ) ;
2019-11-19 13:36:33 +00:00
netbuffer - > u . playerinfo [ i ] . skin = ( UINT8 ) ( players [ i ] . skin
# ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
% 3
# endif
) ;
2014-03-15 16:59:03 +00:00
// Extra data
2017-07-17 22:26:24 +00:00
netbuffer - > u . playerinfo [ i ] . data = 0 ; //players[i].skincolor;
2014-03-15 16:59:03 +00:00
if ( players [ i ] . pflags & PF_TAGIT )
netbuffer - > u . playerinfo [ i ] . data | = 0x20 ;
if ( players [ i ] . gotflag )
netbuffer - > u . playerinfo [ i ] . data | = 0x40 ;
if ( players [ i ] . powers [ pw_super ] )
netbuffer - > u . playerinfo [ i ] . data | = 0x80 ;
}
HSendPacket ( node , false , 0 , sizeof ( plrinfo ) * MAXPLAYERS ) ;
}
2016-12-31 18:26:33 +00:00
/** Sends a PT_SERVERCFG packet
*
* \ param node The destination
* \ return True if the packet was successfully sent
*
*/
2014-03-15 16:59:03 +00:00
static boolean SV_SendServerConfig ( INT32 node )
{
INT32 i ;
UINT8 * p , * op ;
boolean waspacketsent ;
netbuffer - > packettype = PT_SERVERCFG ;
netbuffer - > u . servercfg . version = VERSION ;
netbuffer - > u . servercfg . subversion = SUBVERSION ;
netbuffer - > u . servercfg . serverplayer = ( UINT8 ) serverplayer ;
netbuffer - > u . servercfg . totalslotnum = ( UINT8 ) ( doomcom - > numslots ) ;
netbuffer - > u . servercfg . gametic = ( tic_t ) LONG ( gametic ) ;
netbuffer - > u . servercfg . clientnode = ( UINT8 ) node ;
netbuffer - > u . servercfg . gamestate = ( UINT8 ) gamestate ;
netbuffer - > u . servercfg . gametype = ( UINT8 ) gametype ;
netbuffer - > u . servercfg . modifiedgame = ( UINT8 ) modifiedgame ;
2014-08-04 03:49:33 +00:00
// we fill these structs with FFs so that any players not in game get sent as 0xFFFF
// which is nice and easy for us to detect
memset ( netbuffer - > u . servercfg . playerskins , 0xFF , sizeof ( netbuffer - > u . servercfg . playerskins ) ) ;
memset ( netbuffer - > u . servercfg . playercolor , 0xFF , sizeof ( netbuffer - > u . servercfg . playercolor ) ) ;
2017-03-18 13:03:29 +00:00
memset ( netbuffer - > u . servercfg . playeravailabilities , 0xFF , sizeof ( netbuffer - > u . servercfg . playeravailabilities ) ) ;
2014-08-04 03:49:33 +00:00
2017-11-11 05:34:37 +00:00
memset ( netbuffer - > u . servercfg . adminplayers , - 1 , sizeof ( netbuffer - > u . servercfg . adminplayers ) ) ;
2014-08-04 03:49:33 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2017-12-08 04:45:39 +00:00
netbuffer - > u . servercfg . adminplayers [ i ] = ( SINT8 ) adminplayers [ i ] ;
2017-11-11 05:34:37 +00:00
2014-08-04 03:49:33 +00:00
if ( ! playeringame [ i ] )
continue ;
netbuffer - > u . servercfg . playerskins [ i ] = ( UINT8 ) players [ i ] . skin ;
netbuffer - > u . servercfg . playercolor [ i ] = ( UINT8 ) players [ i ] . skincolor ;
2017-03-18 10:12:14 +00:00
netbuffer - > u . servercfg . playeravailabilities [ i ] = ( UINT32 ) LONG ( players [ i ] . availabilities ) ;
2014-08-04 03:49:33 +00:00
}
2014-03-15 16:59:03 +00:00
memcpy ( netbuffer - > u . servercfg . server_context , server_context , 8 ) ;
2014-08-04 03:49:33 +00:00
op = p = netbuffer - > u . servercfg . varlengthinputs ;
CV_SavePlayerNames ( & p ) ;
2014-03-15 16:59:03 +00:00
CV_SaveNetVars ( & p ) ;
{
const size_t len = sizeof ( serverconfig_pak ) + ( size_t ) ( p - op ) ;
# ifdef DEBUGFILE
if ( debugfile )
{
fprintf ( debugfile , " ServerConfig Packet about to be sent, size of packet:%s to node:%d \n " ,
sizeu1 ( len ) , node ) ;
}
# endif
waspacketsent = HSendPacket ( node , true , 0 , len ) ;
}
# ifdef DEBUGFILE
if ( debugfile )
{
if ( waspacketsent )
{
fprintf ( debugfile , " ServerConfig Packet was sent \n " ) ;
}
else
{
fprintf ( debugfile , " ServerConfig Packet could not be sent right now \n " ) ;
}
}
# endif
return waspacketsent ;
}
# ifdef JOININGAME
# define SAVEGAMESIZE (768*1024)
static void SV_SendSaveGame ( INT32 node )
{
size_t length , compressedlen ;
UINT8 * savebuffer ;
UINT8 * compressedsave ;
UINT8 * buffertosend ;
// first save it in a malloced buffer
savebuffer = ( UINT8 * ) malloc ( SAVEGAMESIZE ) ;
if ( ! savebuffer )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No more free memory for savegame \n " ) ) ;
return ;
}
// Leave room for the uncompressed length.
2014-03-17 18:22:49 +00:00
save_p = savebuffer + sizeof ( UINT32 ) ;
2014-03-15 16:59:03 +00:00
P_SaveNetGame ( ) ;
length = save_p - savebuffer ;
if ( length > SAVEGAMESIZE )
{
free ( savebuffer ) ;
save_p = NULL ;
I_Error ( " Savegame buffer overrun " ) ;
}
// Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile.
compressedsave = malloc ( length - 1 ) ;
if ( ! compressedsave )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No more free memory for savegame \n " ) ) ;
return ;
}
// Attempt to compress it.
2014-03-17 18:22:49 +00:00
if ( ( compressedlen = lzf_compress ( savebuffer + sizeof ( UINT32 ) , length - sizeof ( UINT32 ) , compressedsave + sizeof ( UINT32 ) , length - sizeof ( UINT32 ) - 1 ) ) )
2014-03-15 16:59:03 +00:00
{
// Compressing succeeded; send compressed data
free ( savebuffer ) ;
// State that we're compressed.
buffertosend = compressedsave ;
2014-03-17 18:22:49 +00:00
WRITEUINT32 ( compressedsave , length - sizeof ( UINT32 ) ) ;
length = compressedlen + sizeof ( UINT32 ) ;
2014-03-15 16:59:03 +00:00
}
else
{
// Compression failed to make it smaller; send original
free ( compressedsave ) ;
// State that we're not compressed
buffertosend = savebuffer ;
WRITEUINT32 ( savebuffer , 0 ) ;
}
2016-12-31 18:26:33 +00:00
SV_SendRam ( node , buffertosend , length , SF_RAM , 0 ) ;
2014-03-15 16:59:03 +00:00
save_p = NULL ;
2017-01-13 19:53:52 +00:00
// Remember when we started sending the savegame so we can handle timeouts
sendingsavegame [ node ] = true ;
freezetimeout [ node ] = I_GetTime ( ) + jointimeout + length / 1024 ; // 1 extra tic for each kilobyte
2014-03-15 16:59:03 +00:00
}
# ifdef DUMPCONSISTENCY
# define TMPSAVENAME "badmath.sav"
static consvar_t cv_dumpconsistency = { " dumpconsistency " , " Off " , CV_NETVAR , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
static void SV_SavedGame ( void )
{
size_t length ;
UINT8 * savebuffer ;
2017-09-29 18:46:23 +00:00
char tmpsave [ 256 ] ;
2014-03-15 16:59:03 +00:00
if ( ! cv_dumpconsistency . value )
return ;
sprintf ( tmpsave , " %s " PATHSEP TMPSAVENAME , srb2home ) ;
// first save it in a malloced buffer
save_p = savebuffer = ( UINT8 * ) malloc ( SAVEGAMESIZE ) ;
if ( ! save_p )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No more free memory for savegame \n " ) ) ;
return ;
}
P_SaveNetGame ( ) ;
length = save_p - savebuffer ;
if ( length > SAVEGAMESIZE )
{
free ( savebuffer ) ;
save_p = NULL ;
I_Error ( " Savegame buffer overrun " ) ;
}
// then save it!
if ( ! FIL_WriteFile ( tmpsave , savebuffer , length ) )
CONS_Printf ( M_GetText ( " Didn't save %s for netgame " ) , tmpsave ) ;
free ( savebuffer ) ;
save_p = NULL ;
}
# undef TMPSAVENAME
# endif
# define TMPSAVENAME "$$$.sav"
static void CL_LoadReceivedSavegame ( void )
{
UINT8 * savebuffer = NULL ;
size_t length , decompressedlen ;
2017-09-29 18:46:23 +00:00
char tmpsave [ 256 ] ;
2014-03-15 16:59:03 +00:00
sprintf ( tmpsave , " %s " PATHSEP TMPSAVENAME , srb2home ) ;
length = FIL_ReadFile ( tmpsave , & savebuffer ) ;
CONS_Printf ( M_GetText ( " Loading savegame length %s \n " ) , sizeu1 ( length ) ) ;
if ( ! length )
{
I_Error ( " Can't read savegame sent " ) ;
return ;
}
save_p = savebuffer ;
// Decompress saved game if necessary.
decompressedlen = READUINT32 ( save_p ) ;
if ( decompressedlen > 0 )
{
UINT8 * decompressedbuffer = Z_Malloc ( decompressedlen , PU_STATIC , NULL ) ;
2014-03-17 18:22:49 +00:00
lzf_decompress ( save_p , length - sizeof ( UINT32 ) , decompressedbuffer , decompressedlen ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( savebuffer ) ;
save_p = savebuffer = decompressedbuffer ;
}
paused = false ;
demoplayback = false ;
2020-01-12 19:08:41 +00:00
titlemapinaction = TITLEMAP_OFF ;
2014-03-15 16:59:03 +00:00
titledemo = false ;
automapactive = false ;
// load a base level
if ( P_LoadNetGame ( ) )
{
const INT32 actnum = mapheaderinfo [ gamemap - 1 ] - > actnum ;
CONS_Printf ( M_GetText ( " Map is now \" %s " ) , G_BuildMapName ( gamemap ) ) ;
if ( strcmp ( mapheaderinfo [ gamemap - 1 ] - > lvlttl , " " ) )
{
CONS_Printf ( " : %s " , mapheaderinfo [ gamemap - 1 ] - > lvlttl ) ;
if ( ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_NOZONE ) )
2019-11-25 16:04:03 +00:00
CONS_Printf ( M_GetText ( " Zone " ) ) ;
2014-03-15 16:59:03 +00:00
if ( actnum > 0 )
CONS_Printf ( " %2d " , actnum ) ;
}
CONS_Printf ( " \" \n " ) ;
}
else
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Can't load the level! \n " ) ) ;
Z_Free ( savebuffer ) ;
save_p = NULL ;
if ( unlink ( tmpsave ) = = - 1 )
2014-08-05 23:59:40 +00:00
CONS_Alert ( CONS_ERROR , M_GetText ( " Can't delete %s \n " ) , tmpsave ) ;
2014-03-15 16:59:03 +00:00
return ;
}
// done
Z_Free ( savebuffer ) ;
save_p = NULL ;
if ( unlink ( tmpsave ) = = - 1 )
2014-08-05 23:59:40 +00:00
CONS_Alert ( CONS_ERROR , M_GetText ( " Can't delete %s \n " ) , tmpsave ) ;
2014-03-15 16:59:03 +00:00
consistancy [ gametic % BACKUPTICS ] = Consistancy ( ) ;
CON_ToggleOff ( ) ;
}
# endif
# ifndef NONET
static void SendAskInfo ( INT32 node , boolean viams )
{
const tic_t asktime = I_GetTime ( ) ;
netbuffer - > packettype = PT_ASKINFO ;
netbuffer - > u . askinfo . version = VERSION ;
netbuffer - > u . askinfo . time = ( tic_t ) LONG ( asktime ) ;
// Even if this never arrives due to the host being firewalled, we've
// now allowed traffic from the host to us in, so once the MS relays
// our address to the host, it'll be able to speak to us.
HSendPacket ( node , false , 0 , sizeof ( askinfo_pak ) ) ;
// Also speak to the MS.
if ( viams & & node ! = 0 & & node ! = BROADCASTADDR )
SendAskInfoViaMS ( node , asktime ) ;
}
serverelem_t serverlist [ MAXSERVERLIST ] ;
UINT32 serverlistcount = 0 ;
2018-11-21 20:45:03 +00:00
# define FORCECLOSE 0x8000
2014-03-15 16:59:03 +00:00
static void SL_ClearServerList ( INT32 connectedserver )
{
UINT32 i ;
for ( i = 0 ; i < serverlistcount ; i + + )
if ( connectedserver ! = serverlist [ i ] . node )
{
2018-11-21 20:45:03 +00:00
Net_CloseConnection ( serverlist [ i ] . node | FORCECLOSE ) ;
2014-03-15 16:59:03 +00:00
serverlist [ i ] . node = 0 ;
}
serverlistcount = 0 ;
}
static UINT32 SL_SearchServer ( INT32 node )
{
UINT32 i ;
for ( i = 0 ; i < serverlistcount ; i + + )
if ( serverlist [ i ] . node = = node )
return i ;
return UINT32_MAX ;
}
static void SL_InsertServer ( serverinfo_pak * info , SINT8 node )
{
UINT32 i ;
// search if not already on it
i = SL_SearchServer ( node ) ;
if ( i = = UINT32_MAX )
{
// not found add it
if ( serverlistcount > = MAXSERVERLIST )
return ; // list full
2019-12-26 04:44:25 +00:00
if ( info - > _255 ! = 255 )
return ; /* old packet format */
if ( info - > packetversion ! = PACKETVERSION )
return ; /* old new packet format */
2014-03-15 16:59:03 +00:00
if ( info - > version ! = VERSION )
return ; // Not same version.
if ( info - > subversion ! = SUBVERSION )
return ; // Close, but no cigar.
2019-12-26 04:44:25 +00:00
if ( strcmp ( info - > application , SRB2APPLICATION ) )
return ; /* that's a different mod */
2014-03-15 16:59:03 +00:00
i = serverlistcount + + ;
}
serverlist [ i ] . info = * info ;
serverlist [ i ] . node = node ;
2014-03-17 12:13:16 +00:00
// resort server list
M_SortServerList ( ) ;
2014-03-15 16:59:03 +00:00
}
void CL_UpdateServerList ( boolean internetsearch , INT32 room )
{
SL_ClearServerList ( 0 ) ;
if ( ! netgame & & I_NetOpenSocket )
{
MSCloseUDPSocket ( ) ; // Tidy up before wiping the slate.
if ( I_NetOpenSocket ( ) )
{
netgame = true ;
multiplayer = true ;
}
}
// search for local servers
if ( netgame )
SendAskInfo ( BROADCASTADDR , false ) ;
if ( internetsearch )
{
const msg_server_t * server_list ;
INT32 i = - 1 ;
server_list = GetShortServersList ( room ) ;
if ( server_list )
{
char version [ 8 ] = " " ;
# if VERSION > 0 || SUBVERSION > 0
snprintf ( version , sizeof ( version ) , " %d.%d.%d " , VERSION / 100 , VERSION % 100 , SUBVERSION ) ;
# else
strcpy ( version , GetRevisionString ( ) ) ;
# endif
version [ sizeof ( version ) - 1 ] = ' \0 ' ;
for ( i = 0 ; server_list [ i ] . header . buffer [ 0 ] ; i + + )
{
// Make sure MS version matches our own, to
// thwart nefarious servers who lie to the MS.
2018-11-21 20:45:03 +00:00
if ( strcmp ( version , server_list [ i ] . version ) = = 0 )
2014-03-15 16:59:03 +00:00
{
INT32 node = I_NetMakeNodewPort ( server_list [ i ] . ip , server_list [ i ] . port ) ;
if ( node = = - 1 )
break ; // no more node free
SendAskInfo ( node , true ) ;
2018-11-21 20:45:03 +00:00
// Force close the connection so that servers can't eat
// up nodes forever if we never get a reply back from them
// (usually when they've not forwarded their ports).
//
// Don't worry, we'll get in contact with the working
// servers again when they send SERVERINFO to us later!
//
// (Note: as a side effect this probably means every
// server in the list will probably be using the same node (e.g. node 1),
// not that it matters which nodes they use when
// the connections are closed afterwards anyway)
// -- Monster Iestyn 12/11/18
Net_CloseConnection ( node | FORCECLOSE ) ;
2014-03-15 16:59:03 +00:00
}
}
}
//no server list?(-1) or no servers?(0)
if ( ! i )
{
; /// TODO: display error or warning?
}
}
}
# endif // ifndef NONET
2016-12-31 18:26:33 +00:00
/** Called by CL_ServerConnectionTicker
*
* \ param viams ? ? ?
* \ param asksent ? ? ?
* \ return False if the connection was aborted
* \ sa CL_ServerConnectionTicker
* \ sa CL_ConnectToServer
*
*/
static boolean CL_ServerConnectionSearchTicker ( boolean viams , tic_t * asksent )
{
2017-01-01 22:52:27 +00:00
# ifndef NONET
2016-12-31 18:26:33 +00:00
INT32 i ;
// serverlist is updated by GetPacket function
if ( serverlistcount > 0 )
{
// this can be a responce to our broadcast request
if ( servernode = = - 1 | | servernode > = MAXNETNODES )
{
i = 0 ;
servernode = serverlist [ i ] . node ;
CONS_Printf ( M_GetText ( " Found, " ) ) ;
}
else
{
i = SL_SearchServer ( servernode ) ;
if ( i < 0 )
return true ;
}
// Quit here rather than downloading files and being refused later.
if ( serverlist [ i ] . info . numberofplayer > = serverlist [ i ] . info . maxplayer )
{
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( va ( M_GetText ( " Maximum players reached: %d \n \n Press ESC \n " ) , serverlist [ i ] . info . maxplayer ) , NULL , MM_NOTHING ) ;
return false ;
}
2017-01-13 19:53:52 +00:00
if ( client )
2016-12-31 18:26:33 +00:00
{
D_ParseFileneeded ( serverlist [ i ] . info . fileneedednum ,
serverlist [ i ] . info . fileneeded ) ;
CONS_Printf ( M_GetText ( " Checking files... \n " ) ) ;
i = CL_CheckFiles ( ) ;
2017-05-25 15:06:39 +00:00
if ( i = = 3 ) // too many files
{
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( M_GetText (
" You have too many WAD files loaded \n "
" to add ones the server is using. \n "
" Please restart SRB2 before connecting. \n \n "
" Press ESC \n "
) , NULL , MM_NOTHING ) ;
return false ;
}
else if ( i = = 2 ) // cannot join for some reason
2016-12-31 18:26:33 +00:00
{
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( M_GetText (
" You have WAD files loaded or have \n "
" modified the game in some way, and \n "
" your file list does not match \n "
" the server's file list. \n "
" Please restart SRB2 before connecting. \n \n "
" Press ESC \n "
) , NULL , MM_NOTHING ) ;
return false ;
}
else if ( i = = 1 )
cl_mode = CL_ASKJOIN ;
else
{
// must download something
// can we, though?
if ( ! CL_CheckDownloadable ( ) ) // nope!
{
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( M_GetText (
" You cannot connect to this server \n "
" because you cannot download the files \n "
" that you are missing from the server. \n \n "
" See the console or log file for \n "
" more details. \n \n "
" Press ESC \n "
) , NULL , MM_NOTHING ) ;
return false ;
}
// no problem if can't send packet, we will retry later
if ( CL_SendRequestFile ( ) )
cl_mode = CL_DOWNLOADFILES ;
}
}
else
cl_mode = CL_ASKJOIN ; // files need not be checked for the server.
return true ;
}
// Ask the info to the server (askinfo packet)
if ( * asksent + NEWTICRATE < I_GetTime ( ) )
{
SendAskInfo ( servernode , viams ) ;
* asksent = I_GetTime ( ) ;
}
# else
( void ) viams ;
2017-01-01 22:52:27 +00:00
( void ) asksent ;
2016-12-31 18:26:33 +00:00
// No netgames, so we skip this state.
cl_mode = CL_ASKJOIN ;
# endif // ifndef NONET/else
return true ;
}
/** Called by CL_ConnectToServer
*
* \ param viams ? ? ?
* \ param tmpsave The name of the gamestate file ? ? ?
* \ param oldtic Used for knowing when to poll events and redraw
* \ param asksent ? ? ?
* \ return False if the connection was aborted
* \ sa CL_ServerConnectionSearchTicker
* \ sa CL_ConnectToServer
*
*/
static boolean CL_ServerConnectionTicker ( boolean viams , const char * tmpsave , tic_t * oldtic , tic_t * asksent )
{
boolean waitmore ;
INT32 i ;
2017-01-01 22:52:27 +00:00
# ifdef NONET
( void ) tmpsave ;
# endif
2016-12-31 18:26:33 +00:00
switch ( cl_mode )
{
case CL_SEARCHING :
if ( ! CL_ServerConnectionSearchTicker ( viams , asksent ) )
return false ;
break ;
case CL_DOWNLOADFILES :
waitmore = false ;
for ( i = 0 ; i < fileneedednum ; i + + )
if ( fileneeded [ i ] . status = = FS_DOWNLOADING
| | fileneeded [ i ] . status = = FS_REQUESTED )
{
waitmore = true ;
break ;
}
if ( waitmore )
break ; // exit the case
cl_mode = CL_ASKJOIN ; // don't break case continue to cljoin request now
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2016-12-31 18:26:33 +00:00
case CL_ASKJOIN :
CL_LoadServerFiles ( ) ;
# ifdef JOININGAME
// prepare structures to save the file
// WARNING: this can be useless in case of server not in GS_LEVEL
// but since the network layer doesn't provide ordered packets...
CL_PrepareDownloadSaveGame ( tmpsave ) ;
# endif
if ( CL_SendJoin ( ) )
cl_mode = CL_WAITJOINRESPONSE ;
break ;
# ifdef JOININGAME
case CL_DOWNLOADSAVEGAME :
// At this state, the first (and only) needed file is the gamestate
if ( fileneeded [ 0 ] . status = = FS_FOUND )
{
// Gamestate is now handled within CL_LoadReceivedSavegame()
CL_LoadReceivedSavegame ( ) ;
cl_mode = CL_CONNECTED ;
} // don't break case continue to CL_CONNECTED
else
break ;
# endif
case CL_WAITJOINRESPONSE :
case CL_CONNECTED :
default :
break ;
// Connection closed by cancel, timeout or refusal.
case CL_ABORTED :
cl_mode = CL_SEARCHING ;
return false ;
}
GetPackets ( ) ;
Net_AckTicker ( ) ;
// Call it only once by tic
if ( * oldtic ! = I_GetTime ( ) )
{
INT32 key ;
I_OsPolling ( ) ;
key = I_GetKey ( ) ;
2018-12-03 17:51:18 +00:00
if ( key = = KEY_ESCAPE | | key = = KEY_JOY1 + 1 )
2016-12-31 18:26:33 +00:00
{
CONS_Printf ( M_GetText ( " Network game synchronization aborted. \n " ) ) ;
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
return false ;
}
// why are these here? this is for servers, we're a client
//if (key == 's' && server)
// doomcom->numnodes = (INT16)pnumnodes;
//SV_FileSendTicker();
* oldtic = I_GetTime ( ) ;
# ifdef CLIENT_LOADINGSCREEN
2017-01-13 19:53:52 +00:00
if ( client & & cl_mode ! = CL_CONNECTED & & cl_mode ! = CL_ABORTED )
2016-12-31 18:26:33 +00:00
{
2018-11-25 20:08:12 +00:00
F_MenuPresTicker ( true ) ; // title sky
2016-12-31 18:26:33 +00:00
F_TitleScreenTicker ( true ) ;
F_TitleScreenDrawer ( ) ;
CL_DrawConnectionStatus ( ) ;
I_UpdateNoVsync ( ) ; // page flip or blit buffer
if ( moviemode )
M_SaveFrame ( ) ;
}
# else
CON_Drawer ( ) ;
I_UpdateNoVsync ( ) ;
# endif
}
else
I_Sleep ( ) ;
return true ;
}
/** Use adaptive send using net_bandwidth and stat.sendbytes
*
* \ param viams ? ? ?
* \ todo Better description . . .
*
*/
2014-03-15 16:59:03 +00:00
static void CL_ConnectToServer ( boolean viams )
{
INT32 pnumnodes , nodewaited = doomcom - > numnodes , i ;
tic_t oldtic ;
# ifndef NONET
tic_t asksent ;
# endif
# ifdef JOININGAME
2017-09-29 18:46:23 +00:00
char tmpsave [ 256 ] ;
2014-03-15 16:59:03 +00:00
sprintf ( tmpsave , " %s " PATHSEP TMPSAVENAME , srb2home ) ;
# endif
2016-12-31 18:26:33 +00:00
cl_mode = CL_SEARCHING ;
2014-03-15 16:59:03 +00:00
# ifdef CLIENT_LOADINGSCREEN
2017-01-13 19:53:52 +00:00
lastfilenum = - 1 ;
2014-03-15 16:59:03 +00:00
# endif
# ifdef JOININGAME
2016-12-31 18:26:33 +00:00
// Don't get a corrupt savegame error because tmpsave already exists
2014-03-15 16:59:03 +00:00
if ( FIL_FileExists ( tmpsave ) & & unlink ( tmpsave ) = = - 1 )
I_Error ( " Can't delete %s \n " , tmpsave ) ;
# endif
if ( netgame )
{
if ( servernode < 0 | | servernode > = MAXNETNODES )
CONS_Printf ( M_GetText ( " Searching for a server... \n " ) ) ;
else
CONS_Printf ( M_GetText ( " Contacting the server... \n " ) ) ;
}
if ( gamestate = = GS_INTERMISSION )
Y_EndIntermission ( ) ; // clean up intermission graphics etc
DEBFILE ( va ( " waiting %d nodes \n " , doomcom - > numnodes ) ) ;
G_SetGamestate ( GS_WAITINGPLAYERS ) ;
wipegamestate = GS_WAITINGPLAYERS ;
2017-11-11 05:34:37 +00:00
ClearAdminPlayers ( ) ;
2014-03-15 16:59:03 +00:00
pnumnodes = 1 ;
oldtic = I_GetTime ( ) - 1 ;
# ifndef NONET
2016-12-31 18:26:33 +00:00
asksent = ( tic_t ) - TICRATE ;
2014-03-15 16:59:03 +00:00
i = SL_SearchServer ( servernode ) ;
if ( i ! = - 1 )
{
2019-12-31 06:18:55 +00:00
char * gametypestr = serverlist [ i ] . info . gametypename ;
2014-03-15 16:59:03 +00:00
CONS_Printf ( M_GetText ( " Connecting to: %s \n " ) , serverlist [ i ] . info . servername ) ;
2019-12-31 06:18:55 +00:00
gametypestr [ sizeof serverlist [ i ] . info . gametypename - 1 ] = ' \0 ' ;
CONS_Printf ( M_GetText ( " Gametype: %s \n " ) , gametypestr ) ;
2014-03-25 02:17:59 +00:00
CONS_Printf ( M_GetText ( " Version: %d.%d.%u \n " ) , serverlist [ i ] . info . version / 100 ,
2014-03-15 16:59:03 +00:00
serverlist [ i ] . info . version % 100 , serverlist [ i ] . info . subversion ) ;
}
SL_ClearServerList ( servernode ) ;
# endif
do
{
2016-12-31 18:26:33 +00:00
// If the connection was aborted for some reason, leave
2014-03-15 16:59:03 +00:00
# ifndef NONET
2016-12-31 18:26:33 +00:00
if ( ! CL_ServerConnectionTicker ( viams , tmpsave , & oldtic , & asksent ) )
2014-03-15 16:59:03 +00:00
# else
2017-01-01 22:52:27 +00:00
if ( ! CL_ServerConnectionTicker ( viams , ( char * ) NULL , & oldtic , ( tic_t * ) NULL ) )
2014-03-15 16:59:03 +00:00
# endif
2016-12-31 18:26:33 +00:00
return ;
2014-03-15 16:59:03 +00:00
if ( server )
{
pnumnodes = 0 ;
for ( i = 0 ; i < MAXNETNODES ; i + + )
2016-12-31 18:26:33 +00:00
if ( nodeingame [ i ] )
pnumnodes + + ;
2014-03-15 16:59:03 +00:00
}
}
2017-01-13 19:53:52 +00:00
while ( ! ( cl_mode = = CL_CONNECTED & & ( client | | ( server & & nodewaited < = pnumnodes ) ) ) ) ;
2014-03-15 16:59:03 +00:00
DEBFILE ( va ( " Synchronisation Finished \n " ) ) ;
displayplayer = consoleplayer ;
}
# ifndef NONET
typedef struct banreason_s
{
char * reason ;
struct banreason_s * prev ; //-1
struct banreason_s * next ; //+1
} banreason_t ;
static banreason_t * reasontail = NULL ; //last entry, use prev
static banreason_t * reasonhead = NULL ; //1st entry, use next
static void Command_ShowBan ( void ) //Print out ban list
{
size_t i ;
const char * address , * mask ;
banreason_t * reasonlist = reasonhead ;
if ( I_GetBanAddress )
CONS_Printf ( M_GetText ( " Ban List: \n " ) ) ;
else
return ;
for ( i = 0 ; ( address = I_GetBanAddress ( i ) ) ! = NULL ; i + + )
{
if ( ! I_GetBanMask | | ( mask = I_GetBanMask ( i ) ) = = NULL )
CONS_Printf ( " %s: %s " , sizeu1 ( i + 1 ) , address ) ;
else
CONS_Printf ( " %s: %s/%s " , sizeu1 ( i + 1 ) , address , mask ) ;
if ( reasonlist & & reasonlist - > reason )
CONS_Printf ( " (%s) \n " , reasonlist - > reason ) ;
else
CONS_Printf ( " \n " ) ;
if ( reasonlist ) reasonlist = reasonlist - > next ;
}
if ( i = = 0 & & ! address )
CONS_Printf ( M_GetText ( " (empty) \n " ) ) ;
}
void D_SaveBan ( void )
{
FILE * f ;
size_t i ;
banreason_t * reasonlist = reasonhead ;
const char * address , * mask ;
if ( ! reasonhead )
return ;
f = fopen ( va ( " %s " PATHSEP " %s " , srb2home , " ban.txt " ) , " w " ) ;
if ( ! f )
{
CONS_Alert ( CONS_WARNING , M_GetText ( " Could not save ban list into ban.txt \n " ) ) ;
return ;
}
for ( i = 0 ; ( address = I_GetBanAddress ( i ) ) ! = NULL ; i + + )
{
if ( ! I_GetBanMask | | ( mask = I_GetBanMask ( i ) ) = = NULL )
fprintf ( f , " %s 0 " , address ) ;
else
fprintf ( f , " %s %s " , address , mask ) ;
if ( reasonlist & & reasonlist - > reason )
fprintf ( f , " %s \n " , reasonlist - > reason ) ;
else
fprintf ( f , " %s \n " , " NA " ) ;
if ( reasonlist ) reasonlist = reasonlist - > next ;
}
fclose ( f ) ;
}
static void Ban_Add ( const char * reason )
{
banreason_t * reasonlist = malloc ( sizeof ( * reasonlist ) ) ;
if ( ! reasonlist )
return ;
if ( ! reason )
reason = " NA " ;
reasonlist - > next = NULL ;
reasonlist - > reason = Z_StrDup ( reason ) ;
if ( ( reasonlist - > prev = reasontail ) = = NULL )
reasonhead = reasonlist ;
else
reasontail - > next = reasonlist ;
reasontail = reasonlist ;
}
static void Command_ClearBans ( void )
{
banreason_t * temp ;
if ( ! I_ClearBans )
return ;
I_ClearBans ( ) ;
2019-08-22 22:18:14 +00:00
D_SaveBan ( ) ;
2014-03-15 16:59:03 +00:00
reasontail = NULL ;
while ( reasonhead )
{
temp = reasonhead - > next ;
Z_Free ( reasonhead - > reason ) ;
free ( reasonhead ) ;
reasonhead = temp ;
}
}
static void Ban_Load_File ( boolean warning )
{
FILE * f ;
size_t i ;
const char * address , * mask ;
char buffer [ MAX_WADPATH ] ;
f = fopen ( va ( " %s " PATHSEP " %s " , srb2home , " ban.txt " ) , " r " ) ;
if ( ! f )
{
if ( warning )
CONS_Alert ( CONS_WARNING , M_GetText ( " Could not open ban.txt for ban list \n " ) ) ;
return ;
}
if ( I_ClearBans )
Command_ClearBans ( ) ;
else
{
fclose ( f ) ;
return ;
}
for ( i = 0 ; fgets ( buffer , ( int ) sizeof ( buffer ) , f ) ; i + + )
{
address = strtok ( buffer , " \t \r \n " ) ;
mask = strtok ( NULL , " \t \r \n " ) ;
I_SetBanAddress ( address , mask ) ;
Ban_Add ( strtok ( NULL , " \r \n " ) ) ;
}
fclose ( f ) ;
}
static void Command_ReloadBan ( void ) //recheck ban.txt
{
Ban_Load_File ( true ) ;
}
static void Command_connect ( void )
{
// Assume we connect directly.
boolean viams = false ;
2018-01-12 01:35:39 +00:00
if ( COM_Argc ( ) < 2 | | * COM_Argv ( 1 ) = = 0 )
2014-03-15 16:59:03 +00:00
{
CONS_Printf ( M_GetText (
" Connect <serveraddress> (port): connect to a server \n "
" Connect ANY: connect to the first lan server found \n "
2017-08-18 17:14:03 +00:00
//"Connect SELF: connect to your own server.\n"
) ) ;
2014-03-15 16:59:03 +00:00
return ;
}
2014-08-04 03:49:33 +00:00
if ( Playing ( ) | | titledemo )
2014-03-15 16:59:03 +00:00
{
CONS_Printf ( M_GetText ( " You cannot connect while in a game. End this game first. \n " ) ) ;
return ;
}
// modified game check: no longer handled
// we don't request a restart unless the filelist differs
server = false ;
2017-08-18 17:14:03 +00:00
/*
2014-03-15 16:59:03 +00:00
if ( ! stricmp ( COM_Argv ( 1 ) , " self " ) )
{
servernode = 0 ;
server = true ;
/// \bug should be but...
//SV_SpawnServer();
}
else
2017-08-18 17:14:03 +00:00
*/
2014-03-15 16:59:03 +00:00
{
// used in menu to connect to a server in the list
if ( netgame & & ! stricmp ( COM_Argv ( 1 ) , " node " ) )
{
servernode = ( SINT8 ) atoi ( COM_Argv ( 2 ) ) ;
// Use MS to traverse NAT firewalls.
viams = true ;
}
else if ( netgame )
{
CONS_Printf ( M_GetText ( " You cannot connect while in a game. End this game first. \n " ) ) ;
return ;
}
else if ( I_NetOpenSocket )
{
MSCloseUDPSocket ( ) ; // Tidy up before wiping the slate.
I_NetOpenSocket ( ) ;
netgame = true ;
multiplayer = true ;
if ( ! stricmp ( COM_Argv ( 1 ) , " any " ) )
servernode = BROADCASTADDR ;
else if ( I_NetMakeNodewPort )
2017-08-18 17:14:03 +00:00
{
if ( COM_Argc ( ) > = 3 ) // address AND port
servernode = I_NetMakeNodewPort ( COM_Argv ( 1 ) , COM_Argv ( 2 ) ) ;
else // address only, or address:port
servernode = I_NetMakeNode ( COM_Argv ( 1 ) ) ;
}
2014-03-15 16:59:03 +00:00
else
{
CONS_Alert ( CONS_ERROR , M_GetText ( " There is no server identification with this network driver \n " ) ) ;
D_CloseConnection ( ) ;
return ;
}
}
else
CONS_Alert ( CONS_ERROR , M_GetText ( " There is no network driver \n " ) ) ;
}
splitscreen = false ;
SplitScreen_OnChange ( ) ;
botingame = false ;
botskin = 0 ;
CL_ConnectToServer ( viams ) ;
}
# endif
static void ResetNode ( INT32 node ) ;
//
// CL_ClearPlayer
//
// Clears the player data so that a future client can use this slot
//
void CL_ClearPlayer ( INT32 playernum )
{
if ( players [ playernum ] . mo )
P_RemoveMobj ( players [ playernum ] . mo ) ;
memset ( & players [ playernum ] , 0 , sizeof ( player_t ) ) ;
}
//
// CL_RemovePlayer
//
// Removes a player from the current game
//
2016-10-21 02:30:11 +00:00
static void CL_RemovePlayer ( INT32 playernum , INT32 reason )
2014-03-15 16:59:03 +00:00
{
// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
// kick commands to be issued for the same player.
if ( ! playeringame [ playernum ] )
return ;
if ( server & & ! demoplayback )
{
INT32 node = playernode [ playernum ] ;
playerpernode [ node ] - - ;
if ( playerpernode [ node ] < = 0 )
{
2014-08-27 03:56:30 +00:00
// If a resynch was in progress, well, it no longer needs to be.
SV_InitResynchVars ( playernode [ playernum ] ) ;
2014-03-15 16:59:03 +00:00
nodeingame [ playernode [ playernum ] ] = false ;
Net_CloseConnection ( playernode [ playernum ] ) ;
ResetNode ( node ) ;
}
}
2019-12-18 14:43:50 +00:00
if ( gametyperules & GTR_TEAMFLAGS )
2014-03-15 16:59:03 +00:00
P_PlayerFlagBurst ( & players [ playernum ] , false ) ; // Don't take the flag with you!
2018-06-03 21:41:54 +00:00
// If in a special stage, redistribute the player's spheres across
2014-03-15 16:59:03 +00:00
// the remaining players.
if ( G_IsSpecialStage ( gamemap ) )
{
2019-09-01 14:55:23 +00:00
INT32 i , count , sincrement , spheres , rincrement , rings ;
2014-03-15 16:59:03 +00:00
for ( i = 0 , count = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
count + + ;
}
count - - ;
2018-06-03 21:41:54 +00:00
spheres = players [ playernum ] . spheres ;
2019-09-01 14:55:23 +00:00
rings = players [ playernum ] . rings ;
sincrement = spheres / count ;
rincrement = rings / count ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] & & i ! = playernum )
{
2019-09-01 14:55:23 +00:00
if ( spheres < 2 * sincrement )
{
2018-06-03 21:41:54 +00:00
P_GivePlayerSpheres ( & players [ i ] , spheres ) ;
2019-09-01 14:55:23 +00:00
spheres = 0 ;
}
2014-03-15 16:59:03 +00:00
else
2019-09-01 14:55:23 +00:00
{
P_GivePlayerSpheres ( & players [ i ] , sincrement ) ;
spheres - = sincrement ;
}
2014-03-15 16:59:03 +00:00
2019-09-01 14:55:23 +00:00
if ( rings < 2 * rincrement )
{
P_GivePlayerRings ( & players [ i ] , rings ) ;
rings = 0 ;
}
else
{
P_GivePlayerRings ( & players [ i ] , rincrement ) ;
rings - = rincrement ;
}
2014-03-15 16:59:03 +00:00
}
}
}
2018-11-14 15:54:33 +00:00
2016-10-21 02:30:11 +00:00
# ifdef HAVE_BLUA
LUAh_PlayerQuit ( & players [ playernum ] , reason ) ; // Lua hook for player quitting
2019-03-01 22:31:32 +00:00
# else
( void ) reason ;
2016-10-21 02:30:11 +00:00
# endif
2014-03-15 16:59:03 +00:00
2019-12-23 23:22:57 +00:00
// don't look through someone's view who isn't there
if ( playernum = = displayplayer )
{
# ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch ( & players [ consoleplayer ] , & players [ displayplayer ] , true ) ;
# endif
displayplayer = consoleplayer ;
}
2014-03-15 16:59:03 +00:00
// Reset player data
CL_ClearPlayer ( playernum ) ;
// remove avatar of player
playeringame [ playernum ] = false ;
playernode [ playernum ] = UINT8_MAX ;
while ( ! playeringame [ doomcom - > numslots - 1 ] & & doomcom - > numslots > 1 )
doomcom - > numslots - - ;
// Reset the name
sprintf ( player_names [ playernum ] , " Player %d " , playernum + 1 ) ;
2017-11-11 05:34:37 +00:00
if ( IsPlayerAdmin ( playernum ) )
{
RemoveAdminPlayer ( playernum ) ; // don't stay admin after you're gone
}
2014-03-15 16:59:03 +00:00
# ifdef HAVE_BLUA
LUA_InvalidatePlayer ( & players [ playernum ] ) ;
# endif
if ( G_TagGametype ( ) ) //Check if you still have a game. Location flexible. =P
P_CheckSurvivors ( ) ;
2019-12-18 04:54:45 +00:00
else if ( gametyperules & GTR_RACE )
2014-03-15 16:59:03 +00:00
P_CheckRacers ( ) ;
}
void CL_Reset ( void )
{
2014-03-23 16:00:29 +00:00
if ( metalrecording )
2019-10-29 17:38:14 +00:00
G_StopMetalRecording ( false ) ;
2014-03-23 16:00:29 +00:00
if ( metalplayback )
G_StopMetalDemo ( ) ;
if ( demorecording )
2014-03-15 16:59:03 +00:00
G_CheckDemoStatus ( ) ;
// reset client/server code
DEBFILE ( va ( " \n -=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=- \n \n " ) ) ;
if ( servernode > 0 & & servernode < MAXNETNODES )
{
nodeingame [ ( UINT8 ) servernode ] = false ;
Net_CloseConnection ( servernode ) ;
}
D_CloseConnection ( ) ; // netgame = false
multiplayer = false ;
servernode = 0 ;
server = true ;
doomcom - > numnodes = 1 ;
doomcom - > numslots = 1 ;
SV_StopServer ( ) ;
SV_ResetServer ( ) ;
2014-08-05 23:59:40 +00:00
// make sure we don't leave any fileneeded gunk over from a failed join
fileneedednum = 0 ;
memset ( fileneeded , 0 , sizeof ( fileneeded ) ) ;
2014-03-15 16:59:03 +00:00
// D_StartTitle should get done now, but the calling function will handle it
}
# ifndef NONET
static void Command_GetPlayerNum ( void )
{
INT32 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] )
{
if ( serverplayer = = i )
CONS_Printf ( M_GetText ( " num:%2d node:%2d %s \n " ) , i , playernode [ i ] , player_names [ i ] ) ;
else
CONS_Printf ( M_GetText ( " \x82 num:%2d node:%2d %s \n " ) , i , playernode [ i ] , player_names [ i ] ) ;
}
}
SINT8 nametonum ( const char * name )
{
INT32 playernum , i ;
if ( ! strcmp ( name , " 0 " ) )
return 0 ;
playernum = ( SINT8 ) atoi ( name ) ;
if ( playernum < 0 | | playernum > = MAXPLAYERS )
return - 1 ;
if ( playernum )
{
if ( playeringame [ playernum ] )
return ( SINT8 ) playernum ;
else
return - 1 ;
}
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] & & ! stricmp ( player_names [ i ] , name ) )
return ( SINT8 ) i ;
CONS_Printf ( M_GetText ( " There is no player named \" %s \" \n " ) , name ) ;
return - 1 ;
}
/** Lists all players and their player numbers.
*
* \ sa Command_GetPlayerNum
*/
static void Command_Nodes ( void )
{
INT32 i ;
size_t maxlen = 0 ;
const char * address ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
const size_t plen = strlen ( player_names [ i ] ) ;
if ( playeringame [ i ] & & plen > maxlen )
maxlen = plen ;
}
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
{
CONS_Printf ( " %.2u: %*s " , i , ( int ) maxlen , player_names [ i ] ) ;
2019-11-15 16:50:40 +00:00
if ( playernode [ i ] ! = UINT8_MAX )
{
CONS_Printf ( " - node %.2d " , playernode [ i ] ) ;
if ( I_GetNodeAddress & & ( address = I_GetNodeAddress ( playernode [ i ] ) ) ! = NULL )
CONS_Printf ( " - %s " , address ) ;
}
2014-03-15 16:59:03 +00:00
2017-11-11 05:34:37 +00:00
if ( IsPlayerAdmin ( i ) )
2014-03-15 16:59:03 +00:00
CONS_Printf ( M_GetText ( " (verified admin) " ) ) ;
if ( players [ i ] . spectator )
CONS_Printf ( M_GetText ( " (spectator) " ) ) ;
CONS_Printf ( " \n " ) ;
}
}
}
static void Command_Ban ( void )
{
2017-07-07 21:40:00 +00:00
if ( COM_Argc ( ) < 2 )
2014-03-15 16:59:03 +00:00
{
CONS_Printf ( M_GetText ( " Ban <playername/playernum> <reason>: ban and kick a player \n " ) ) ;
return ;
}
2017-07-07 21:40:00 +00:00
if ( ! netgame ) // Don't kick Tails in splitscreen!
2014-03-15 16:59:03 +00:00
{
2017-07-07 21:40:00 +00:00
CONS_Printf ( M_GetText ( " This only works in a netgame. \n " ) ) ;
2014-03-15 16:59:03 +00:00
return ;
}
2017-11-11 05:34:37 +00:00
if ( server | | IsPlayerAdmin ( consoleplayer ) )
2014-03-15 16:59:03 +00:00
{
2017-09-29 18:46:23 +00:00
UINT8 buf [ 3 + MAX_REASONLENGTH ] ;
2014-03-15 16:59:03 +00:00
UINT8 * p = buf ;
const SINT8 pn = nametonum ( COM_Argv ( 1 ) ) ;
const INT32 node = playernode [ ( INT32 ) pn ] ;
if ( pn = = - 1 | | pn = = 0 )
return ;
2017-07-05 15:29:21 +00:00
WRITEUINT8 ( p , pn ) ;
2017-02-20 19:58:29 +00:00
if ( server & & I_Ban & & ! I_Ban ( node ) ) // only the server is allowed to do this right now
2014-03-15 16:59:03 +00:00
{
CONS_Alert ( CONS_WARNING , M_GetText ( " Too many bans! Geez, that's a lot of people you're excluding... \n " ) ) ;
WRITEUINT8 ( p , KICK_MSG_GO_AWAY ) ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
else
{
2017-02-20 19:58:29 +00:00
if ( server ) // only the server is allowed to do this right now
2019-02-18 06:03:39 +00:00
{
2017-02-20 19:58:29 +00:00
Ban_Add ( COM_Argv ( 2 ) ) ;
2019-02-18 06:03:39 +00:00
D_SaveBan ( ) ; // save the ban list
}
2014-03-15 16:59:03 +00:00
if ( COM_Argc ( ) = = 2 )
{
WRITEUINT8 ( p , KICK_MSG_BANNED ) ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
else
{
size_t i , j = COM_Argc ( ) ;
char message [ MAX_REASONLENGTH ] ;
//Steal from the motd code so you don't have to put the reason in quotes.
strlcpy ( message , COM_Argv ( 2 ) , sizeof message ) ;
for ( i = 3 ; i < j ; i + + )
{
strlcat ( message , " " , sizeof message ) ;
strlcat ( message , COM_Argv ( i ) , sizeof message ) ;
}
WRITEUINT8 ( p , KICK_MSG_CUSTOM_BAN ) ;
WRITESTRINGN ( p , message , MAX_REASONLENGTH ) ;
SendNetXCmd ( XD_KICK , & buf , p - buf ) ;
}
}
}
else
CONS_Printf ( M_GetText ( " Only the server or a remote admin can use this. \n " ) ) ;
}
2019-02-22 00:44:18 +00:00
static void Command_BanIP ( void )
{
if ( COM_Argc ( ) < 2 )
{
CONS_Printf ( M_GetText ( " banip <ip> <reason>: ban an ip address \n " ) ) ;
return ;
}
if ( server ) // Only the server can use this, otherwise does nothing.
{
const char * address = ( COM_Argv ( 1 ) ) ;
const char * reason ;
if ( COM_Argc ( ) = = 2 )
reason = NULL ;
else
reason = COM_Argv ( 2 ) ;
if ( I_SetBanAddress & & I_SetBanAddress ( address , NULL ) )
{
if ( reason )
CONS_Printf ( " Banned IP address %s for: %s \n " , address , reason ) ;
else
CONS_Printf ( " Banned IP address %s \n " , address ) ;
Ban_Add ( reason ) ;
D_SaveBan ( ) ;
}
else
{
return ;
}
}
}
2014-03-15 16:59:03 +00:00
static void Command_Kick ( void )
{
2017-07-07 21:40:00 +00:00
if ( COM_Argc ( ) < 2 )
2014-03-15 16:59:03 +00:00
{
CONS_Printf ( M_GetText ( " kick <playername/playernum> <reason>: kick a player \n " ) ) ;
return ;
}
2017-07-07 21:40:00 +00:00
if ( ! netgame ) // Don't kick Tails in splitscreen!
2014-03-15 16:59:03 +00:00
{
2017-07-07 21:40:00 +00:00
CONS_Printf ( M_GetText ( " This only works in a netgame. \n " ) ) ;
2014-03-15 16:59:03 +00:00
return ;
}
2017-11-11 05:34:37 +00:00
if ( server | | IsPlayerAdmin ( consoleplayer ) )
2014-03-15 16:59:03 +00:00
{
2017-09-29 18:46:23 +00:00
UINT8 buf [ 3 + MAX_REASONLENGTH ] ;
2017-07-07 21:40:00 +00:00
UINT8 * p = buf ;
2014-03-15 16:59:03 +00:00
const SINT8 pn = nametonum ( COM_Argv ( 1 ) ) ;
2017-07-05 15:20:23 +00:00
2014-03-15 16:59:03 +00:00
if ( pn = = - 1 | | pn = = 0 )
return ;
2017-07-05 15:20:23 +00:00
2018-12-05 17:39:02 +00:00
if ( server )
2017-01-13 19:53:52 +00:00
{
2018-12-05 17:39:02 +00:00
// Special case if we are trying to kick a player who is downloading the game state:
// trigger a timeout instead of kicking them, because a kick would only
// take effect after they have finished downloading
if ( sendingsavegame [ playernode [ pn ] ] )
{
Net_ConnectionTimeout ( playernode [ pn ] ) ;
return ;
}
2017-01-13 19:53:52 +00:00
}
2017-07-05 15:20:23 +00:00
WRITESINT8 ( p , pn ) ;
2014-03-15 16:59:03 +00:00
if ( COM_Argc ( ) = = 2 )
{
WRITEUINT8 ( p , KICK_MSG_GO_AWAY ) ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
else
{
size_t i , j = COM_Argc ( ) ;
char message [ MAX_REASONLENGTH ] ;
//Steal from the motd code so you don't have to put the reason in quotes.
strlcpy ( message , COM_Argv ( 2 ) , sizeof message ) ;
for ( i = 3 ; i < j ; i + + )
{
strlcat ( message , " " , sizeof message ) ;
strlcat ( message , COM_Argv ( i ) , sizeof message ) ;
}
WRITEUINT8 ( p , KICK_MSG_CUSTOM_KICK ) ;
WRITESTRINGN ( p , message , MAX_REASONLENGTH ) ;
SendNetXCmd ( XD_KICK , & buf , p - buf ) ;
}
}
else
CONS_Printf ( M_GetText ( " Only the server or a remote admin can use this. \n " ) ) ;
}
# endif
static void Got_KickCmd ( UINT8 * * p , INT32 playernum )
{
INT32 pnum , msg ;
2017-09-29 18:46:23 +00:00
char buf [ 3 + MAX_REASONLENGTH ] ;
2014-03-15 16:59:03 +00:00
char * reason = buf ;
2016-10-21 20:16:54 +00:00
kickreason_t kickreason = KR_KICK ;
2014-03-15 16:59:03 +00:00
pnum = READUINT8 ( * p ) ;
msg = READUINT8 ( * p ) ;
2017-11-11 05:34:37 +00:00
if ( pnum = = serverplayer & & IsPlayerAdmin ( playernum ) )
2014-03-15 16:59:03 +00:00
{
CONS_Printf ( M_GetText ( " Server is being shut down remotely. Goodbye! \n " ) ) ;
if ( server )
COM_BufAddText ( " quit \n " ) ;
return ;
}
// Is playernum authorized to make this kick?
2017-11-11 05:34:37 +00:00
if ( playernum ! = serverplayer & & ! IsPlayerAdmin ( playernum )
2014-03-15 16:59:03 +00:00
& & ! ( playerpernode [ playernode [ playernum ] ] = = 2
& & nodetoplayer2 [ playernode [ playernum ] ] = = pnum ) )
{
// We received a kick command from someone who isn't the
// server or admin, and who isn't in splitscreen removing
// player 2. Thus, it must be someone with a modified
// binary, trying to kick someone but without having
// authorization.
// We deal with this by changing the kick reason to
// "consistency failure" and kicking the offending user
// instead.
// Note: Splitscreen in netgames is broken because of
// this. Only the server has any idea of which players
// are using splitscreen on the same computer, so
// clients cannot always determine if a kick is
// legitimate.
CONS_Alert ( CONS_WARNING , M_GetText ( " Illegal kick command received from %s for player %d \n " ) , player_names [ playernum ] , pnum ) ;
// In debug, print a longer message with more details.
// TODO Callum: Should we translate this?
/*
CONS_Debug ( DBG_NETPLAY ,
" So, you must be asking, why is this an illegal kick? \n "
" Well, let's take a look at the facts, shall we? \n "
" \n "
" playernum (this is the guy who did it), he's %d. \n "
" pnum (the guy he's trying to kick) is %d. \n "
" playernum's node is %d. \n "
" That node has %d players. \n "
" Player 2 on that node is %d. \n "
" pnum's node is %d. \n "
" That node has %d players. \n "
" Player 2 on that node is %d. \n "
" \n "
" If you think this is a bug, please report it, including all of the details above. \n " ,
playernum , pnum ,
playernode [ playernum ] , playerpernode [ playernode [ playernum ] ] ,
nodetoplayer2 [ playernode [ playernum ] ] ,
playernode [ pnum ] , playerpernode [ playernode [ pnum ] ] ,
nodetoplayer2 [ playernode [ pnum ] ] ) ;
*/
pnum = playernum ;
msg = KICK_MSG_CON_FAIL ;
}
2018-12-22 03:28:28 +00:00
//CONS_Printf("\x82%s ", player_names[pnum]);
2014-03-15 16:59:03 +00:00
// If a verified admin banned someone, the server needs to know about it.
// If the playernum isn't zero (the server) then the server needs to record the ban.
2017-02-10 20:31:58 +00:00
if ( server & & playernum & & ( msg = = KICK_MSG_BANNED | | msg = = KICK_MSG_CUSTOM_BAN ) )
2014-03-15 16:59:03 +00:00
{
if ( I_Ban & & ! I_Ban ( playernode [ ( INT32 ) pnum ] ) )
CONS_Alert ( CONS_WARNING , M_GetText ( " Too many bans! Geez, that's a lot of people you're excluding... \n " ) ) ;
2017-02-20 21:36:05 +00:00
# ifndef NONET
2017-02-20 19:58:29 +00:00
else
Ban_Add ( reason ) ;
2017-02-20 21:36:05 +00:00
# endif
2014-03-15 16:59:03 +00:00
}
switch ( msg )
{
case KICK_MSG_GO_AWAY :
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s has been kicked (Go away) " , player_names [ pnum ] ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_KICK ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_PING_HIGH :
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s left the game (Broke ping limit) " , player_names [ pnum ] ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_PINGLIMIT ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_CON_FAIL :
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s left the game (Synch Failure) " , player_names [ pnum ] ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_SYNCH ;
2014-03-15 16:59:03 +00:00
if ( M_CheckParm ( " -consisdump " ) ) // Helps debugging some problems
{
INT32 i ;
CONS_Printf ( M_GetText ( " Player kicked is #%d, dumping consistency... \n " ) , pnum ) ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
CONS_Printf ( " ------------------------------------- \n " ) ;
CONS_Printf ( " Player %d: %s \n " , i , player_names [ i ] ) ;
CONS_Printf ( " Skin: %d \n " , players [ i ] . skin ) ;
CONS_Printf ( " Color: %d \n " , players [ i ] . skincolor ) ;
CONS_Printf ( " Speed: %d \n " , players [ i ] . speed > > FRACBITS ) ;
if ( players [ i ] . mo )
{
if ( ! players [ i ] . mo - > skin )
CONS_Printf ( " Mobj skin: NULL! \n " ) ;
else
CONS_Printf ( " Mobj skin: %s \n " , ( ( skin_t * ) players [ i ] . mo - > skin ) - > name ) ;
CONS_Printf ( " Position: %d, %d, %d \n " , players [ i ] . mo - > x , players [ i ] . mo - > y , players [ i ] . mo - > z ) ;
if ( ! players [ i ] . mo - > state )
CONS_Printf ( " State: S_NULL \n " ) ;
else
CONS_Printf ( " State: %d \n " , ( statenum_t ) ( players [ i ] . mo - > state - states ) ) ;
}
else
CONS_Printf ( " Mobj: NULL \n " ) ;
CONS_Printf ( " ------------------------------------- \n " ) ;
}
}
break ;
case KICK_MSG_TIMEOUT :
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s left the game (Connection timeout) " , player_names [ pnum ] ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_TIMEOUT ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_PLAYER_QUIT :
if ( netgame ) // not splitscreen/bots
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s left the game " , player_names [ pnum ] ) , false ) ;
2018-11-14 16:06:45 +00:00
kickreason = KR_LEAVE ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_BANNED :
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s has been banned (Don't come back) " , player_names [ pnum ] ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_BAN ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_CUSTOM_KICK :
READSTRINGN ( * p , reason , MAX_REASONLENGTH + 1 ) ;
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s has been kicked (%s) " , player_names [ pnum ] , reason ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_KICK ;
2014-03-15 16:59:03 +00:00
break ;
case KICK_MSG_CUSTOM_BAN :
READSTRINGN ( * p , reason , MAX_REASONLENGTH + 1 ) ;
2018-12-19 15:44:57 +00:00
HU_AddChatText ( va ( " \x82 *%s has been banned (%s) " , player_names [ pnum ] , reason ) , false ) ;
2016-10-21 20:16:54 +00:00
kickreason = KR_BAN ;
2014-03-15 16:59:03 +00:00
break ;
}
if ( pnum = = consoleplayer )
{
# ifdef DUMPCONSISTENCY
if ( msg = = KICK_MSG_CON_FAIL ) SV_SavedGame ( ) ;
# endif
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
if ( msg = = KICK_MSG_CON_FAIL )
2014-03-21 18:42:55 +00:00
M_StartMessage ( M_GetText ( " Server closed connection \n (synch failure) \n Press ESC \n " ) , NULL , MM_NOTHING ) ;
2014-03-15 16:59:03 +00:00
else if ( msg = = KICK_MSG_PING_HIGH )
M_StartMessage ( M_GetText ( " Server closed connection \n (Broke ping limit) \n Press ESC \n " ) , NULL , MM_NOTHING ) ;
else if ( msg = = KICK_MSG_BANNED )
M_StartMessage ( M_GetText ( " You have been banned by the server \n \n Press ESC \n " ) , NULL , MM_NOTHING ) ;
else if ( msg = = KICK_MSG_CUSTOM_KICK )
M_StartMessage ( va ( M_GetText ( " You have been kicked \n (%s) \n Press ESC \n " ) , reason ) , NULL , MM_NOTHING ) ;
else if ( msg = = KICK_MSG_CUSTOM_BAN )
M_StartMessage ( va ( M_GetText ( " You have been banned \n (%s) \n Press ESC \n " ) , reason ) , NULL , MM_NOTHING ) ;
else
M_StartMessage ( M_GetText ( " You have been kicked by the server \n \n Press ESC \n " ) , NULL , MM_NOTHING ) ;
}
else
2016-10-21 02:30:11 +00:00
CL_RemovePlayer ( pnum , kickreason ) ;
2014-03-15 16:59:03 +00:00
}
2020-01-02 23:40:49 +00:00
consvar_t cv_allownewplayer = { " allowjoin " , " On " , CV_NETVAR , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
consvar_t cv_joinnextround = { " joinnextround " , " Off " , CV_NETVAR , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ; /// \todo not done
2014-03-15 16:59:03 +00:00
static CV_PossibleValue_t maxplayers_cons_t [ ] = { { 2 , " MIN " } , { 32 , " MAX " } , { 0 , NULL } } ;
consvar_t cv_maxplayers = { " maxplayers " , " 8 " , CV_SAVE , maxplayers_cons_t , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
2019-10-24 00:37:09 +00:00
static CV_PossibleValue_t resynchattempts_cons_t [ ] = { { 1 , " MIN " } , { 20 , " MAX " } , { 0 , " No " } , { 0 , NULL } } ;
2019-08-27 00:38:32 +00:00
consvar_t cv_resynchattempts = { " resynchattempts " , " 10 " , CV_SAVE , resynchattempts_cons_t , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
consvar_t cv_blamecfail = { " blamecfail " , " Off " , CV_SAVE , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
2014-03-15 16:59:03 +00:00
// max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t [ ] = { { 0 , " MIN " } , { 51200 , " MAX " } , { 0 , NULL } } ;
2017-01-13 19:53:52 +00:00
consvar_t cv_maxsend = { " maxsend " , " 4096 " , CV_SAVE , maxsend_cons_t , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
consvar_t cv_noticedownload = { " noticedownload " , " Off " , CV_SAVE , CV_OnOff , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
// Speed of file downloading (in packets per tic)
static CV_PossibleValue_t downloadspeed_cons_t [ ] = { { 0 , " MIN " } , { 32 , " MAX " } , { 0 , NULL } } ;
consvar_t cv_downloadspeed = { " downloadspeed " , " 16 " , CV_SAVE , downloadspeed_cons_t , NULL , 0 , NULL , NULL , 0 , 0 , NULL } ;
2014-03-15 16:59:03 +00:00
static void Got_AddPlayer ( UINT8 * * p , INT32 playernum ) ;
// called one time at init
void D_ClientServerInit ( void )
{
DEBFILE ( va ( " - - -== SRB2 v%d.%.2d.%d " VERSIONSTRING " debugfile ==- - - \n " ,
VERSION / 100 , VERSION % 100 , SUBVERSION ) ) ;
# ifndef NONET
COM_AddCommand ( " getplayernum " , Command_GetPlayerNum ) ;
COM_AddCommand ( " kick " , Command_Kick ) ;
COM_AddCommand ( " ban " , Command_Ban ) ;
2019-02-22 00:44:18 +00:00
COM_AddCommand ( " banip " , Command_BanIP ) ;
2014-03-15 16:59:03 +00:00
COM_AddCommand ( " clearbans " , Command_ClearBans ) ;
COM_AddCommand ( " showbanlist " , Command_ShowBan ) ;
COM_AddCommand ( " reloadbans " , Command_ReloadBan ) ;
COM_AddCommand ( " connect " , Command_connect ) ;
COM_AddCommand ( " nodes " , Command_Nodes ) ;
2016-12-31 18:26:33 +00:00
# ifdef PACKETDROP
COM_AddCommand ( " drop " , Command_Drop ) ;
COM_AddCommand ( " droprate " , Command_Droprate ) ;
# endif
2017-01-14 18:24:22 +00:00
# ifdef _DEBUG
2017-01-13 19:53:52 +00:00
COM_AddCommand ( " numnodes " , Command_Numnodes ) ;
# endif
2014-03-15 16:59:03 +00:00
# endif
RegisterNetXCmd ( XD_KICK , Got_KickCmd ) ;
RegisterNetXCmd ( XD_ADDPLAYER , Got_AddPlayer ) ;
# ifndef NONET
# ifdef DUMPCONSISTENCY
CV_RegisterVar ( & cv_dumpconsistency ) ;
# endif
Ban_Load_File ( false ) ;
# endif
gametic = 0 ;
localgametic = 0 ;
// do not send anything before the real begin
SV_StopServer ( ) ;
SV_ResetServer ( ) ;
if ( dedicated )
SV_SpawnServer ( ) ;
}
static void ResetNode ( INT32 node )
{
nodeingame [ node ] = false ;
nodetoplayer [ node ] = - 1 ;
nodetoplayer2 [ node ] = - 1 ;
nettics [ node ] = gametic ;
supposedtics [ node ] = gametic ;
nodewaiting [ node ] = 0 ;
playerpernode [ node ] = 0 ;
2017-01-13 19:53:52 +00:00
sendingsavegame [ node ] = false ;
2014-03-15 16:59:03 +00:00
}
void SV_ResetServer ( void )
{
INT32 i ;
// +1 because this command will be executed in com_executebuffer in
// tryruntic so gametic will be incremented, anyway maketic > gametic
2016-12-31 18:26:33 +00:00
// is not an issue
2014-03-15 16:59:03 +00:00
maketic = gametic + 1 ;
neededtic = maketic ;
tictoclear = maketic ;
for ( i = 0 ; i < MAXNETNODES ; i + + )
2014-08-27 03:56:30 +00:00
{
2014-03-15 16:59:03 +00:00
ResetNode ( i ) ;
2014-08-27 03:56:30 +00:00
// Make sure resynch status doesn't get carried over!
SV_InitResynchVars ( i ) ;
}
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
# ifdef HAVE_BLUA
LUA_InvalidatePlayer ( & players [ i ] ) ;
# endif
playeringame [ i ] = false ;
playernode [ i ] = UINT8_MAX ;
sprintf ( player_names [ i ] , " Player %d " , i + 1 ) ;
2018-12-04 18:54:36 +00:00
adminplayers [ i ] = - 1 ; // Populate the entire adminplayers array with -1.
2014-03-15 16:59:03 +00:00
}
mynode = 0 ;
cl_packetmissed = false ;
if ( dedicated )
{
nodeingame [ 0 ] = true ;
serverplayer = 0 ;
}
else
serverplayer = consoleplayer ;
if ( server )
servernode = 0 ;
doomcom - > numslots = 0 ;
// clear server_context
memset ( server_context , ' - ' , 8 ) ;
DEBFILE ( " \n -=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=- \n \n " ) ;
}
static inline void SV_GenContext ( void )
{
UINT8 i ;
// generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z
// (hopefully M_Random is initialized!! if not this will be awfully silly!)
for ( i = 0 ; i < 8 ; i + + )
{
const char a = M_RandomKey ( 26 * 2 ) ;
2016-12-31 18:26:33 +00:00
if ( a < 26 ) // uppercase
2014-03-15 16:59:03 +00:00
server_context [ i ] = ' A ' + a ;
else // lowercase
server_context [ i ] = ' a ' + ( a - 26 ) ;
}
}
//
// D_QuitNetGame
// Called before quitting to leave a net game
// without hanging the other players
//
void D_QuitNetGame ( void )
{
if ( ! netgame | | ! netbuffer )
return ;
DEBFILE ( " =========================================================================== \n "
" Quitting Game, closing connection \n "
" =========================================================================== \n " ) ;
// abort send/receive of files
CloseNetFile ( ) ;
2020-01-22 22:08:57 +00:00
RemoveLuaFileTransfers ( ) ;
waitingforluafiletransfer = false ;
2014-03-15 16:59:03 +00:00
if ( server )
{
INT32 i ;
netbuffer - > packettype = PT_SERVERSHUTDOWN ;
for ( i = 0 ; i < MAXNETNODES ; i + + )
if ( nodeingame [ i ] )
HSendPacket ( i , true , 0 , 0 ) ;
if ( serverrunning & & ms_RoomId > 0 )
UnregisterServer ( ) ;
}
2016-12-31 18:26:33 +00:00
else if ( servernode > 0 & & servernode < MAXNETNODES & & nodeingame [ ( UINT8 ) servernode ] )
2014-03-15 16:59:03 +00:00
{
netbuffer - > packettype = PT_CLIENTQUIT ;
HSendPacket ( servernode , true , 0 , 0 ) ;
}
D_CloseConnection ( ) ;
2017-11-11 05:34:37 +00:00
ClearAdminPlayers ( ) ;
2014-03-15 16:59:03 +00:00
DEBFILE ( " =========================================================================== \n "
" Log finish \n "
" =========================================================================== \n " ) ;
# ifdef DEBUGFILE
if ( debugfile )
{
fclose ( debugfile ) ;
debugfile = NULL ;
}
# endif
}
2016-12-31 18:26:33 +00:00
// Adds a node to the game (player will follow at map change or at savegame....)
2014-03-15 16:59:03 +00:00
static inline void SV_AddNode ( INT32 node )
{
nettics [ node ] = gametic ;
supposedtics [ node ] = gametic ;
2016-12-31 18:26:33 +00:00
// little hack because the server connects to itself and puts
2014-03-15 16:59:03 +00:00
// nodeingame when connected not here
if ( node )
nodeingame [ node ] = true ;
}
// Xcmd XD_ADDPLAYER
static void Got_AddPlayer ( UINT8 * * p , INT32 playernum )
{
INT16 node , newplayernum ;
boolean splitscreenplayer ;
2017-11-11 05:34:37 +00:00
if ( playernum ! = serverplayer & & ! IsPlayerAdmin ( playernum ) )
2014-03-15 16:59:03 +00:00
{
// protect against hacked/buggy client
CONS_Alert ( CONS_WARNING , M_GetText ( " Illegal add player command received from %s \n " ) , player_names [ playernum ] ) ;
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( playernum , KICK_MSG_CON_FAIL ) ;
2014-03-15 16:59:03 +00:00
return ;
}
node = READUINT8 ( * p ) ;
newplayernum = READUINT8 ( * p ) ;
splitscreenplayer = newplayernum & 0x80 ;
newplayernum & = ~ 0x80 ;
// Clear player before joining, lest some things get set incorrectly
// HACK: don't do this for splitscreen, it relies on preset values
if ( ! splitscreen & & ! botingame )
CL_ClearPlayer ( newplayernum ) ;
playeringame [ newplayernum ] = true ;
2019-10-19 15:09:18 +00:00
READSTRINGN ( * p , player_names [ newplayernum ] , MAXPLAYERNAME ) ;
2014-03-15 16:59:03 +00:00
G_AddPlayer ( newplayernum ) ;
if ( newplayernum + 1 > doomcom - > numslots )
doomcom - > numslots = ( INT16 ) ( newplayernum + 1 ) ;
// the server is creating my player
if ( node = = mynode )
{
playernode [ newplayernum ] = 0 ; // for information only
if ( ! splitscreenplayer )
{
consoleplayer = newplayernum ;
displayplayer = newplayernum ;
secondarydisplayplayer = newplayernum ;
DEBFILE ( " spawning me \n " ) ;
}
else
{
secondarydisplayplayer = newplayernum ;
DEBFILE ( " spawning my brother \n " ) ;
if ( botingame )
players [ newplayernum ] . bot = 1 ;
}
D_SendPlayerConfig ( ) ;
addedtogame = true ;
}
2018-12-19 15:44:57 +00:00
if ( netgame )
2014-03-15 16:59:03 +00:00
{
2018-12-19 15:44:57 +00:00
if ( server & & cv_showjoinaddress . value )
{
const char * address ;
if ( I_GetNodeAddress & & ( address = I_GetNodeAddress ( node ) ) ! = NULL )
2019-11-16 00:42:02 +00:00
HU_AddChatText ( va ( " \x82 *%s has joined the game (player %d) (%s) " , player_names [ newplayernum ] , newplayernum , address ) , false ) ; // merge join notification + IP to avoid clogging console/chat.
2018-12-19 15:44:57 +00:00
}
else
2019-11-16 00:42:02 +00:00
HU_AddChatText ( va ( " \x82 *%s has joined the game (player %d) " , player_names [ newplayernum ] , newplayernum ) , false ) ; // if you don't wanna see the join address.
2014-03-15 16:59:03 +00:00
}
if ( server & & multiplayer & & motd [ 0 ] ! = ' \0 ' )
COM_BufAddText ( va ( " sayto %d %s \n " , newplayernum , motd ) ) ;
# ifdef HAVE_BLUA
LUAh_PlayerJoin ( newplayernum ) ;
# endif
}
2019-10-19 15:09:18 +00:00
static boolean SV_AddWaitingPlayers ( const char * name , const char * name2 )
2014-03-15 16:59:03 +00:00
{
INT32 node , n , newplayer = false ;
2019-10-19 15:09:18 +00:00
UINT8 buf [ 2 + MAXPLAYERNAME ] ;
UINT8 * p ;
2014-03-15 16:59:03 +00:00
UINT8 newplayernum = 0 ;
// What is the reason for this? Why can't newplayernum always be 0?
if ( dedicated )
newplayernum = 1 ;
for ( node = 0 ; node < MAXNETNODES ; node + + )
{
// splitscreen can allow 2 player in one node
for ( ; nodewaiting [ node ] > 0 ; nodewaiting [ node ] - - )
{
newplayer = true ;
2017-10-25 08:32:48 +00:00
if ( netgame )
2017-12-18 20:35:02 +00:00
// !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!!
//
// The line just after that comment is an awful, horrible, terrible, TERRIBLE hack.
//
// Basically, the fix I did in order to fix the download freezes happens
// to cause situations in which a player number does not match
// the node number associated to that player.
// That is totally normal, there is absolutely *nothing* wrong with that.
// Really. Player 7 being tied to node 29, for instance, is totally fine.
//
// HOWEVER. A few (broken) parts of the netcode do the TERRIBLE mistake
// of mixing up the concepts of node and player, resulting in
// incorrect handling of cases where a player is tied to a node that has
// a different number (which is a totally normal case, or at least should be).
// This incorrect handling can go as far as literally
// anyone from joining your server at all, forever.
//
// Given those two facts, there are two options available
// in order to let this download freeze fix be:
// 1) Fix the broken parts that assume a node is a player or similar bullshit.
// 2) Change the part this comment is located at, so that any player who joins
// is given the same number as their associated node.
//
// No need to say, 1) is by far the obvious best, whereas 2) is a terrible hack.
// Unfortunately, after trying 1), I most likely didn't manage to find all
// of those broken parts, and thus 2) has become the only safe option that remains.
//
// So I did this hack.
//
// If it isn't clear enough, in order to get rid of this ugly hack,
// you will have to fix all parts of the netcode that
// make a confusion between nodes and players.
//
// And if it STILL isn't clear enough, a node and a player
// is NOT the same thing. Never. NEVER. *NEVER*.
//
// And if someday you make the terrible mistake of
// daring to have the unforgivable idea to try thinking
// that a node might possibly be the same as a player,
// or that a player should have the same number as its node,
// be sure that I will somehow know about it and
// hunt you down tirelessly and make you regret it,
// even if you live on the other side of the world.
//
// TODO: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// \todo >>>>>>>>>> Remove this horrible hack as soon as possible <<<<<<<<<<
// TODO: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!!
2017-10-25 08:32:48 +00:00
newplayernum = node ; // OMFG SAY WELCOME TO TEH NEW HACK FOR FIX FIL DOWNLOAD!!1!
2017-12-18 20:35:02 +00:00
else // Don't use the hack if we don't have to
2017-10-25 08:32:48 +00:00
// search for a free playernum
// we can't use playeringame since it is not updated here
for ( ; newplayernum < MAXPLAYERS ; newplayernum + + )
{
for ( n = 0 ; n < MAXNETNODES ; n + + )
if ( nodetoplayer [ n ] = = newplayernum | | nodetoplayer2 [ n ] = = newplayernum )
break ;
if ( n = = MAXNETNODES )
2014-03-15 16:59:03 +00:00
break ;
2017-10-25 08:32:48 +00:00
}
2014-03-15 16:59:03 +00:00
// should never happen since we check the playernum
// before accepting the join
I_Assert ( newplayernum < MAXPLAYERS ) ;
playernode [ newplayernum ] = ( UINT8 ) node ;
2019-10-19 15:09:18 +00:00
p = buf + 2 ;
2014-03-15 16:59:03 +00:00
buf [ 0 ] = ( UINT8 ) node ;
buf [ 1 ] = newplayernum ;
if ( playerpernode [ node ] < 1 )
2019-10-19 15:09:18 +00:00
{
2014-03-15 16:59:03 +00:00
nodetoplayer [ node ] = newplayernum ;
2019-10-19 15:09:18 +00:00
WRITESTRINGN ( p , name , MAXPLAYERNAME ) ;
}
2014-03-15 16:59:03 +00:00
else
{
nodetoplayer2 [ node ] = newplayernum ;
buf [ 1 ] | = 0x80 ;
2019-10-19 15:09:18 +00:00
WRITESTRINGN ( p , name2 , MAXPLAYERNAME ) ;
2014-03-15 16:59:03 +00:00
}
playerpernode [ node ] + + ;
2019-10-19 15:09:18 +00:00
SendNetXCmd ( XD_ADDPLAYER , & buf , p - buf ) ;
2014-03-15 16:59:03 +00:00
DEBFILE ( va ( " Server added player %d node %d \n " , newplayernum , node ) ) ;
// use the next free slot (we can't put playeringame[newplayernum] = true here)
newplayernum + + ;
}
}
return newplayer ;
}
void CL_AddSplitscreenPlayer ( void )
{
2016-12-31 18:26:33 +00:00
if ( cl_mode = = CL_CONNECTED )
2014-03-15 16:59:03 +00:00
CL_SendJoin ( ) ;
}
void CL_RemoveSplitscreenPlayer ( void )
{
2016-12-31 18:26:33 +00:00
if ( cl_mode ! = CL_CONNECTED )
2014-03-15 16:59:03 +00:00
return ;
2020-01-12 18:43:04 +00:00
SendKick ( secondarydisplayplayer , KICK_MSG_PLAYER_QUIT ) ;
2014-03-15 16:59:03 +00:00
}
// is there a game running
boolean Playing ( void )
{
2017-01-13 19:53:52 +00:00
return ( server & & serverrunning ) | | ( client & & cl_mode = = CL_CONNECTED ) ;
2014-03-15 16:59:03 +00:00
}
boolean SV_SpawnServer ( void )
{
2014-03-23 16:00:29 +00:00
if ( demoplayback )
2014-03-15 16:59:03 +00:00
G_StopDemo ( ) ; // reset engine parameter
2014-03-23 16:00:29 +00:00
if ( metalplayback )
G_StopMetalDemo ( ) ;
2014-03-15 16:59:03 +00:00
if ( ! serverrunning )
{
CONS_Printf ( M_GetText ( " Starting Server.... \n " ) ) ;
serverrunning = true ;
SV_ResetServer ( ) ;
SV_GenContext ( ) ;
if ( netgame & & I_NetOpenSocket )
{
MSCloseUDPSocket ( ) ; // Tidy up before wiping the slate.
I_NetOpenSocket ( ) ;
if ( ms_RoomId > 0 )
RegisterServer ( ) ;
}
// non dedicated server just connect to itself
if ( ! dedicated )
CL_ConnectToServer ( false ) ;
else doomcom - > numslots = 1 ;
}
2019-10-19 15:09:18 +00:00
return SV_AddWaitingPlayers ( cv_playername . zstring , cv_playername2 . zstring ) ;
2014-03-15 16:59:03 +00:00
}
void SV_StopServer ( void )
{
tic_t i ;
if ( gamestate = = GS_INTERMISSION )
Y_EndIntermission ( ) ;
gamestate = wipegamestate = GS_NULL ;
localtextcmd [ 0 ] = 0 ;
localtextcmd2 [ 0 ] = 0 ;
2018-02-14 20:00:55 +00:00
for ( i = firstticstosend ; i < firstticstosend + BACKUPTICS ; i + + )
2014-03-15 16:59:03 +00:00
D_Clearticcmd ( i ) ;
consoleplayer = 0 ;
2016-12-31 18:26:33 +00:00
cl_mode = CL_SEARCHING ;
2014-03-15 16:59:03 +00:00
maketic = gametic + 1 ;
neededtic = maketic ;
serverrunning = false ;
}
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer ( void )
{
server = true ;
netgame = false ;
multiplayer = false ;
2019-12-18 04:25:57 +00:00
G_SetGametype ( GT_COOP ) ;
2014-03-15 16:59:03 +00:00
// no more tic the game with this settings!
SV_StopServer ( ) ;
if ( splitscreen )
multiplayer = true ;
}
static void SV_SendRefuse ( INT32 node , const char * reason )
{
strcpy ( netbuffer - > u . serverrefuse . reason , reason ) ;
netbuffer - > packettype = PT_SERVERREFUSE ;
HSendPacket ( node , true , 0 , strlen ( netbuffer - > u . serverrefuse . reason ) + 1 ) ;
Net_CloseConnection ( node ) ;
}
// used at txtcmds received to check packetsize bound
static size_t TotalTextCmdPerTic ( tic_t tic )
{
INT32 i ;
size_t total = 1 ; // num of textcmds in the tic (ntextcmd byte)
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
UINT8 * textcmd = D_GetExistingTextcmd ( tic , i ) ;
if ( ( ! i | | playeringame [ i ] ) & & textcmd )
total + = 2 + textcmd [ 0 ] ; // "+2" for size and playernum
}
return total ;
}
2016-12-31 18:26:33 +00:00
/** Called when a PT_CLIENTJOIN packet is received
*
* \ param node The packet sender
*
*/
2014-03-15 16:59:03 +00:00
static void HandleConnect ( SINT8 node )
{
2019-10-19 15:09:18 +00:00
char names [ MAXSPLITSCREENPLAYERS ] [ MAXPLAYERNAME + 1 ] ;
INT32 i ;
2014-03-15 16:59:03 +00:00
if ( bannednode & & bannednode [ node ] )
SV_SendRefuse ( node , M_GetText ( " You have been banned \n from the server " ) ) ;
2019-12-26 04:44:25 +00:00
else if ( netbuffer - > u . clientcfg . _255 ! = 255 | |
netbuffer - > u . clientcfg . packetversion ! = PACKETVERSION )
SV_SendRefuse ( node , " Incompatible packet formats. " ) ;
else if ( strncmp ( netbuffer - > u . clientcfg . application , SRB2APPLICATION ,
sizeof netbuffer - > u . clientcfg . application ) )
SV_SendRefuse ( node , " Different SRB2 modifications \n are not compatible. " ) ;
2014-03-15 16:59:03 +00:00
else if ( netbuffer - > u . clientcfg . version ! = VERSION
| | netbuffer - > u . clientcfg . subversion ! = SUBVERSION )
SV_SendRefuse ( node , va ( M_GetText ( " Different SRB2 versions cannot \n play a netgame! \n (server version %d.%d.%d) " ) , VERSION / 100 , VERSION % 100 , SUBVERSION ) ) ;
else if ( ! cv_allownewplayer . value & & node )
SV_SendRefuse ( node , M_GetText ( " The server is not accepting \n joins for the moment " ) ) ;
else if ( D_NumPlayers ( ) > = cv_maxplayers . value )
SV_SendRefuse ( node , va ( M_GetText ( " Maximum players reached: %d " ) , cv_maxplayers . value ) ) ;
else if ( netgame & & netbuffer - > u . clientcfg . localplayers > 1 ) // Hacked client?
SV_SendRefuse ( node , M_GetText ( " Too many players from \n this node. " ) ) ;
else if ( netgame & & ! netbuffer - > u . clientcfg . localplayers ) // Stealth join?
SV_SendRefuse ( node , M_GetText ( " No players from \n this node. " ) ) ;
2020-01-22 22:08:57 +00:00
# ifdef HAVE_BLUA
else if ( luafiletransfers )
SV_SendRefuse ( node , M_GetText ( " The server is broadcasting a file \n requested by a Lua script. \n Please wait a bit and then \n try rejoining. " ) ) ;
# endif
2014-03-15 16:59:03 +00:00
else
{
# ifndef NONET
boolean newnode = false ;
# endif
2019-10-19 15:09:18 +00:00
for ( i = 0 ; i < netbuffer - > u . clientcfg . localplayers - playerpernode [ node ] ; i + + )
{
strlcpy ( names [ i ] , netbuffer - > u . clientcfg . names [ i ] , MAXPLAYERNAME + 1 ) ;
if ( ! EnsurePlayerNameIsGood ( names [ i ] , - 1 ) )
{
SV_SendRefuse ( node , " Bad player name " ) ;
return ;
}
}
2014-03-15 16:59:03 +00:00
// client authorised to join
nodewaiting [ node ] = ( UINT8 ) ( netbuffer - > u . clientcfg . localplayers - playerpernode [ node ] ) ;
if ( ! nodeingame [ node ] )
{
gamestate_t backupstate = gamestate ;
# ifndef NONET
newnode = true ;
# endif
SV_AddNode ( node ) ;
2014-03-21 18:42:55 +00:00
2016-12-31 18:26:33 +00:00
/// \note Wait what???
/// What if the gamestate takes more than one second to get downloaded?
/// Or if a lagspike happens?
2014-03-21 18:42:55 +00:00
// you get a free second before desynch checks. use it wisely.
SV_InitResynchVars ( node ) ;
2014-03-15 16:59:03 +00:00
if ( cv_joinnextround . value & & gameaction = = ga_nothing )
G_SetGamestate ( GS_WAITINGPLAYERS ) ;
if ( ! SV_SendServerConfig ( node ) )
{
G_SetGamestate ( backupstate ) ;
2016-12-31 18:26:33 +00:00
/// \note Shouldn't SV_SendRefuse be called before ResetNode?
2014-03-15 16:59:03 +00:00
ResetNode ( node ) ;
SV_SendRefuse ( node , M_GetText ( " Server couldn't send info, please try again " ) ) ;
/// \todo fix this !!!
return ; // restart the while
}
//if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc?
// SV_SendPlayerConfigs(node); // send bare minimum player info
G_SetGamestate ( backupstate ) ;
DEBFILE ( " new node joined \n " ) ;
}
# ifdef JOININGAME
if ( nodewaiting [ node ] )
{
if ( ( gamestate = = GS_LEVEL | | gamestate = = GS_INTERMISSION ) & & newnode )
{
SV_SendSaveGame ( node ) ; // send a complete game state
DEBFILE ( " send savegame \n " ) ;
}
2019-10-19 15:09:18 +00:00
SV_AddWaitingPlayers ( names [ 0 ] , names [ 1 ] ) ;
2014-03-21 18:42:55 +00:00
player_joining = true ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
# else
# ifndef NONET
// I guess we have no use for this if we aren't doing mid-level joins?
( void ) newnode ;
# endif
2014-03-15 16:59:03 +00:00
# endif
}
}
2016-12-31 18:26:33 +00:00
/** Called when a PT_SERVERSHUTDOWN packet is received
*
* \ param node The packet sender ( should be the server )
*
*/
2014-03-15 16:59:03 +00:00
static void HandleShutdown ( SINT8 node )
{
( void ) node ;
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( M_GetText ( " Server has shutdown \n \n Press Esc \n " ) , NULL , MM_NOTHING ) ;
}
2016-12-31 18:26:33 +00:00
/** Called when a PT_NODETIMEOUT packet is received
*
* \ param node The packet sender ( should be the server )
*
*/
2014-03-15 16:59:03 +00:00
static void HandleTimeout ( SINT8 node )
{
( void ) node ;
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
M_StartMessage ( M_GetText ( " Server Timeout \n \n Press Esc \n " ) , NULL , MM_NOTHING ) ;
}
# ifndef NONET
2016-12-31 18:26:33 +00:00
/** Called when a PT_SERVERINFO packet is received
*
* \ param node The packet sender
* \ note What happens if the packet comes from a client or something like that ?
*
*/
2014-03-15 16:59:03 +00:00
static void HandleServerInfo ( SINT8 node )
{
// compute ping in ms
const tic_t ticnow = I_GetTime ( ) ;
const tic_t ticthen = ( tic_t ) LONG ( netbuffer - > u . serverinfo . time ) ;
const tic_t ticdiff = ( ticnow - ticthen ) * 1000 / NEWTICRATE ;
netbuffer - > u . serverinfo . time = ( tic_t ) LONG ( ticdiff ) ;
netbuffer - > u . serverinfo . servername [ MAXSERVERNAME - 1 ] = 0 ;
2019-12-26 04:44:25 +00:00
netbuffer - > u . serverinfo . application
[ sizeof netbuffer - > u . serverinfo . application - 1 ] = ' \0 ' ;
2019-12-31 06:18:55 +00:00
netbuffer - > u . serverinfo . gametypename
[ sizeof netbuffer - > u . serverinfo . gametypename - 1 ] = ' \0 ' ;
2014-03-15 16:59:03 +00:00
SL_InsertServer ( & netbuffer - > u . serverinfo , node ) ;
}
# endif
2016-12-31 18:26:33 +00:00
/** Handles a packet received from a node that isn't in game
*
* \ param node The packet sender
* \ todo Choose a better name , as the packet can also come from the server apparently ?
* \ sa HandlePacketFromPlayer
* \ sa GetPackets
*
*/
static void HandlePacketFromAwayNode ( SINT8 node )
{
if ( node ! = servernode )
DEBFILE ( va ( " Received packet from unknown host %d \n " , node ) ) ;
2014-03-21 18:42:55 +00:00
2017-05-23 15:29:09 +00:00
// macro for packets that should only be sent by the server
// if it is NOT from the server, bail out and close the connection!
# define SERVERONLY \
if ( node ! = servernode ) \
{ \
Net_CloseConnection ( node ) ; \
break ; \
}
2016-12-31 18:26:33 +00:00
switch ( netbuffer - > packettype )
2014-03-15 16:59:03 +00:00
{
2016-12-31 18:26:33 +00:00
case PT_ASKINFOVIAMS :
2017-05-27 18:06:46 +00:00
#if 0
2016-12-31 18:26:33 +00:00
if ( server & & serverrunning )
{
2017-05-22 15:54:39 +00:00
INT32 clientnode ;
if ( ms_RoomId < 0 ) // ignore if we're not actually on the MS right now
{
Net_CloseConnection ( node ) ; // and yes, close connection
return ;
}
clientnode = I_NetMakeNode ( netbuffer - > u . msaskinfo . clientaddr ) ;
2017-05-22 15:44:50 +00:00
if ( clientnode ! = - 1 )
{
SV_SendServerInfo ( clientnode , ( tic_t ) LONG ( netbuffer - > u . msaskinfo . time ) ) ;
SV_SendPlayerInfo ( clientnode ) ; // Send extra info
Net_CloseConnection ( clientnode ) ;
// Don't close connection to MS...
}
else
Net_CloseConnection ( node ) ; // ...unless the IP address is not valid
2016-12-31 18:26:33 +00:00
}
2017-05-22 15:54:39 +00:00
else
Net_CloseConnection ( node ) ; // you're not supposed to get it, so ignore it
2017-05-27 18:06:46 +00:00
# else
Net_CloseConnection ( node ) ;
# endif
2016-12-31 18:26:33 +00:00
break ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
case PT_ASKINFO :
if ( server & & serverrunning )
{
SV_SendServerInfo ( node , ( tic_t ) LONG ( netbuffer - > u . askinfo . time ) ) ;
SV_SendPlayerInfo ( node ) ; // Send extra info
}
2017-05-22 21:17:51 +00:00
Net_CloseConnection ( node ) ;
2016-12-31 18:26:33 +00:00
break ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
case PT_SERVERREFUSE : // Negative response of client join request
if ( server & & serverrunning )
{ // But wait I thought I'm the server?
Net_CloseConnection ( node ) ;
break ;
}
2017-05-23 15:29:09 +00:00
SERVERONLY
2016-12-31 18:26:33 +00:00
if ( cl_mode = = CL_WAITJOINRESPONSE )
2014-03-15 16:59:03 +00:00
{
2017-01-13 19:53:52 +00:00
// Save the reason so it can be displayed after quitting the netgame
char * reason = strdup ( netbuffer - > u . serverrefuse . reason ) ;
if ( ! reason )
I_Error ( " Out of memory! \n " ) ;
2016-12-31 18:26:33 +00:00
D_QuitNetGame ( ) ;
CL_Reset ( ) ;
D_StartTitle ( ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
M_StartMessage ( va ( M_GetText ( " Server refuses connection \n \n Reason: \n %s " ) ,
2017-01-13 19:53:52 +00:00
reason ) , NULL , MM_NOTHING ) ;
free ( reason ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Will be reset by caller. Signals refusal.
cl_mode = CL_ABORTED ;
}
break ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
case PT_SERVERCFG : // Positive response of client join request
2014-03-15 16:59:03 +00:00
{
2016-12-31 18:26:33 +00:00
INT32 j ;
UINT8 * scp ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( server & & serverrunning & & node ! = servernode )
{ // but wait I thought I'm the server?
Net_CloseConnection ( node ) ;
break ;
}
2017-05-23 15:29:09 +00:00
SERVERONLY
2016-12-31 18:26:33 +00:00
/// \note how would this happen? and is it doing the right thing if it does?
if ( cl_mode ! = CL_WAITJOINRESPONSE )
break ;
2014-03-15 16:59:03 +00:00
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-15 16:59:03 +00:00
{
2016-12-31 18:26:33 +00:00
maketic = gametic = neededtic = ( tic_t ) LONG ( netbuffer - > u . servercfg . gametic ) ;
2019-12-18 04:25:57 +00:00
G_SetGametype ( netbuffer - > u . servercfg . gametype ) ;
2016-12-31 18:26:33 +00:00
modifiedgame = netbuffer - > u . servercfg . modifiedgame ;
2017-12-08 04:45:39 +00:00
for ( j = 0 ; j < MAXPLAYERS ; j + + )
2017-11-11 05:34:37 +00:00
adminplayers [ j ] = netbuffer - > u . servercfg . adminplayers [ j ] ;
2016-12-31 18:26:33 +00:00
memcpy ( server_context , netbuffer - > u . servercfg . server_context , 8 ) ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
nodeingame [ ( UINT8 ) servernode ] = true ;
serverplayer = netbuffer - > u . servercfg . serverplayer ;
doomcom - > numslots = SHORT ( netbuffer - > u . servercfg . totalslotnum ) ;
mynode = netbuffer - > u . servercfg . clientnode ;
if ( serverplayer > = 0 )
playernode [ ( UINT8 ) serverplayer ] = servernode ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( netgame )
2014-03-15 16:59:03 +00:00
# ifdef JOININGAME
2016-12-31 18:26:33 +00:00
CONS_Printf ( M_GetText ( " Join accepted, waiting for complete game state... \n " ) ) ;
2014-03-15 16:59:03 +00:00
# else
2016-12-31 18:26:33 +00:00
CONS_Printf ( M_GetText ( " Join accepted, waiting for next level change... \n " ) ) ;
2014-03-15 16:59:03 +00:00
# endif
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " Server accept join gametic=%u mynode=%d \n " , gametic , mynode ) ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
memset ( playeringame , 0 , sizeof ( playeringame ) ) ;
for ( j = 0 ; j < MAXPLAYERS ; j + + )
{
if ( netbuffer - > u . servercfg . playerskins [ j ] = = 0xFF
2017-03-17 23:11:32 +00:00
& & netbuffer - > u . servercfg . playercolor [ j ] = = 0xFF
2017-03-18 13:03:29 +00:00
& & netbuffer - > u . servercfg . playeravailabilities [ j ] = = 0xFFFFFFFF )
2016-12-31 18:26:33 +00:00
continue ; // not in game
2014-08-04 03:49:33 +00:00
2016-12-31 18:26:33 +00:00
playeringame [ j ] = true ;
2017-03-18 10:12:14 +00:00
players [ j ] . availabilities = ( UINT32 ) LONG ( netbuffer - > u . servercfg . playeravailabilities [ j ] ) ;
2016-12-31 18:26:33 +00:00
SetPlayerSkinByNum ( j , ( INT32 ) netbuffer - > u . servercfg . playerskins [ j ] ) ;
players [ j ] . skincolor = netbuffer - > u . servercfg . playercolor [ j ] ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
scp = netbuffer - > u . servercfg . varlengthinputs ;
CV_LoadPlayerNames ( & scp ) ;
CV_LoadNetVars ( & scp ) ;
2014-03-15 16:59:03 +00:00
# ifdef JOININGAME
2016-12-31 18:26:33 +00:00
/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
/// Shouldn't them be downloaded even at intermission time?
/// Also, according to HandleConnect, the server will send the savegame even during intermission...
if ( netbuffer - > u . servercfg . gamestate = = GS_LEVEL /* ||
netbuffer - > u . servercfg . gamestate = = GS_INTERMISSION */ )
cl_mode = CL_DOWNLOADSAVEGAME ;
else
2014-03-15 16:59:03 +00:00
# endif
2016-12-31 18:26:33 +00:00
cl_mode = CL_CONNECTED ;
break ;
2014-03-15 16:59:03 +00:00
}
2016-12-31 18:26:33 +00:00
// Handled in d_netfil.c
case PT_FILEFRAGMENT :
if ( server )
{ // But wait I thought I'm the server?
Net_CloseConnection ( node ) ;
break ;
}
2017-05-23 15:29:09 +00:00
SERVERONLY
2017-05-22 21:17:51 +00:00
Got_Filetxpak ( ) ;
2016-12-31 18:26:33 +00:00
break ;
case PT_REQUESTFILE :
if ( server )
2017-05-26 12:39:54 +00:00
{
if ( ! cv_downloading . value | | ! Got_RequestFilePak ( node ) )
Net_CloseConnection ( node ) ; // close connection if one of the requested files could not be sent, or you disabled downloading anyway
}
else
Net_CloseConnection ( node ) ; // nope
2016-12-31 18:26:33 +00:00
break ;
case PT_NODETIMEOUT :
case PT_CLIENTQUIT :
if ( server )
Net_CloseConnection ( node ) ;
break ;
case PT_CLIENTCMD :
break ; // This is not an "unknown packet"
case PT_SERVERTICS :
// Do not remove my own server (we have just get a out of order packet)
if ( node = = servernode )
break ;
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2016-12-31 18:26:33 +00:00
default :
DEBFILE ( va ( " unknown packet received (%d) from unknown host \n " , netbuffer - > packettype ) ) ;
Net_CloseConnection ( node ) ;
break ; // Ignore it
}
2017-05-23 15:29:09 +00:00
# undef SERVERONLY
2016-12-31 18:26:33 +00:00
}
/** Handles a packet received from a node that is in game
*
* \ param node The packet sender
* \ todo Choose a better name
* \ sa HandlePacketFromAwayNode
* \ sa GetPackets
*
*/
2017-01-01 22:27:06 +00:00
static void HandlePacketFromPlayer ( SINT8 node )
2017-09-29 18:46:23 +00:00
{
INT32 netconsole ;
tic_t realend , realstart ;
UINT8 * pak , * txtpak , numtxtpak ;
2019-08-20 12:54:48 +00:00
UINT8 finalmd5 [ 16 ] ; /* Well, it's the cool thing to do? */
2016-12-31 18:26:33 +00:00
txtpak = NULL ;
if ( dedicated & & node = = 0 )
netconsole = 0 ;
else
netconsole = nodetoplayer [ node ] ;
2014-03-15 16:59:03 +00:00
# ifdef PARANOIA
2016-12-31 18:26:33 +00:00
if ( netconsole > = MAXPLAYERS )
I_Error ( " bad table nodetoplayer: node %d player %d " , doomcom - > remotenode , netconsole ) ;
2014-03-15 16:59:03 +00:00
# endif
2016-12-31 18:26:33 +00:00
switch ( netbuffer - > packettype )
{
2014-03-15 16:59:03 +00:00
// -------------------------------------------- SERVER RECEIVE ----------
2016-12-31 18:26:33 +00:00
case PT_RESYNCHGET :
2017-05-23 15:34:56 +00:00
if ( client )
break ;
2016-12-31 18:26:33 +00:00
SV_AcknowledgeResynchAck ( netconsole , netbuffer - > u . resynchgot ) ;
break ;
case PT_CLIENTCMD :
case PT_CLIENT2CMD :
case PT_CLIENTMIS :
case PT_CLIENT2MIS :
case PT_NODEKEEPALIVE :
case PT_NODEKEEPALIVEMIS :
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-21 18:42:55 +00:00
break ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Ignore tics from those not synched
2019-10-16 19:24:30 +00:00
if ( resynch_inprogress [ node ] & & nettics [ node ] = = gametic )
2016-12-31 18:26:33 +00:00
break ;
2014-03-21 18:42:55 +00:00
2016-12-31 18:26:33 +00:00
// To save bytes, only the low byte of tic numbers are sent
// Use ExpandTics to figure out what the rest of the bytes are
realstart = ExpandTics ( netbuffer - > u . clientpak . client_tic ) ;
realend = ExpandTics ( netbuffer - > u . clientpak . resendfrom ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( netbuffer - > packettype = = PT_CLIENTMIS | | netbuffer - > packettype = = PT_CLIENT2MIS
| | netbuffer - > packettype = = PT_NODEKEEPALIVEMIS
| | supposedtics [ node ] < realend )
{
supposedtics [ node ] = realend ;
}
// Discard out of order packet
if ( nettics [ node ] > realend )
{
DEBFILE ( va ( " out of order ticcmd discarded nettics = %u \n " , nettics [ node ] ) ) ;
break ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Update the nettics
nettics [ node ] = realend ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Don't do anything for packets of type NODEKEEPALIVE?
if ( netconsole = = - 1 | | netbuffer - > packettype = = PT_NODEKEEPALIVE
| | netbuffer - > packettype = = PT_NODEKEEPALIVEMIS )
break ;
2014-03-15 16:59:03 +00:00
2017-01-13 19:53:52 +00:00
// If a client sends a ticcmd it should mean they are done receiving the savegame
sendingsavegame [ node ] = false ;
// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
/// \todo Use a separate cvar for that kind of timeout?
freezetimeout [ node ] = I_GetTime ( ) + connectiontimeout ;
2016-12-31 18:26:33 +00:00
// Copy ticcmd
G_MoveTiccmd ( & netcmds [ maketic % BACKUPTICS ] [ netconsole ] , & netbuffer - > u . clientpak . cmd , 1 ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Check ticcmd for "speed hacks"
if ( netcmds [ maketic % BACKUPTICS ] [ netconsole ] . forwardmove > MAXPLMOVE | | netcmds [ maketic % BACKUPTICS ] [ netconsole ] . forwardmove < - MAXPLMOVE
| | netcmds [ maketic % BACKUPTICS ] [ netconsole ] . sidemove > MAXPLMOVE | | netcmds [ maketic % BACKUPTICS ] [ netconsole ] . sidemove < - MAXPLMOVE )
{
CONS_Alert ( CONS_WARNING , M_GetText ( " Illegal movement value received from node %d \n " ) , netconsole ) ;
//D_Clearticcmd(k);
2014-03-15 16:59:03 +00:00
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2016-12-31 18:26:33 +00:00
break ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Splitscreen cmd
2017-05-23 17:46:34 +00:00
if ( ( netbuffer - > packettype = = PT_CLIENT2CMD | | netbuffer - > packettype = = PT_CLIENT2MIS )
& & nodetoplayer2 [ node ] > = 0 )
2016-12-31 18:26:33 +00:00
G_MoveTiccmd ( & netcmds [ maketic % BACKUPTICS ] [ ( UINT8 ) nodetoplayer2 [ node ] ] ,
& netbuffer - > u . client2pak . cmd2 , 1 ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// A delay before we check resynching
// Used on join or just after a synch fail
if ( resynch_delay [ node ] )
{
- - resynch_delay [ node ] ;
break ;
}
// Check player consistancy during the level
if ( realstart < = gametic & & realstart > gametic - BACKUPTICS + 1 & & gamestate = = GS_LEVEL
& & consistancy [ realstart % BACKUPTICS ] ! = SHORT ( netbuffer - > u . clientpak . consistancy ) )
{
SV_RequireResynch ( node ) ;
if ( cv_resynchattempts . value & & resynch_score [ node ] < = ( unsigned ) cv_resynchattempts . value * 250 )
2014-03-21 18:42:55 +00:00
{
2016-12-31 18:26:33 +00:00
if ( cv_blamecfail . value )
CONS_Printf ( M_GetText ( " Synch failure for player %d (%s); expected %hd, got %hd \n " ) ,
netconsole + 1 , player_names [ netconsole ] ,
consistancy [ realstart % BACKUPTICS ] ,
SHORT ( netbuffer - > u . clientpak . consistancy ) ) ;
DEBFILE ( va ( " Restoring player %d (synch failure) [%update] %d!=%d \n " ,
netconsole , realstart , consistancy [ realstart % BACKUPTICS ] ,
SHORT ( netbuffer - > u . clientpak . consistancy ) ) ) ;
2014-03-21 18:42:55 +00:00
break ;
}
2016-12-31 18:26:33 +00:00
else
2014-03-15 16:59:03 +00:00
{
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " player %d kicked (synch failure) [%u] %d!=%d \n " ,
netconsole , realstart , consistancy [ realstart % BACKUPTICS ] ,
SHORT ( netbuffer - > u . clientpak . consistancy ) ) ) ;
break ;
2014-03-15 16:59:03 +00:00
}
2016-12-31 18:26:33 +00:00
}
else if ( resynch_score [ node ] )
- - resynch_score [ node ] ;
break ;
case PT_TEXTCMD2 : // splitscreen special
netconsole = nodetoplayer2 [ node ] ;
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2016-12-31 18:26:33 +00:00
case PT_TEXTCMD :
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-15 16:59:03 +00:00
break ;
2016-12-31 18:26:33 +00:00
if ( netconsole < 0 | | netconsole > = MAXPLAYERS )
Net_UnAcknowledgePacket ( node ) ;
else
{
size_t j ;
tic_t tic = maketic ;
UINT8 * textcmd ;
2017-05-23 16:35:32 +00:00
// ignore if the textcmd has a reported size of zero
// this shouldn't be sent at all
if ( ! netbuffer - > u . textcmd [ 0 ] )
{
DEBFILE ( va ( " GetPacket: Textcmd with size 0 detected! (node %u, player %d) \n " ,
node , netconsole ) ) ;
Net_UnAcknowledgePacket ( node ) ;
break ;
}
2017-05-23 15:13:42 +00:00
// ignore if the textcmd size var is actually larger than it should be
2017-05-23 16:35:32 +00:00
// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
if ( netbuffer - > u . textcmd [ 0 ] > ( size_t ) doomcom - > datalength - BASEPACKETSIZE - 1 )
2017-05-23 15:13:42 +00:00
{
2017-05-25 17:10:20 +00:00
DEBFILE ( va ( " GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d) \n " ,
netbuffer - > u . textcmd [ 0 ] , sizeu1 ( ( size_t ) doomcom - > datalength - BASEPACKETSIZE - 1 ) ,
2017-05-23 16:35:32 +00:00
node , netconsole ) ) ;
2017-05-23 15:13:42 +00:00
Net_UnAcknowledgePacket ( node ) ;
break ;
}
2016-12-31 18:26:33 +00:00
// check if tic that we are making isn't too large else we cannot send it :(
// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
j = software_MAXPACKETLENGTH
- ( netbuffer - > u . textcmd [ 0 ] + 2 + BASESERVERTICSSIZE
+ ( doomcom - > numslots + 1 ) * sizeof ( ticcmd_t ) ) ;
// search a tic that have enougth space in the ticcmd
while ( ( textcmd = D_GetExistingTextcmd ( tic , netconsole ) ) ,
( TotalTextCmdPerTic ( tic ) > j | | netbuffer - > u . textcmd [ 0 ] + ( textcmd ? textcmd [ 0 ] : 0 ) > MAXTEXTCMD )
& & tic < firstticstosend + BACKUPTICS )
tic + + ;
if ( tic > = firstticstosend + BACKUPTICS )
2014-03-15 16:59:03 +00:00
{
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " GetPacket: Textcmd too long (max %s, used %s, mak %d, "
" tosend %u, node %u, player %d) \n " , sizeu1 ( j ) , sizeu2 ( TotalTextCmdPerTic ( maketic ) ) ,
maketic , firstticstosend , node , netconsole ) ) ;
Net_UnAcknowledgePacket ( node ) ;
break ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Make sure we have a buffer
if ( ! textcmd ) textcmd = D_GetTextcmd ( tic , netconsole ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " textcmd put in tic %u at position %d (player %d) ftts %u mk %u \n " ,
tic , textcmd [ 0 ] + 1 , netconsole , firstticstosend , maketic ) ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
M_Memcpy ( & textcmd [ textcmd [ 0 ] + 1 ] , netbuffer - > u . textcmd + 1 , netbuffer - > u . textcmd [ 0 ] ) ;
textcmd [ 0 ] + = ( UINT8 ) netbuffer - > u . textcmd [ 0 ] ;
}
break ;
2019-07-15 22:39:58 +00:00
case PT_LOGIN :
if ( client )
break ;
# ifndef NOMD5
2019-08-17 17:33:14 +00:00
if ( doomcom - > datalength < 16 ) /* ignore partial sends */
2019-07-15 22:39:58 +00:00
break ;
if ( ! adminpasswordset )
{
CONS_Printf ( M_GetText ( " Password from %s failed (no password set). \n " ) , player_names [ netconsole ] ) ;
break ;
}
// Do the final pass to compare with the sent md5
2019-08-17 17:33:14 +00:00
D_MD5PasswordPass ( adminpassmd5 , 16 , va ( " PNUM%02d " , netconsole ) , & finalmd5 ) ;
2019-07-15 22:39:58 +00:00
2019-08-17 17:33:14 +00:00
if ( ! memcmp ( netbuffer - > u . md5sum , finalmd5 , 16 ) )
2019-07-15 22:39:58 +00:00
{
CONS_Printf ( M_GetText ( " %s passed authentication. \n " ) , player_names [ netconsole ] ) ;
COM_BufInsertText ( va ( " promote %d \n " , netconsole ) ) ; // do this immediately
}
else
CONS_Printf ( M_GetText ( " Password from %s failed. \n " ) , player_names [ netconsole ] ) ;
# endif
break ;
2016-12-31 18:26:33 +00:00
case PT_NODETIMEOUT :
case PT_CLIENTQUIT :
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-15 16:59:03 +00:00
break ;
2016-12-31 18:26:33 +00:00
// nodeingame will be put false in the execution of kick command
// this allow to send some packets to the quitting client to have their ack back
nodewaiting [ node ] = 0 ;
if ( netconsole ! = - 1 & & playeringame [ netconsole ] )
{
2020-01-12 18:43:04 +00:00
UINT8 kickmsg ;
2016-12-31 18:26:33 +00:00
if ( netbuffer - > packettype = = PT_NODETIMEOUT )
2020-01-12 18:43:04 +00:00
kickmsg = KICK_MSG_TIMEOUT ;
2016-12-31 18:26:33 +00:00
else
2020-01-12 18:43:04 +00:00
kickmsg = KICK_MSG_PLAYER_QUIT ;
SendKick ( netconsole , kickmsg ) ;
2016-12-31 18:26:33 +00:00
nodetoplayer [ node ] = - 1 ;
2020-01-12 18:43:04 +00:00
2016-12-31 18:26:33 +00:00
if ( nodetoplayer2 [ node ] ! = - 1 & & nodetoplayer2 [ node ] > = 0
& & playeringame [ ( UINT8 ) nodetoplayer2 [ node ] ] )
2014-03-15 16:59:03 +00:00
{
2020-01-12 18:43:04 +00:00
SendKick ( nodetoplayer2 [ node ] , kickmsg ) ;
2016-12-31 18:26:33 +00:00
nodetoplayer2 [ node ] = - 1 ;
2014-03-15 16:59:03 +00:00
}
2016-12-31 18:26:33 +00:00
}
Net_CloseConnection ( node ) ;
nodeingame [ node ] = false ;
break ;
2020-01-22 22:08:57 +00:00
# ifdef HAVE_BLUA
case PT_ASKLUAFILE :
if ( server & & luafiletransfers & & luafiletransfers - > nodestatus [ node ] = = LFTNS_ASKED )
{
char * name = va ( " %s " PATHSEP " %s " , luafiledir , luafiletransfers - > filename ) ;
boolean textmode = ! strchr ( luafiletransfers - > mode , ' b ' ) ;
SV_SendLuaFile ( node , name , textmode ) ;
}
break ;
case PT_HASLUAFILE :
if ( server & & luafiletransfers & & luafiletransfers - > nodestatus [ node ] = = LFTNS_SENDING )
SV_HandleLuaFileSent ( node ) ;
break ;
# endif
2014-03-15 16:59:03 +00:00
// -------------------------------------------- CLIENT RECEIVE ----------
2016-12-31 18:26:33 +00:00
case PT_RESYNCHEND :
// Only accept PT_RESYNCHEND from the server.
if ( node ! = servernode )
{
2017-01-13 19:53:52 +00:00
CONS_Alert ( CONS_WARNING , M_GetText ( " %s received from non-host %d \n " ) , " PT_RESYNCHEND " , node ) ;
2016-12-31 18:26:33 +00:00
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2016-12-31 18:26:33 +00:00
break ;
}
resynch_local_inprogress = false ;
2014-03-21 18:42:55 +00:00
2016-12-31 18:26:33 +00:00
P_SetRandSeed ( netbuffer - > u . resynchend . randomseed ) ;
2014-03-21 18:42:55 +00:00
2019-12-18 14:43:50 +00:00
if ( gametyperules & GTR_TEAMFLAGS )
2016-12-31 18:26:33 +00:00
resynch_read_ctf ( & netbuffer - > u . resynchend ) ;
resynch_read_others ( & netbuffer - > u . resynchend ) ;
2014-03-21 18:42:55 +00:00
2016-12-31 18:26:33 +00:00
break ;
case PT_SERVERTICS :
// Only accept PT_SERVERTICS from the server.
if ( node ! = servernode )
{
CONS_Alert ( CONS_WARNING , M_GetText ( " %s received from non-host %d \n " ) , " PT_SERVERTICS " , node ) ;
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2016-12-31 18:26:33 +00:00
break ;
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
realstart = ExpandTics ( netbuffer - > u . serverpak . starttic ) ;
realend = realstart + netbuffer - > u . serverpak . numtics ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( ! txtpak )
txtpak = ( UINT8 * ) & netbuffer - > u . serverpak . cmds [ netbuffer - > u . serverpak . numslots
* netbuffer - > u . serverpak . numtics ] ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( realend > gametic + BACKUPTICS )
realend = gametic + BACKUPTICS ;
cl_packetmissed = realstart > neededtic ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
if ( realstart < = neededtic & & realend > neededtic )
{
tic_t i , j ;
pak = ( UINT8 * ) & netbuffer - > u . serverpak . cmds ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
for ( i = realstart ; i < realend ; i + + )
2014-03-15 16:59:03 +00:00
{
2016-12-31 18:26:33 +00:00
// clear first
D_Clearticcmd ( i ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// copy the tics
pak = G_ScpyTiccmd ( netcmds [ i % BACKUPTICS ] , pak ,
netbuffer - > u . serverpak . numslots * sizeof ( ticcmd_t ) ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// copy the textcmds
numtxtpak = * txtpak + + ;
for ( j = 0 ; j < numtxtpak ; j + + )
{
INT32 k = * txtpak + + ; // playernum
const size_t txtsize = txtpak [ 0 ] + 1 ;
2014-03-15 16:59:03 +00:00
2018-02-14 14:09:52 +00:00
if ( i > = gametic ) // Don't copy old net commands
M_Memcpy ( D_GetTextcmd ( i , k ) , txtpak , txtsize ) ;
2016-12-31 18:26:33 +00:00
txtpak + = txtsize ;
2014-03-15 16:59:03 +00:00
}
}
2016-12-31 18:26:33 +00:00
neededtic = realend ;
}
else
2017-01-13 19:53:52 +00:00
{
2016-12-31 18:26:33 +00:00
DEBFILE ( va ( " frame not in bound: %u \n " , neededtic ) ) ;
2017-01-13 19:53:52 +00:00
/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
I_Error ( " Received an out of order PT_SERVERTICS packet! \n "
" Got tics %d-%d, needed tic %d \n \n "
" Please report this crash on the Master Board, \n "
" IRC or Discord so it can be fixed. \n " , ( INT32 ) realstart , ( INT32 ) realend , ( INT32 ) neededtic ) ; */
}
2016-12-31 18:26:33 +00:00
break ;
case PT_RESYNCHING :
// Only accept PT_RESYNCHING from the server.
if ( node ! = servernode )
{
2017-01-13 19:53:52 +00:00
CONS_Alert ( CONS_WARNING , M_GetText ( " %s received from non-host %d \n " ) , " PT_RESYNCHING " , node ) ;
2016-12-31 18:26:33 +00:00
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2014-03-15 16:59:03 +00:00
break ;
2016-12-31 18:26:33 +00:00
}
resynch_local_inprogress = true ;
CL_AcknowledgeResynch ( & netbuffer - > u . resynchpak ) ;
break ;
case PT_PING :
// Only accept PT_PING from the server.
if ( node ! = servernode )
{
2017-01-13 19:53:52 +00:00
CONS_Alert ( CONS_WARNING , M_GetText ( " %s received from non-host %d \n " ) , " PT_PING " , node ) ;
2016-12-31 18:26:33 +00:00
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2014-03-15 16:59:03 +00:00
break ;
2016-12-31 18:26:33 +00:00
}
//Update client ping table from the server.
2017-01-13 19:53:52 +00:00
if ( client )
2016-12-31 18:26:33 +00:00
{
2019-11-18 01:22:47 +00:00
UINT8 i ;
2017-06-01 19:46:44 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2016-12-31 18:26:33 +00:00
if ( playeringame [ i ] )
playerpingtable [ i ] = ( tic_t ) netbuffer - > u . pingtable [ i ] ;
2019-11-18 01:22:47 +00:00
servermaxping = ( tic_t ) netbuffer - > u . pingtable [ MAXPLAYERS ] ;
2016-12-31 18:26:33 +00:00
}
break ;
case PT_SERVERCFG :
break ;
case PT_FILEFRAGMENT :
2017-05-22 21:17:51 +00:00
// Only accept PT_FILEFRAGMENT from the server.
if ( node ! = servernode )
{
CONS_Alert ( CONS_WARNING , M_GetText ( " %s received from non-host %d \n " ) , " PT_FILEFRAGMENT " , node ) ;
if ( server )
2020-01-12 18:43:04 +00:00
SendKick ( netconsole , KICK_MSG_CON_FAIL ) ;
2017-05-22 21:17:51 +00:00
break ;
}
2017-01-13 19:53:52 +00:00
if ( client )
2016-12-31 18:26:33 +00:00
Got_Filetxpak ( ) ;
break ;
2020-01-22 22:08:57 +00:00
# ifdef HAVE_BLUA
case PT_SENDINGLUAFILE :
if ( client )
CL_PrepareDownloadLuaFile ( ) ;
break ;
# endif
2016-12-31 18:26:33 +00:00
default :
DEBFILE ( va ( " UNKNOWN PACKET TYPE RECEIVED %d from host %d \n " ,
netbuffer - > packettype , node ) ) ;
} // end switch
}
/** Handles all received packets, if any
*
* \ todo Add details to this description ( lol )
*
*/
static void GetPackets ( void )
2017-09-29 18:46:23 +00:00
{
SINT8 node ; // The packet sender
2016-12-31 18:26:33 +00:00
player_joining = false ;
while ( HGetPacket ( ) )
{
node = ( SINT8 ) doomcom - > remotenode ;
if ( netbuffer - > packettype = = PT_CLIENTJOIN & & server )
{
HandleConnect ( node ) ;
continue ;
}
2017-01-13 19:53:52 +00:00
if ( node = = servernode & & client & & cl_mode ! = CL_SEARCHING )
2016-12-31 18:26:33 +00:00
{
2017-01-13 19:53:52 +00:00
if ( netbuffer - > packettype = = PT_SERVERSHUTDOWN )
{
HandleShutdown ( node ) ;
continue ;
}
if ( netbuffer - > packettype = = PT_NODETIMEOUT )
{
HandleTimeout ( node ) ;
continue ;
}
2016-12-31 18:26:33 +00:00
}
# ifndef NONET
if ( netbuffer - > packettype = = PT_SERVERINFO )
{
HandleServerInfo ( node ) ;
continue ;
}
# endif
if ( netbuffer - > packettype = = PT_PLAYERINFO )
continue ; // We do nothing with PLAYERINFO, that's for the MS browser.
// Packet received from someone already playing
if ( nodeingame [ node ] )
HandlePacketFromPlayer ( node ) ;
2017-01-13 19:53:52 +00:00
// Packet received from someone not playing
2016-12-31 18:26:33 +00:00
else
HandlePacketFromAwayNode ( node ) ;
}
2014-03-15 16:59:03 +00:00
}
//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
2014-03-21 18:42:55 +00:00
// no more use random generator, because at very first tic isn't yet synchronized
2014-03-15 16:59:03 +00:00
// Note: It is called consistAncy on purpose.
//
static INT16 Consistancy ( void )
{
INT32 i ;
2014-03-21 18:42:55 +00:00
UINT32 ret = 0 ;
2017-01-01 22:07:34 +00:00
# ifdef MOBJCONSISTANCY
2016-12-31 18:26:33 +00:00
thinker_t * th ;
mobj_t * mo ;
2017-01-01 22:07:34 +00:00
# endif
2014-03-15 16:59:03 +00:00
DEBFILE ( va ( " TIC %u " , gametic ) ) ;
2014-03-21 18:42:55 +00:00
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2014-03-21 18:42:55 +00:00
{
if ( ! playeringame [ i ] )
ret ^ = 0xCCCC ;
else if ( ! players [ i ] . mo ) ;
else
2014-03-15 16:59:03 +00:00
{
2014-03-21 18:42:55 +00:00
ret + = players [ i ] . mo - > x ;
ret - = players [ i ] . mo - > y ;
ret + = players [ i ] . powers [ pw_shield ] ;
ret * = i + 1 ;
2014-03-15 16:59:03 +00:00
}
2014-03-21 18:42:55 +00:00
}
// I give up
// Coop desynching enemies is painful
if ( ! G_PlatformGametype ( ) )
ret + = P_GetRandSeed ( ) ;
2014-03-15 16:59:03 +00:00
2017-01-01 22:07:34 +00:00
# ifdef MOBJCONSISTANCY
2019-04-21 10:58:22 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2016-12-31 18:26:33 +00:00
{
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2016-12-31 18:26:33 +00:00
mo = ( mobj_t * ) th ;
if ( mo - > flags & ( MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY ) )
{
ret - = mo - > type ;
ret + = mo - > x ;
ret - = mo - > y ;
ret + = mo - > z ;
ret - = mo - > momx ;
ret + = mo - > momy ;
ret - = mo - > momz ;
ret + = mo - > angle ;
ret - = mo - > flags ;
ret + = mo - > flags2 ;
ret - = mo - > eflags ;
if ( mo - > target )
{
ret + = mo - > target - > type ;
ret - = mo - > target - > x ;
ret + = mo - > target - > y ;
ret - = mo - > target - > z ;
ret + = mo - > target - > momx ;
ret - = mo - > target - > momy ;
ret + = mo - > target - > momz ;
ret - = mo - > target - > angle ;
ret + = mo - > target - > flags ;
ret - = mo - > target - > flags2 ;
ret + = mo - > target - > eflags ;
ret - = mo - > target - > state - states ;
ret + = mo - > target - > tics ;
ret - = mo - > target - > sprite ;
ret + = mo - > target - > frame ;
}
else
ret ^ = 0x3333 ;
if ( mo - > tracer & & mo - > tracer - > type ! = MT_OVERLAY )
{
ret + = mo - > tracer - > type ;
ret - = mo - > tracer - > x ;
ret + = mo - > tracer - > y ;
ret - = mo - > tracer - > z ;
ret + = mo - > tracer - > momx ;
ret - = mo - > tracer - > momy ;
ret + = mo - > tracer - > momz ;
ret - = mo - > tracer - > angle ;
ret + = mo - > tracer - > flags ;
ret - = mo - > tracer - > flags2 ;
ret + = mo - > tracer - > eflags ;
ret - = mo - > tracer - > state - states ;
ret + = mo - > tracer - > tics ;
ret - = mo - > tracer - > sprite ;
ret + = mo - > tracer - > frame ;
}
else
ret ^ = 0xAAAA ;
ret - = mo - > state - states ;
ret + = mo - > tics ;
ret - = mo - > sprite ;
ret + = mo - > frame ;
}
2017-01-01 22:07:34 +00:00
}
# endif
2016-12-31 18:26:33 +00:00
2017-06-10 16:09:08 +00:00
DEBFILE ( va ( " Consistancy = %u \n " , ( ret & 0xFFFF ) ) ) ;
2014-03-21 18:42:55 +00:00
return ( INT16 ) ( ret & 0xFFFF ) ;
2014-03-15 16:59:03 +00:00
}
// send the client packet to the server
static void CL_SendClientCmd ( void )
{
size_t packetsize = 0 ;
netbuffer - > packettype = PT_CLIENTCMD ;
if ( cl_packetmissed )
netbuffer - > packettype + + ;
netbuffer - > u . clientpak . resendfrom = ( UINT8 ) ( neededtic & UINT8_MAX ) ;
netbuffer - > u . clientpak . client_tic = ( UINT8 ) ( gametic & UINT8_MAX ) ;
if ( gamestate = = GS_WAITINGPLAYERS )
{
2017-01-13 19:53:52 +00:00
// Send PT_NODEKEEPALIVE packet
2014-03-15 16:59:03 +00:00
netbuffer - > packettype + = 4 ;
packetsize = sizeof ( clientcmd_pak ) - sizeof ( ticcmd_t ) - sizeof ( INT16 ) ;
HSendPacket ( servernode , false , 0 , packetsize ) ;
}
else if ( gamestate ! = GS_NULL )
{
G_MoveTiccmd ( & netbuffer - > u . clientpak . cmd , & localcmds , 1 ) ;
netbuffer - > u . clientpak . consistancy = SHORT ( consistancy [ gametic % BACKUPTICS ] ) ;
2017-01-13 19:53:52 +00:00
// Send a special packet with 2 cmd for splitscreen
2014-03-15 16:59:03 +00:00
if ( splitscreen | | botingame )
{
netbuffer - > packettype + = 2 ;
G_MoveTiccmd ( & netbuffer - > u . client2pak . cmd2 , & localcmds2 , 1 ) ;
packetsize = sizeof ( client2cmd_pak ) ;
}
else
packetsize = sizeof ( clientcmd_pak ) ;
HSendPacket ( servernode , false , 0 , packetsize ) ;
}
2016-12-31 18:26:33 +00:00
if ( cl_mode = = CL_CONNECTED | | dedicated )
2014-03-15 16:59:03 +00:00
{
2017-01-13 19:53:52 +00:00
// Send extra data if needed
2014-03-15 16:59:03 +00:00
if ( localtextcmd [ 0 ] )
{
netbuffer - > packettype = PT_TEXTCMD ;
M_Memcpy ( netbuffer - > u . textcmd , localtextcmd , localtextcmd [ 0 ] + 1 ) ;
2017-01-13 19:53:52 +00:00
// All extra data have been sent
if ( HSendPacket ( servernode , true , 0 , localtextcmd [ 0 ] + 1 ) ) // Send can fail...
2014-03-15 16:59:03 +00:00
localtextcmd [ 0 ] = 0 ;
}
2017-01-13 19:53:52 +00:00
// Send extra data if needed for player 2 (splitscreen)
2014-03-15 16:59:03 +00:00
if ( localtextcmd2 [ 0 ] )
{
netbuffer - > packettype = PT_TEXTCMD2 ;
M_Memcpy ( netbuffer - > u . textcmd , localtextcmd2 , localtextcmd2 [ 0 ] + 1 ) ;
2017-01-13 19:53:52 +00:00
// All extra data have been sent
if ( HSendPacket ( servernode , true , 0 , localtextcmd2 [ 0 ] + 1 ) ) // Send can fail...
2014-03-15 16:59:03 +00:00
localtextcmd2 [ 0 ] = 0 ;
}
}
}
// send the server packet
// send tic from firstticstosend to maketic-1
static void SV_SendTics ( void )
{
tic_t realfirsttic , lasttictosend , i ;
UINT32 n ;
INT32 j ;
size_t packsize ;
UINT8 * bufpos ;
UINT8 * ntextcmd ;
// send to all client but not to me
// for each node create a packet with x tics and send it
// x is computed using supposedtics[n], max packet size and maketic
for ( n = 1 ; n < MAXNETNODES ; n + + )
if ( nodeingame [ n ] )
{
lasttictosend = maketic ;
// assert supposedtics[n]>=nettics[n]
realfirsttic = supposedtics [ n ] ;
if ( realfirsttic > = maketic )
{
// well we have sent all tics we will so use extrabandwidth
// to resent packet that are supposed lost (this is necessary since lost
// packet detection work when we have received packet with firsttic > neededtic
// (getpacket servertics case)
DEBFILE ( va ( " Nothing to send node %u mak=%u sup=%u net=%u \n " ,
n , maketic , supposedtics [ n ] , nettics [ n ] ) ) ;
realfirsttic = nettics [ n ] ;
if ( realfirsttic > = maketic | | ( I_GetTime ( ) + n ) & 3 )
// all tic are ok
continue ;
DEBFILE ( va ( " Sent %d anyway \n " , realfirsttic ) ) ;
}
if ( realfirsttic < firstticstosend )
realfirsttic = firstticstosend ;
// compute the length of the packet and cut it if too large
packsize = BASESERVERTICSSIZE ;
for ( i = realfirsttic ; i < lasttictosend ; i + + )
{
packsize + = sizeof ( ticcmd_t ) * doomcom - > numslots ;
packsize + = TotalTextCmdPerTic ( i ) ;
if ( packsize > software_MAXPACKETLENGTH )
{
DEBFILE ( va ( " packet too large (%s) at tic %d (should be from %d to %d) \n " ,
sizeu1 ( packsize ) , i , realfirsttic , lasttictosend ) ) ;
lasttictosend = i ;
// too bad: too much player have send extradata and there is too
// much data in one tic.
// To avoid it put the data on the next tic. (see getpacket
// textcmd case) but when numplayer changes the computation can be different
if ( lasttictosend = = realfirsttic )
{
if ( packsize > MAXPACKETLENGTH )
I_Error ( " Too many players: can't send %s data for %d players to node %d \n "
" Well sorry nobody is perfect.... \n " ,
sizeu1 ( packsize ) , doomcom - > numslots , n ) ;
else
{
lasttictosend + + ; // send it anyway!
DEBFILE ( " sending it anyway \n " ) ;
}
}
break ;
}
}
// Send the tics
netbuffer - > packettype = PT_SERVERTICS ;
netbuffer - > u . serverpak . starttic = ( UINT8 ) realfirsttic ;
netbuffer - > u . serverpak . numtics = ( UINT8 ) ( lasttictosend - realfirsttic ) ;
netbuffer - > u . serverpak . numslots = ( UINT8 ) SHORT ( doomcom - > numslots ) ;
bufpos = ( UINT8 * ) & netbuffer - > u . serverpak . cmds ;
for ( i = realfirsttic ; i < lasttictosend ; i + + )
{
bufpos = G_DcpyTiccmd ( bufpos , netcmds [ i % BACKUPTICS ] , doomcom - > numslots * sizeof ( ticcmd_t ) ) ;
}
// add textcmds
for ( i = realfirsttic ; i < lasttictosend ; i + + )
{
ntextcmd = bufpos + + ;
* ntextcmd = 0 ;
for ( j = 0 ; j < MAXPLAYERS ; j + + )
{
UINT8 * textcmd = D_GetExistingTextcmd ( i , j ) ;
INT32 size = textcmd ? textcmd [ 0 ] : 0 ;
if ( ( ! j | | playeringame [ j ] ) & & size )
{
( * ntextcmd ) + + ;
WRITEUINT8 ( bufpos , j ) ;
M_Memcpy ( bufpos , textcmd , size + 1 ) ;
bufpos + = size + 1 ;
}
}
}
packsize = bufpos - ( UINT8 * ) & ( netbuffer - > u ) ;
HSendPacket ( n , false , 0 , packsize ) ;
// when tic are too large, only one tic is sent so don't go backward!
if ( lasttictosend - doomcom - > extratics > realfirsttic )
supposedtics [ n ] = lasttictosend - doomcom - > extratics ;
else
supposedtics [ n ] = lasttictosend ;
if ( supposedtics [ n ] < nettics [ n ] ) supposedtics [ n ] = nettics [ n ] ;
}
// node 0 is me!
supposedtics [ 0 ] = maketic ;
}
//
// TryRunTics
//
static void Local_Maketic ( INT32 realtics )
{
I_OsPolling ( ) ; // I_Getevent
D_ProcessEvents ( ) ; // menu responder, cons responder,
2019-11-21 05:16:09 +00:00
// game responder calls HU_Responder, AM_Responder,
2014-03-15 16:59:03 +00:00
// and G_MapEventsToControls
if ( ! dedicated ) rendergametic = gametic ;
// translate inputs (keyboard/mouse/joystick) into game controls
2019-12-08 19:27:11 +00:00
G_BuildTiccmd ( & localcmds , realtics , 1 ) ;
2014-03-15 16:59:03 +00:00
if ( splitscreen | | botingame )
2019-12-08 19:27:11 +00:00
G_BuildTiccmd ( & localcmds2 , realtics , 2 ) ;
2014-03-15 16:59:03 +00:00
localcmds . angleturn | = TICCMD_RECEIVED ;
}
void SV_SpawnPlayer ( INT32 playernum , INT32 x , INT32 y , angle_t angle )
{
tic_t tic ;
2018-01-16 15:21:49 +00:00
UINT8 numadjust = 0 ;
2014-03-15 16:59:03 +00:00
( void ) x ;
( void ) y ;
// Revisionist history: adjust the angles in the ticcmds received
// for this player, because they actually preceded the player
// spawning, but will be applied afterwards.
for ( tic = server ? maketic : ( neededtic - 1 ) ; tic > = gametic ; tic - - )
2018-01-16 15:21:49 +00:00
{
if ( numadjust + + = = BACKUPTICS )
{
DEBFILE ( va ( " SV_SpawnPlayer: All netcmds for player %d adjusted! \n " , playernum ) ) ;
// We already adjusted them all, waste of time doing the same thing over and over
// This shouldn't happen normally though, either gametic was 0 (which is handled now anyway)
// or maketic >= gametic + BACKUPTICS
// -- Monster Iestyn 16/01/18
break ;
}
2014-03-15 16:59:03 +00:00
netcmds [ tic % BACKUPTICS ] [ playernum ] . angleturn = ( INT16 ) ( ( angle > > 16 ) | TICCMD_RECEIVED ) ;
2018-01-16 15:21:49 +00:00
if ( ! tic ) // failsafe for gametic == 0 -- Monster Iestyn 16/01/18
break ;
}
2014-03-15 16:59:03 +00:00
}
// create missed tic
static void SV_Maketic ( void )
{
INT32 j ;
for ( j = 0 ; j < MAXNETNODES ; j + + )
if ( playerpernode [ j ] )
{
INT32 player = nodetoplayer [ j ] ;
if ( ( netcmds [ maketic % BACKUPTICS ] [ player ] . angleturn & TICCMD_RECEIVED ) = = 0 )
{ // we didn't receive this tic
INT32 i ;
DEBFILE ( va ( " MISS tic%4d for node %d \n " , maketic , j ) ) ;
# if defined(PARANOIA) && 0
CONS_Debug ( DBG_NETPLAY , " Client Misstic %d \n " , maketic ) ;
# endif
// copy the old tic
for ( i = 0 ; i < playerpernode [ j ] ; i + + , player = nodetoplayer2 [ j ] )
{
netcmds [ maketic % BACKUPTICS ] [ player ] = netcmds [ ( maketic - 1 ) % BACKUPTICS ] [ player ] ;
netcmds [ maketic % BACKUPTICS ] [ player ] . angleturn & = ~ TICCMD_RECEIVED ;
}
}
}
// all tic are now proceed make the next
maketic + + ;
}
void TryRunTics ( tic_t realtics )
{
// the machine has lagged but it is not so bad
if ( realtics > TICRATE / 7 ) // FIXME: consistency failure!!
{
if ( server )
realtics = 1 ;
else
realtics = TICRATE / 7 ;
}
if ( singletics )
realtics = 1 ;
if ( realtics > = 1 )
{
2019-04-14 22:17:47 +00:00
COM_BufTicker ( ) ;
2014-03-15 16:59:03 +00:00
if ( mapchangepending )
D_MapChange ( - 1 , 0 , ultimatemode , false , 2 , false , fromlevelselect ) ; // finish the map change
}
NetUpdate ( ) ;
if ( demoplayback )
{
neededtic = gametic + ( realtics * cv_playbackspeed . value ) ;
// start a game after a demo
maketic + = realtics ;
firstticstosend = maketic ;
tictoclear = firstticstosend ;
}
GetPackets ( ) ;
# ifdef DEBUGFILE
if ( debugfile & & ( realtics | | neededtic > gametic ) )
{
//SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5.
//Shut up stupid warning!
fprintf ( debugfile , " ------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d \n " ,
realtics , neededtic , gametic , debugload ) ;
debugload = 100000 ;
}
# endif
2014-03-21 18:42:55 +00:00
if ( player_joining )
return ;
2019-10-16 19:24:30 +00:00
if ( neededtic > gametic & & ! resynch_local_inprogress )
2014-03-15 16:59:03 +00:00
{
if ( advancedemo )
2018-12-29 09:51:00 +00:00
{
if ( timedemo_quit )
COM_ImmedExecute ( " quit " ) ;
else
D_StartTitle ( ) ;
}
2014-03-15 16:59:03 +00:00
else
// run the count * tics
while ( neededtic > gametic )
{
DEBFILE ( va ( " ============ Running tic %d (local %d) \n " , gametic , localgametic ) ) ;
G_Ticker ( ( gametic % NEWTICRATERATIO ) = = 0 ) ;
ExtraDataTicker ( ) ;
gametic + + ;
consistancy [ gametic % BACKUPTICS ] = Consistancy ( ) ;
}
}
}
2019-11-18 01:22:47 +00:00
/*
Ping Update except better :
We call this once per second and check for people ' s pings . If their ping happens to be too high , we increment some timer and kick them out .
If they ' re not lagging , decrement the timer by 1. Of course , reset all of this if they leave .
*/
static INT32 pingtimeout [ MAXPLAYERS ] ;
2014-03-15 16:59:03 +00:00
static inline void PingUpdate ( void )
{
INT32 i ;
boolean laggers [ MAXPLAYERS ] ;
UINT8 numlaggers = 0 ;
memset ( laggers , 0 , sizeof ( boolean ) * MAXPLAYERS ) ;
netbuffer - > packettype = PT_PING ;
//check for ping limit breakage.
if ( cv_maxping . value )
{
2017-01-13 19:53:52 +00:00
for ( i = 1 ; i < MAXPLAYERS ; i + + )
2014-03-15 16:59:03 +00:00
{
if ( playeringame [ i ] & & ( realpingtable [ i ] / pingmeasurecount > ( unsigned ) cv_maxping . value ) )
{
if ( players [ i ] . jointime > 30 * TICRATE )
laggers [ i ] = true ;
numlaggers + + ;
}
2019-11-18 01:22:47 +00:00
else
pingtimeout [ i ] = 0 ;
2014-03-15 16:59:03 +00:00
}
//kick lagging players... unless everyone but the server's ping sucks.
//in that case, it is probably the server's fault.
if ( numlaggers < D_NumPlayers ( ) - 1 )
{
2017-01-13 19:53:52 +00:00
for ( i = 1 ; i < MAXPLAYERS ; i + + )
2014-03-15 16:59:03 +00:00
{
if ( playeringame [ i ] & & laggers [ i ] )
{
2019-11-18 01:22:47 +00:00
pingtimeout [ i ] + + ;
if ( pingtimeout [ i ] > cv_pingtimeout . value )
// ok your net has been bad for too long, you deserve to die.
{
pingtimeout [ i ] = 0 ;
2020-01-12 18:43:04 +00:00
SendKick ( i , KICK_MSG_PING_HIGH ) ;
2019-11-18 01:22:47 +00:00
}
2014-03-15 16:59:03 +00:00
}
2019-11-18 01:22:47 +00:00
/*
you aren ' t lagging ,
but you aren ' t free yet .
In case you ' ll keep spiking ,
we just make the timer go back down . ( Very unstable net must still get kicked ) .
*/
else
pingtimeout [ i ] = ( pingtimeout [ i ] = = 0 ? 0 : pingtimeout [ i ] - 1 ) ;
2014-03-15 16:59:03 +00:00
}
}
}
//make the ping packet and clear server data for next one
2017-01-13 19:53:52 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2014-03-15 16:59:03 +00:00
{
netbuffer - > u . pingtable [ i ] = realpingtable [ i ] / pingmeasurecount ;
//server takes a snapshot of the real ping for display.
//otherwise, pings fluctuate a lot and would be odd to look at.
playerpingtable [ i ] = realpingtable [ i ] / pingmeasurecount ;
realpingtable [ i ] = 0 ; //Reset each as we go.
}
2019-11-18 01:22:47 +00:00
// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
netbuffer - > u . pingtable [ MAXPLAYERS ] = cv_maxping . value ;
2014-03-15 16:59:03 +00:00
//send out our ping packets
2017-06-01 18:01:57 +00:00
for ( i = 0 ; i < MAXNETNODES ; i + + )
if ( nodeingame [ i ] )
2019-11-18 01:22:47 +00:00
HSendPacket ( i , true , 0 , sizeof ( INT32 ) * ( MAXPLAYERS + 1 ) ) ;
2014-03-15 16:59:03 +00:00
pingmeasurecount = 1 ; //Reset count
}
void NetUpdate ( void )
{
static tic_t gametime = 0 ;
static tic_t resptime = 0 ;
tic_t nowtime ;
INT32 i ;
INT32 realtics ;
nowtime = I_GetTime ( ) ;
realtics = nowtime - gametime ;
if ( realtics < = 0 ) // nothing new to update
return ;
if ( realtics > 5 )
{
if ( server )
realtics = 1 ;
else
realtics = 5 ;
}
gametime = nowtime ;
if ( server )
{
2019-11-18 01:22:47 +00:00
if ( netgame & & ! ( gametime % 35 ) ) // update once per second.
2017-06-01 19:43:24 +00:00
PingUpdate ( ) ;
2014-03-15 16:59:03 +00:00
// update node latency values so we can take an average later.
2017-06-01 18:01:57 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2014-03-15 16:59:03 +00:00
if ( playeringame [ i ] )
2017-06-01 18:01:57 +00:00
realpingtable [ i ] + = G_TicsToMilliseconds ( GetLag ( playernode [ i ] ) ) ;
2014-03-15 16:59:03 +00:00
pingmeasurecount + + ;
}
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-15 16:59:03 +00:00
maketic = neededtic ;
Local_Maketic ( realtics ) ; // make local tic, and call menu?
if ( server )
CL_SendClientCmd ( ) ; // send it
2017-09-29 18:46:23 +00:00
2014-03-15 16:59:03 +00:00
GetPackets ( ) ; // get packet from client or from server
2017-09-29 18:46:23 +00:00
2014-03-15 16:59:03 +00:00
// client send the command after a receive of the server
// the server send before because in single player is beter
2016-12-31 18:26:33 +00:00
MasterClient_Ticker ( ) ; // Acking the Master Server
2014-03-15 16:59:03 +00:00
2017-01-13 19:53:52 +00:00
if ( client )
2014-03-21 18:42:55 +00:00
{
if ( ! resynch_local_inprogress )
2016-12-31 18:26:33 +00:00
CL_SendClientCmd ( ) ; // Send tic cmd
2014-03-21 18:42:55 +00:00
hu_resynching = resynch_local_inprogress ;
}
2014-03-15 16:59:03 +00:00
else
{
if ( ! demoplayback )
{
INT32 counts ;
2014-03-21 18:42:55 +00:00
hu_resynching = false ;
2014-03-15 16:59:03 +00:00
firstticstosend = gametic ;
for ( i = 0 ; i < MAXNETNODES ; i + + )
if ( nodeingame [ i ] & & nettics [ i ] < firstticstosend )
firstticstosend = nettics [ i ] ;
// Don't erase tics not acknowledged
counts = realtics ;
2014-03-21 18:42:55 +00:00
for ( i = 0 ; i < MAXNETNODES ; + + i )
if ( resynch_inprogress [ i ] )
{
2019-10-16 19:24:30 +00:00
if ( ! nodeingame [ i ] | | nettics [ i ] = = gametic )
{
SV_SendResynch ( i ) ;
counts = - 666 ;
}
else
counts = 0 ; // Let the client catch up with the server
2014-03-21 18:42:55 +00:00
}
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
// Do not make tics while resynching
2014-03-21 18:42:55 +00:00
if ( counts ! = - 666 )
{
if ( maketic + counts > = firstticstosend + BACKUPTICS )
counts = firstticstosend + BACKUPTICS - maketic - 1 ;
for ( i = 0 ; i < counts ; i + + )
2016-12-31 18:26:33 +00:00
SV_Maketic ( ) ; // Create missed tics and increment maketic
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
for ( ; tictoclear < firstticstosend ; tictoclear + + ) // Clear only when acknowledged
D_Clearticcmd ( tictoclear ) ; // Clear the maketic the new tic
2014-03-15 16:59:03 +00:00
2014-03-21 18:42:55 +00:00
SV_SendTics ( ) ;
2014-03-15 16:59:03 +00:00
2016-12-31 18:26:33 +00:00
neededtic = maketic ; // The server is a client too
2014-03-21 18:42:55 +00:00
}
else
hu_resynching = true ;
2014-03-15 16:59:03 +00:00
}
}
Net_AckTicker ( ) ;
2017-01-13 19:53:52 +00:00
// Handle timeouts to prevent definitive freezes from happenning
if ( server )
for ( i = 1 ; i < MAXNETNODES ; i + + )
if ( nodeingame [ i ] & & freezetimeout [ i ] < I_GetTime ( ) )
Net_ConnectionTimeout ( i ) ;
2014-03-15 16:59:03 +00:00
nowtime / = NEWTICRATERATIO ;
if ( nowtime > resptime )
{
resptime = nowtime ;
M_Ticker ( ) ;
CON_Ticker ( ) ;
}
2016-12-31 18:26:33 +00:00
SV_FileSendTicker ( ) ;
2014-03-15 16:59:03 +00:00
}
/** Returns the number of players playing.
* \ return Number of players . Can be zero if we ' re running a : : dedicated
* server .
* \ author Graue < graue @ oceanbase . org >
*/
INT32 D_NumPlayers ( void )
{
INT32 num = 0 , ix ;
for ( ix = 0 ; ix < MAXPLAYERS ; ix + + )
if ( playeringame [ ix ] )
num + + ;
return num ;
}
tic_t GetLag ( INT32 node )
{
return gametic - nettics [ node ] ;
}
2019-07-15 22:39:58 +00:00
void D_MD5PasswordPass ( const UINT8 * buffer , size_t len , const char * salt , void * dest )
{
# ifdef NOMD5
( void ) buffer ;
( void ) len ;
( void ) salt ;
memset ( dest , 0 , 16 ) ;
# else
2019-08-20 12:54:48 +00:00
char tmpbuf [ 256 ] ;
2019-07-15 22:39:58 +00:00
const size_t sl = strlen ( salt ) ;
if ( len > 256 - sl )
len = 256 - sl ;
memcpy ( tmpbuf , buffer , len ) ;
memmove ( & tmpbuf [ len ] , salt , sl ) ;
//strcpy(&tmpbuf[len], salt);
len + = strlen ( salt ) ;
if ( len < 256 )
memset ( & tmpbuf [ len ] , 0 , 256 - len ) ;
// Yes, we intentionally md5 the ENTIRE buffer regardless of size...
md5_buffer ( tmpbuf , 256 , dest ) ;
# endif
}