2023-01-05 21:51:17 +00:00
|
|
|
// 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"
|
2023-01-07 12:01:48 +00:00
|
|
|
#include "gamestate.h"
|
2023-01-05 21:51:17 +00:00
|
|
|
#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);
|
|
|
|
}
|