mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-02-16 17:11:33 +00:00
Move tic and net command handling to new files
This commit is contained in:
parent
fc41dd78f4
commit
0ade3ae0bc
21 changed files with 955 additions and 877 deletions
|
@ -20,6 +20,7 @@
|
|||
#include "../i_system.h"
|
||||
#include "../g_game.h"
|
||||
#include "../netcode/d_netfil.h"
|
||||
#include "../netcode/net_command.h"
|
||||
#include "../lua_libs.h"
|
||||
#include "../byteptr.h"
|
||||
#include "../lua_script.h"
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "p_saveg.h"
|
||||
#include "g_game.h" // for player_names
|
||||
#include "netcode/d_netcmd.h"
|
||||
#include "netcode/net_command.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "p_setup.h"
|
||||
#include "lua_script.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "d_main.h"
|
||||
#include "d_player.h"
|
||||
#include "netcode/d_clisrv.h"
|
||||
#include "netcode/net_command.h"
|
||||
#include "f_finale.h"
|
||||
#include "p_setup.h"
|
||||
#include "p_saveg.h"
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "m_misc.h" // word jumping
|
||||
|
||||
#include "netcode/d_clisrv.h"
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
#include "g_game.h"
|
||||
#include "g_input.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "g_game.h"
|
||||
#include "byteptr.h"
|
||||
#include "z_zone.h"
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
#include "lua_script.h"
|
||||
#include "lua_libs.h"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
d_clisrv.c
|
||||
server_connection.c
|
||||
client_connection.c
|
||||
tic_command.c
|
||||
net_command.c
|
||||
d_net.c
|
||||
d_netcmd.c
|
||||
d_netfil.c
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
#include "../m_perfstats.h"
|
||||
#include "server_connection.h"
|
||||
#include "client_connection.h"
|
||||
#include "tic_command.h"
|
||||
#include "net_command.h"
|
||||
|
||||
//
|
||||
// NETWORKING
|
||||
|
@ -86,11 +88,9 @@ UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will
|
|||
UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
|
||||
tic_t servermaxping = 800; // server's max ping. Defaults to 800
|
||||
|
||||
static tic_t firstticstosend; // min of the nettics
|
||||
static tic_t tictoclear = 0; // optimize d_clearticcmd
|
||||
tic_t maketic;
|
||||
|
||||
static INT16 consistancy[BACKUPTICS];
|
||||
INT16 consistancy[BACKUPTICS];
|
||||
|
||||
UINT8 hu_redownloadinggamestate = 0;
|
||||
|
||||
|
@ -101,14 +101,9 @@ UINT8 adminpassmd5[16];
|
|||
boolean adminpasswordset = false;
|
||||
|
||||
// Client specific
|
||||
static ticcmd_t localcmds;
|
||||
static ticcmd_t localcmds2;
|
||||
static boolean cl_packetmissed;
|
||||
// here it is for the secondary local player (splitscreen)
|
||||
static boolean cl_redownloadinggamestate = false;
|
||||
|
||||
static UINT8 localtextcmd[MAXTEXTCMD];
|
||||
static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
|
||||
tic_t neededtic;
|
||||
SINT8 servernode = 0; // the number of the server node
|
||||
|
||||
|
@ -116,385 +111,19 @@ SINT8 servernode = 0; // the number of the server node
|
|||
/// \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};
|
||||
|
||||
|
||||
consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
|
||||
|
||||
static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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)
|
||||
UINT16 software_MAXPACKETLENGTH;
|
||||
|
||||
/** Guesses the full value of a tic from its lowest byte, for a specific node
|
||||
*
|
||||
* \param low The lowest byte of the tic value
|
||||
* \param node The node to deduce the tic for
|
||||
* \return The full tic value
|
||||
*
|
||||
*/
|
||||
tic_t ExpandTics(INT32 low, INT32 node)
|
||||
{
|
||||
INT32 delta;
|
||||
|
||||
delta = low - (netnodes[node].tic & UINT8_MAX);
|
||||
|
||||
if (delta >= -64 && delta <= 64)
|
||||
return (netnodes[node].tic & ~UINT8_MAX) + low;
|
||||
else if (delta > 64)
|
||||
return (netnodes[node].tic & ~UINT8_MAX) - 256 + low;
|
||||
else //if (delta < -64)
|
||||
return (netnodes[node].tic & ~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)
|
||||
I_Error("Command id %d too big", id);
|
||||
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++;
|
||||
DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
|
||||
(listnetxcmd[id])(&curpos, i);
|
||||
DEBFILE("done\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 acknowledged,
|
||||
// because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it
|
||||
if (client)
|
||||
D_FreeTextcmd(gametic);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void SendKick(UINT8 playernum, UINT8 msg)
|
||||
{
|
||||
UINT8 buf[2];
|
||||
|
||||
if (!(server && cv_rejointimeout.value))
|
||||
msg &= ~KICK_MSG_KEEP_BODY;
|
||||
|
||||
buf[0] = playernum;
|
||||
buf[1] = msg;
|
||||
SendNetXCmd(XD_KICK, &buf, 2);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// 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
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static INT16 Consistancy(void);
|
||||
|
||||
#define SAVEGAMESIZE (768*1024)
|
||||
|
||||
static boolean SV_ResendingSavegameToAnyone(void)
|
||||
boolean SV_ResendingSavegameToAnyone(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
|
@ -2005,22 +1634,6 @@ void SV_StartSinglePlayerServer(void)
|
|||
multiplayer = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** Called when a PT_SERVERSHUTDOWN packet is received
|
||||
*
|
||||
* \param node The packet sender (should be the server)
|
||||
|
@ -2051,168 +1664,6 @@ static void HandleTimeout(SINT8 node)
|
|||
M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
static void PT_ClientCmd(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
tic_t realend, realstart;
|
||||
|
||||
if (client)
|
||||
return;
|
||||
|
||||
// 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, node);
|
||||
realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
|
||||
|
||||
if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
|
||||
|| netnodes[node].supposedtic < realend)
|
||||
{
|
||||
netnodes[node].supposedtic = realend;
|
||||
}
|
||||
// Discard out of order packet
|
||||
if (netnodes[node].tic > realend)
|
||||
{
|
||||
DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the nettics
|
||||
netnodes[node].tic = realend;
|
||||
|
||||
// Don't do anything for packets of type NODEKEEPALIVE?
|
||||
if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
|
||||
return;
|
||||
|
||||
// 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?
|
||||
netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
|
||||
|
||||
// Copy ticcmd
|
||||
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
|
||||
|
||||
// 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);
|
||||
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Splitscreen cmd
|
||||
if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
|
||||
&& netnodes[node].player2 >= 0)
|
||||
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
|
||||
&netbuffer->u.client2pak.cmd2, 1);
|
||||
|
||||
// Check player consistancy during the level
|
||||
if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
|
||||
&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
|
||||
&& !SV_ResendingSavegameToAnyone()
|
||||
&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
|
||||
{
|
||||
if (cv_resynchattempts.value)
|
||||
{
|
||||
// Tell the client we are about to resend them the gamestate
|
||||
netbuffer->packettype = PT_WILLRESENDGAMESTATE;
|
||||
HSendPacket(node, true, 0, 0);
|
||||
|
||||
netnodes[node].resendingsavegame = true;
|
||||
|
||||
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)));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
|
||||
netconsole, realstart, consistancy[realstart%BACKUPTICS],
|
||||
SHORT(netbuffer->u.clientpak.consistancy)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PT_TextCmd(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
if (client)
|
||||
return;
|
||||
|
||||
// splitscreen special
|
||||
if (netbuffer->packettype == PT_TEXTCMD2)
|
||||
netconsole = netnodes[node].player2;
|
||||
|
||||
if (netconsole < 0 || netconsole >= MAXPLAYERS)
|
||||
Net_UnAcknowledgePacket(node);
|
||||
else
|
||||
{
|
||||
size_t j;
|
||||
tic_t tic = maketic;
|
||||
UINT8 *textcmd;
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore if the textcmd size var is actually larger than it should be
|
||||
// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
|
||||
if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
|
||||
{
|
||||
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),
|
||||
node, netconsole));
|
||||
Net_UnAcknowledgePacket(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a buffer
|
||||
if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
|
||||
|
||||
DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
|
||||
tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
|
||||
|
||||
M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
|
||||
textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void PT_Login(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
(void)node;
|
||||
|
@ -2314,81 +1765,6 @@ static void PT_ReceivedGamestate(SINT8 node)
|
|||
netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
|
||||
}
|
||||
|
||||
static void PT_ServerTics(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
UINT8 *pak, *txtpak, numtxtpak;
|
||||
tic_t realend, realstart;
|
||||
|
||||
if (!netnodes[node].ingame)
|
||||
{
|
||||
// Do not remove my own server (we have just get a out of order packet)
|
||||
if (node != servernode)
|
||||
{
|
||||
DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
return;
|
||||
}
|
||||
|
||||
realstart = netbuffer->u.serverpak.starttic;
|
||||
realend = realstart + netbuffer->u.serverpak.numtics;
|
||||
|
||||
txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
|
||||
* netbuffer->u.serverpak.numtics];
|
||||
|
||||
if (realend > gametic + CLIENTBACKUPTICS)
|
||||
realend = gametic + CLIENTBACKUPTICS;
|
||||
cl_packetmissed = realstart > neededtic;
|
||||
|
||||
if (realstart <= neededtic && realend > neededtic)
|
||||
{
|
||||
tic_t i, j;
|
||||
pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
|
||||
|
||||
for (i = realstart; i < realend; i++)
|
||||
{
|
||||
// clear first
|
||||
D_Clearticcmd(i);
|
||||
|
||||
// copy the tics
|
||||
pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
|
||||
netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
|
||||
|
||||
// copy the textcmds
|
||||
numtxtpak = *txtpak++;
|
||||
for (j = 0; j < numtxtpak; j++)
|
||||
{
|
||||
INT32 k = *txtpak++; // playernum
|
||||
const size_t txtsize = txtpak[0]+1;
|
||||
|
||||
if (i >= gametic) // Don't copy old net commands
|
||||
M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
|
||||
txtpak += txtsize;
|
||||
}
|
||||
}
|
||||
|
||||
neededtic = realend;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBFILE(va("frame not in bound: %u\n", neededtic));
|
||||
/*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);*/
|
||||
}
|
||||
}
|
||||
|
||||
static void PT_Ping(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
// Only accept PT_PING from the server.
|
||||
|
@ -2602,7 +1978,7 @@ void GetPackets(void)
|
|||
// no more use random generator, because at very first tic isn't yet synchronized
|
||||
// Note: It is called consistAncy on purpose.
|
||||
//
|
||||
static INT16 Consistancy(void)
|
||||
INT16 Consistancy(void)
|
||||
{
|
||||
INT32 i;
|
||||
UINT32 ret = 0;
|
||||
|
@ -2708,239 +2084,11 @@ static INT16 Consistancy(void)
|
|||
return (INT16)(ret & 0xFFFF);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Send PT_NODEKEEPALIVE packet
|
||||
netbuffer->packettype += 4;
|
||||
packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
|
||||
HSendPacket(servernode, false, 0, packetsize);
|
||||
}
|
||||
else if (gamestate != GS_NULL && (addedtogame || dedicated))
|
||||
{
|
||||
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
|
||||
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
|
||||
|
||||
// Send a special packet with 2 cmd for splitscreen
|
||||
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);
|
||||
}
|
||||
|
||||
if (cl_mode == CL_CONNECTED || dedicated)
|
||||
{
|
||||
// Send extra data if needed
|
||||
if (localtextcmd[0])
|
||||
{
|
||||
netbuffer->packettype = PT_TEXTCMD;
|
||||
M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
|
||||
// All extra data have been sent
|
||||
if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
|
||||
localtextcmd[0] = 0;
|
||||
}
|
||||
|
||||
// Send extra data if needed for player 2 (splitscreen)
|
||||
if (localtextcmd2[0])
|
||||
{
|
||||
netbuffer->packettype = PT_TEXTCMD2;
|
||||
M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
|
||||
// All extra data have been sent
|
||||
if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
|
||||
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 netnodes[n].supposedtic, max packet size and maketic
|
||||
for (n = 1; n < MAXNETNODES; n++)
|
||||
if (netnodes[n].ingame)
|
||||
{
|
||||
// assert netnodes[n].supposedtic>=netnodes[n].tic
|
||||
realfirsttic = netnodes[n].supposedtic;
|
||||
lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
|
||||
|
||||
if (realfirsttic >= lasttictosend)
|
||||
{
|
||||
// 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, netnodes[n].supposedtic, netnodes[n].tic));
|
||||
realfirsttic = netnodes[n].tic;
|
||||
if (realfirsttic >= lasttictosend || (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 = 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)
|
||||
netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
|
||||
else
|
||||
netnodes[n].supposedtic = lasttictosend;
|
||||
if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
|
||||
}
|
||||
// node 0 is me!
|
||||
netnodes[0].supposedtic = maketic;
|
||||
}
|
||||
|
||||
//
|
||||
// TryRunTics
|
||||
//
|
||||
static void Local_Maketic(INT32 realtics)
|
||||
{
|
||||
I_OsPolling(); // I_Getevent
|
||||
D_ProcessEvents(); // menu responder, cons responder,
|
||||
// game responder calls HU_Responder, AM_Responder,
|
||||
// and G_MapEventsToControls
|
||||
if (!dedicated) rendergametic = gametic;
|
||||
// translate inputs (keyboard/mouse/gamepad) into game controls
|
||||
G_BuildTiccmd(&localcmds, realtics, 1);
|
||||
if (splitscreen || botingame)
|
||||
G_BuildTiccmd(&localcmds2, realtics, 2);
|
||||
|
||||
localcmds.angleturn |= TICCMD_RECEIVED;
|
||||
localcmds2.angleturn |= TICCMD_RECEIVED;
|
||||
}
|
||||
|
||||
// create missed tic
|
||||
static void SV_Maketic(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
// We didn't receive this tic
|
||||
if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
|
||||
{
|
||||
ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i];
|
||||
ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
|
||||
|
||||
if (players[i].quittime)
|
||||
{
|
||||
// Copy the angle/aiming from the previous tic
|
||||
// and empty the other inputs
|
||||
memset(ticcmd, 0, sizeof(netcmds[0][0]));
|
||||
ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED;
|
||||
ticcmd->aiming = prevticcmd->aiming;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBFILE(va("MISS tic%4d for player %d\n", maketic, i));
|
||||
// Copy the input from the previous tic
|
||||
*ticcmd = *prevticcmd;
|
||||
ticcmd->angleturn &= ~TICCMD_RECEIVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all tic are now proceed make the next
|
||||
maketic++;
|
||||
}
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
boolean TryRunTics(tic_t realtics)
|
||||
{
|
||||
|
@ -3049,12 +2197,6 @@ boolean TryRunTics(tic_t realtics)
|
|||
return ticking;
|
||||
}
|
||||
|
||||
/*
|
||||
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];
|
||||
|
||||
static inline void PingUpdate(void)
|
||||
|
|
|
@ -378,6 +378,7 @@ extern boolean acceptnewnode;
|
|||
extern SINT8 servernode;
|
||||
extern tic_t maketic;
|
||||
extern tic_t neededtic;
|
||||
extern INT16 consistancy[BACKUPTICS];
|
||||
|
||||
void Command_Ping_f(void);
|
||||
extern tic_t connectiontimeout;
|
||||
|
@ -391,20 +392,15 @@ extern consvar_t cv_resynchattempts, cv_blamecfail;
|
|||
extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
|
||||
|
||||
// Used in d_net, the only dependence
|
||||
tic_t ExpandTics(INT32 low, INT32 node);
|
||||
void D_ClientServerInit(void);
|
||||
|
||||
// Initialise the other field
|
||||
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
|
||||
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
|
||||
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
|
||||
void SendKick(UINT8 playernum, UINT8 msg);
|
||||
|
||||
// Create any new ticcmds and broadcast to other players.
|
||||
void NetUpdate(void);
|
||||
|
||||
void GetPackets(void);
|
||||
void ResetNode(INT32 node);
|
||||
INT16 Consistancy(void);
|
||||
boolean SV_ResendingSavegameToAnyone(void);
|
||||
|
||||
void SV_StartSinglePlayerServer(void);
|
||||
void SV_SpawnServer(void);
|
||||
|
@ -440,10 +436,8 @@ extern char motd[254], server_context[8];
|
|||
extern UINT8 playernode[MAXPLAYERS];
|
||||
|
||||
INT32 D_NumPlayers(void);
|
||||
void D_ResetTiccmds(void);
|
||||
|
||||
tic_t GetLag(INT32 node);
|
||||
UINT8 GetFreeXCmdSize(void);
|
||||
|
||||
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "../w_wad.h"
|
||||
#include "d_netfil.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "tic_command.h"
|
||||
#include "../z_zone.h"
|
||||
#include "i_tcp.h"
|
||||
#include "../d_main.h" // srb2home
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "../m_cheat.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "server_connection.h"
|
||||
#include "net_command.h"
|
||||
#include "d_net.h"
|
||||
#include "../v_video.h"
|
||||
#include "../d_main.h"
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "d_net.h"
|
||||
#include "../w_wad.h"
|
||||
#include "d_netfil.h"
|
||||
#include "net_command.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../byteptr.h"
|
||||
#include "../p_setup.h"
|
||||
|
|
315
src/netcode/net_command.c
Normal file
315
src/netcode/net_command.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 by Sonic Team Junior.
|
||||
//
|
||||
// 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 net_command.c
|
||||
/// \brief Net command handling
|
||||
|
||||
#include "net_command.h"
|
||||
#include "tic_command.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "i_net.h"
|
||||
#include "../g_game.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../doomtype.h"
|
||||
|
||||
textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
|
||||
UINT8 localtextcmd[MAXTEXTCMD];
|
||||
UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
|
||||
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)
|
||||
I_Error("Command id %d too big", id);
|
||||
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
|
||||
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
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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++;
|
||||
DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
|
||||
(listnetxcmd[id])(&curpos, i);
|
||||
DEBFILE("done\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 acknowledged,
|
||||
// because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it
|
||||
if (client)
|
||||
D_FreeTextcmd(gametic);
|
||||
}
|
||||
|
||||
// used at txtcmds received to check packetsize bound
|
||||
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;
|
||||
}
|
||||
|
||||
void PT_TextCmd(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
if (client)
|
||||
return;
|
||||
|
||||
// splitscreen special
|
||||
if (netbuffer->packettype == PT_TEXTCMD2)
|
||||
netconsole = netnodes[node].player2;
|
||||
|
||||
if (netconsole < 0 || netconsole >= MAXPLAYERS)
|
||||
Net_UnAcknowledgePacket(node);
|
||||
else
|
||||
{
|
||||
size_t j;
|
||||
tic_t tic = maketic;
|
||||
UINT8 *textcmd;
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore if the textcmd size var is actually larger than it should be
|
||||
// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
|
||||
if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
|
||||
{
|
||||
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),
|
||||
node, netconsole));
|
||||
Net_UnAcknowledgePacket(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a buffer
|
||||
if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
|
||||
|
||||
DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
|
||||
tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
|
||||
|
||||
M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
|
||||
textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
|
||||
}
|
||||
}
|
||||
|
||||
void SendKick(UINT8 playernum, UINT8 msg)
|
||||
{
|
||||
UINT8 buf[2];
|
||||
|
||||
if (!(server && cv_rejointimeout.value))
|
||||
msg &= ~KICK_MSG_KEEP_BODY;
|
||||
|
||||
buf[0] = playernum;
|
||||
buf[1] = msg;
|
||||
SendNetXCmd(XD_KICK, &buf, 2);
|
||||
}
|
62
src/netcode/net_command.h
Normal file
62
src/netcode/net_command.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 by Sonic Team Junior.
|
||||
//
|
||||
// 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 net_command.h
|
||||
/// \brief Net command handling
|
||||
|
||||
#ifndef __D_NET_COMMAND__
|
||||
#define __D_NET_COMMAND__
|
||||
|
||||
#include "d_clisrv.h"
|
||||
#include "../doomtype.h"
|
||||
|
||||
// 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;
|
||||
|
||||
extern textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE];
|
||||
|
||||
extern UINT8 localtextcmd[MAXTEXTCMD];
|
||||
extern UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
|
||||
|
||||
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
|
||||
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
|
||||
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
|
||||
|
||||
UINT8 GetFreeXCmdSize(void);
|
||||
void D_FreeTextcmd(tic_t tic);
|
||||
|
||||
// Gets the buffer for the specified ticcmd, or NULL if there isn't one
|
||||
UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum);
|
||||
|
||||
// Gets the buffer for the specified ticcmd, creating one if necessary
|
||||
UINT8* D_GetTextcmd(tic_t tic, INT32 playernum);
|
||||
|
||||
void ExtraDataTicker(void);
|
||||
|
||||
// used at txtcmds received to check packetsize bound
|
||||
size_t TotalTextCmdPerTic(tic_t tic);
|
||||
|
||||
void PT_TextCmd(SINT8 node, INT32 netconsole);
|
||||
void SendKick(UINT8 playernum, UINT8 msg);
|
||||
|
||||
#endif
|
|
@ -15,6 +15,7 @@
|
|||
#include "d_clisrv.h"
|
||||
#include "d_netfil.h"
|
||||
#include "mserv.h"
|
||||
#include "net_command.h"
|
||||
#include "../byteptr.h"
|
||||
#include "../g_game.h"
|
||||
#include "../g_state.h"
|
||||
|
|
504
src/netcode/tic_command.c
Normal file
504
src/netcode/tic_command.c
Normal file
|
@ -0,0 +1,504 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 by Sonic Team Junior.
|
||||
//
|
||||
// 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 tic_command.c
|
||||
/// \brief Tic command handling
|
||||
|
||||
#include "tic_command.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "net_command.h"
|
||||
#include "client_connection.h"
|
||||
#include "i_net.h"
|
||||
#include "../d_main.h"
|
||||
#include "../g_game.h"
|
||||
#include "../i_system.h"
|
||||
#include "../i_time.h"
|
||||
#include "../byteptr.h"
|
||||
#include "../doomstat.h"
|
||||
#include "../doomtype.h"
|
||||
|
||||
tic_t firstticstosend; // min of the nettics
|
||||
tic_t tictoclear = 0; // optimize d_clearticcmd
|
||||
ticcmd_t localcmds;
|
||||
ticcmd_t localcmds2;
|
||||
boolean cl_packetmissed;
|
||||
ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** Guesses the full value of a tic from its lowest byte, for a specific node
|
||||
*
|
||||
* \param low The lowest byte of the tic value
|
||||
* \param node The node to deduce the tic for
|
||||
* \return The full tic value
|
||||
*
|
||||
*/
|
||||
tic_t ExpandTics(INT32 low, INT32 node)
|
||||
{
|
||||
INT32 delta;
|
||||
|
||||
delta = low - (netnodes[node].tic & UINT8_MAX);
|
||||
|
||||
if (delta >= -64 && delta <= 64)
|
||||
return (netnodes[node].tic & ~UINT8_MAX) + low;
|
||||
else if (delta > 64)
|
||||
return (netnodes[node].tic & ~UINT8_MAX) - 256 + low;
|
||||
else //if (delta < -64)
|
||||
return (netnodes[node].tic & ~UINT8_MAX) + 256 + low;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void PT_ClientCmd(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
tic_t realend, realstart;
|
||||
|
||||
if (client)
|
||||
return;
|
||||
|
||||
// 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, node);
|
||||
realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
|
||||
|
||||
if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
|
||||
|| netnodes[node].supposedtic < realend)
|
||||
{
|
||||
netnodes[node].supposedtic = realend;
|
||||
}
|
||||
// Discard out of order packet
|
||||
if (netnodes[node].tic > realend)
|
||||
{
|
||||
DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the nettics
|
||||
netnodes[node].tic = realend;
|
||||
|
||||
// Don't do anything for packets of type NODEKEEPALIVE?
|
||||
if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
|
||||
return;
|
||||
|
||||
// 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?
|
||||
netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
|
||||
|
||||
// Copy ticcmd
|
||||
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
|
||||
|
||||
// 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);
|
||||
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Splitscreen cmd
|
||||
if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
|
||||
&& netnodes[node].player2 >= 0)
|
||||
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
|
||||
&netbuffer->u.client2pak.cmd2, 1);
|
||||
|
||||
// Check player consistancy during the level
|
||||
if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
|
||||
&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
|
||||
&& !SV_ResendingSavegameToAnyone()
|
||||
&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
|
||||
{
|
||||
if (cv_resynchattempts.value)
|
||||
{
|
||||
// Tell the client we are about to resend them the gamestate
|
||||
netbuffer->packettype = PT_WILLRESENDGAMESTATE;
|
||||
HSendPacket(node, true, 0, 0);
|
||||
|
||||
netnodes[node].resendingsavegame = true;
|
||||
|
||||
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)));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
|
||||
netconsole, realstart, consistancy[realstart%BACKUPTICS],
|
||||
SHORT(netbuffer->u.clientpak.consistancy)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PT_ServerTics(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
UINT8 *pak, *txtpak, numtxtpak;
|
||||
tic_t realend, realstart;
|
||||
|
||||
if (!netnodes[node].ingame)
|
||||
{
|
||||
// Do not remove my own server (we have just get a out of order packet)
|
||||
if (node != servernode)
|
||||
{
|
||||
DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
|
||||
return;
|
||||
}
|
||||
|
||||
realstart = netbuffer->u.serverpak.starttic;
|
||||
realend = realstart + netbuffer->u.serverpak.numtics;
|
||||
|
||||
txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
|
||||
* netbuffer->u.serverpak.numtics];
|
||||
|
||||
if (realend > gametic + CLIENTBACKUPTICS)
|
||||
realend = gametic + CLIENTBACKUPTICS;
|
||||
cl_packetmissed = realstart > neededtic;
|
||||
|
||||
if (realstart <= neededtic && realend > neededtic)
|
||||
{
|
||||
tic_t i, j;
|
||||
pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
|
||||
|
||||
for (i = realstart; i < realend; i++)
|
||||
{
|
||||
// clear first
|
||||
D_Clearticcmd(i);
|
||||
|
||||
// copy the tics
|
||||
pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
|
||||
netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
|
||||
|
||||
// copy the textcmds
|
||||
numtxtpak = *txtpak++;
|
||||
for (j = 0; j < numtxtpak; j++)
|
||||
{
|
||||
INT32 k = *txtpak++; // playernum
|
||||
const size_t txtsize = txtpak[0]+1;
|
||||
|
||||
if (i >= gametic) // Don't copy old net commands
|
||||
M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
|
||||
txtpak += txtsize;
|
||||
}
|
||||
}
|
||||
|
||||
neededtic = realend;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBFILE(va("frame not in bound: %u\n", neededtic));
|
||||
/*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);*/
|
||||
}
|
||||
}
|
||||
|
||||
// send the client packet to the server
|
||||
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)
|
||||
{
|
||||
// Send PT_NODEKEEPALIVE packet
|
||||
netbuffer->packettype += 4;
|
||||
packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
|
||||
HSendPacket(servernode, false, 0, packetsize);
|
||||
}
|
||||
else if (gamestate != GS_NULL && (addedtogame || dedicated))
|
||||
{
|
||||
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
|
||||
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
|
||||
|
||||
// Send a special packet with 2 cmd for splitscreen
|
||||
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);
|
||||
}
|
||||
|
||||
if (cl_mode == CL_CONNECTED || dedicated)
|
||||
{
|
||||
// Send extra data if needed
|
||||
if (localtextcmd[0])
|
||||
{
|
||||
netbuffer->packettype = PT_TEXTCMD;
|
||||
M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
|
||||
// All extra data have been sent
|
||||
if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
|
||||
localtextcmd[0] = 0;
|
||||
}
|
||||
|
||||
// Send extra data if needed for player 2 (splitscreen)
|
||||
if (localtextcmd2[0])
|
||||
{
|
||||
netbuffer->packettype = PT_TEXTCMD2;
|
||||
M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
|
||||
// All extra data have been sent
|
||||
if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
|
||||
localtextcmd2[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send the server packet
|
||||
// send tic from firstticstosend to maketic-1
|
||||
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 netnodes[n].supposedtic, max packet size and maketic
|
||||
for (n = 1; n < MAXNETNODES; n++)
|
||||
if (netnodes[n].ingame)
|
||||
{
|
||||
// assert netnodes[n].supposedtic>=netnodes[n].tic
|
||||
realfirsttic = netnodes[n].supposedtic;
|
||||
lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
|
||||
|
||||
if (realfirsttic >= lasttictosend)
|
||||
{
|
||||
// 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, netnodes[n].supposedtic, netnodes[n].tic));
|
||||
realfirsttic = netnodes[n].tic;
|
||||
if (realfirsttic >= lasttictosend || (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 = 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)
|
||||
netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
|
||||
else
|
||||
netnodes[n].supposedtic = lasttictosend;
|
||||
if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
|
||||
}
|
||||
// node 0 is me!
|
||||
netnodes[0].supposedtic = maketic;
|
||||
}
|
||||
|
||||
//
|
||||
// TryRunTics
|
||||
//
|
||||
void Local_Maketic(INT32 realtics)
|
||||
{
|
||||
I_OsPolling(); // I_Getevent
|
||||
D_ProcessEvents(); // menu responder, cons responder,
|
||||
// game responder calls HU_Responder, AM_Responder,
|
||||
// and G_MapEventsToControls
|
||||
if (!dedicated) rendergametic = gametic;
|
||||
// translate inputs (keyboard/mouse/gamepad) into game controls
|
||||
G_BuildTiccmd(&localcmds, realtics, 1);
|
||||
if (splitscreen || botingame)
|
||||
G_BuildTiccmd(&localcmds2, realtics, 2);
|
||||
|
||||
localcmds.angleturn |= TICCMD_RECEIVED;
|
||||
localcmds2.angleturn |= TICCMD_RECEIVED;
|
||||
}
|
||||
|
||||
// create missed tic
|
||||
void SV_Maketic(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
// We didn't receive this tic
|
||||
if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
|
||||
{
|
||||
ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i];
|
||||
ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
|
||||
|
||||
if (players[i].quittime)
|
||||
{
|
||||
// Copy the angle/aiming from the previous tic
|
||||
// and empty the other inputs
|
||||
memset(ticcmd, 0, sizeof(netcmds[0][0]));
|
||||
ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED;
|
||||
ticcmd->aiming = prevticcmd->aiming;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBFILE(va("MISS tic%4d for player %d\n", maketic, i));
|
||||
// Copy the input from the previous tic
|
||||
*ticcmd = *prevticcmd;
|
||||
ticcmd->angleturn &= ~TICCMD_RECEIVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all tic are now proceed make the next
|
||||
maketic++;
|
||||
}
|
44
src/netcode/tic_command.h
Normal file
44
src/netcode/tic_command.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 by Sonic Team Junior.
|
||||
//
|
||||
// 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 tic_command.h
|
||||
/// \brief Tic command handling
|
||||
|
||||
#ifndef __D_TIC_COMMAND__
|
||||
#define __D_TIC_COMMAND__
|
||||
|
||||
#include "d_clisrv.h"
|
||||
#include "../d_ticcmd.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../doomtype.h"
|
||||
|
||||
extern tic_t firstticstosend; // min of the nettics
|
||||
extern tic_t tictoclear; // optimize d_clearticcmd
|
||||
|
||||
extern ticcmd_t localcmds;
|
||||
extern ticcmd_t localcmds2;
|
||||
extern boolean cl_packetmissed;
|
||||
|
||||
extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
|
||||
|
||||
tic_t ExpandTics(INT32 low, INT32 node);
|
||||
void D_Clearticcmd(tic_t tic);
|
||||
void D_ResetTiccmds(void);
|
||||
|
||||
void PT_ClientCmd(SINT8 node, INT32 netconsole);
|
||||
void PT_ServerTics(SINT8 node, INT32 netconsole);
|
||||
|
||||
// send the client packet to the server
|
||||
void CL_SendClientCmd(void);
|
||||
|
||||
void SV_SendTics(void);
|
||||
void Local_Maketic(INT32 realtics);
|
||||
void SV_Maketic(void);
|
||||
|
||||
#endif
|
|
@ -28,6 +28,7 @@
|
|||
#include "m_misc.h"
|
||||
#include "v_video.h" // video flags for CEchos
|
||||
#include "f_finale.h"
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
// CTF player names
|
||||
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "p_slopes.h"
|
||||
#include "f_finale.h"
|
||||
#include "m_cond.h"
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
|
||||
|
|
|
@ -87,6 +87,8 @@
|
|||
|
||||
#include "taglist.h"
|
||||
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
//
|
||||
// Map MD5, calculated on level load.
|
||||
// Sent to clients in PT_SERVERINFO.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
#include "i_video.h" // rendermode
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
// Object place
|
||||
#include "m_cheat.h"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "i_system.h"
|
||||
#include "d_event.h"
|
||||
#include "netcode/d_net.h"
|
||||
#include "netcode/net_command.h"
|
||||
#include "g_game.h"
|
||||
#include "p_local.h"
|
||||
#include "r_fps.h"
|
||||
|
|
Loading…
Reference in a new issue