Skip to content
Snippets Groups Projects
d_clisrv.c 169 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// 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.

#if !defined (UNDER_CE)
#include <time.h>
#endif
#ifdef __GNUC__
#include <unistd.h> //for unlink
#endif

Eidolon's avatar
Eidolon committed
#include "i_time.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "i_net.h"
#include "i_system.h"
#include "i_video.h"
#include "d_net.h"
#include "d_netfil.h" // fileneedednum
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "d_main.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
Marco Z's avatar
Marco Z committed
#include "g_input.h" // JOY1
Alam Ed Arias's avatar
Alam Ed Arias committed
#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"
Sal's avatar
Sal committed
#include "k_kart.h"
Sal's avatar
Sal committed
#include "s_sound.h" // sfx_syfail
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef CLIENT_LOADINGSCREEN
// cl loading screen
#include "v_video.h"
#include "f_finale.h"
#endif

#ifdef _XBOX
#include "sdl12/SRB2XBOX/xboxhelp.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif

#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif

Alam Ed Arias's avatar
Alam Ed Arias committed
//
// NETWORKING
//
// gametic is the tic about to (or currently being) run
// Server:
//   maketic is the tic that hasn't had control made for it yet
Alam Ed Arias's avatar
Alam Ed Arias committed
//   nettics is the tic for each node
//   firstticstosend is the lowest value of nettics
// Client:
//   neededtic is the tic needed by the client to run the game
Alam Ed Arias's avatar
Alam Ed Arias committed
//   firstticstosend is used to optimize a condition
// Normally maketic >= gametic > 0
Alam Ed Arias's avatar
Alam Ed Arias committed

#define MAX_REASONLENGTH 30
#define FORCECLOSE 0x8000
Alam Ed Arias's avatar
Alam Ed Arias committed

boolean server = true; // true or false but !server == client
Alam Ed Arias's avatar
Alam Ed Arias committed
boolean nodownload = false;
boolean serverrunning = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
INT32 serverplayer = 0;
char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)

Alam Ed Arias's avatar
Alam Ed Arias committed
UINT8 playernode[MAXPLAYERS];

// Minimum timeout for sending the savegame
// The actual timeout will be longer depending on the savegame length
tic_t jointimeout = (3*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?

Alam Ed Arias's avatar
Alam Ed Arias committed
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)
SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 2)
SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 3)
UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen
Alam Ed Arias's avatar
Alam Ed Arias committed
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game

tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20
Alam Ed Arias's avatar
Alam Ed Arias committed
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;

static INT16 consistancy[TICQUEUE];
Alam Ed Arias's avatar
Alam Ed Arias committed

// 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
static UINT8 resynch_sent[MAXNETNODES][MAXPLAYERS]; // what synch packets have we attempted to send to the player
Alam Ed Arias's avatar
Alam Ed Arias committed
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;

// kart, true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
UINT8 hu_stopped = 0;

Alam Ed Arias's avatar
Alam Ed Arias committed
static ticcmd_t localcmds;
static ticcmd_t localcmds2;
Sal's avatar
Sal committed
static ticcmd_t localcmds3;
static ticcmd_t localcmds4;
Alam Ed Arias's avatar
Alam Ed Arias committed
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 UINT8 localtextcmd3[MAXTEXTCMD]; // splitscreen == 2
static UINT8 localtextcmd4[MAXTEXTCMD]; // splitscreen == 3
Alam Ed Arias's avatar
Alam Ed Arias committed
static tic_t neededtic;
SINT8 servernode = 0; // the number of the server node
char connectedservername[MAXSERVERNAME];
Alam Ed Arias's avatar
Alam Ed Arias committed
/// \brief do we accept new players?
/// \todo WORK!
boolean acceptnewnode = true;

boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
tic_t firstconnectattempttime = 0;

Alam Ed Arias's avatar
Alam Ed Arias committed
// 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[TICQUEUE][MAXPLAYERS];
Alam Ed Arias's avatar
Alam Ed Arias committed
static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};


SteelT's avatar
SteelT committed
consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
Alam Ed Arias's avatar
Alam Ed Arias committed

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};

SteelT's avatar
SteelT committed
consvar_t cv_httpsource = {"http_source", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};

toaster's avatar
toaster committed
consvar_t cv_kicktime = {"kicktime", "10", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
toaster's avatar
toaster committed

Alam Ed Arias's avatar
Alam Ed Arias committed
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)
Alam Ed Arias's avatar
Alam Ed Arias committed
UINT16 software_MAXPACKETLENGTH;

/** Guesses the value of a tic from its lowest byte and from maketic
  *
  * \param low The lowest byte of the tic value
  * \param basetic The last full tic value to compare against
  * \return The full tic value
  *
  */
tic_t ExpandTics(INT32 low, tic_t basetic)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	INT32 delta;

	delta = low - (basetic & UINT8_MAX);
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (delta >= -64 && delta <= 64)
		return (basetic & ~UINT8_MAX) + low;
Alam Ed Arias's avatar
Alam Ed Arias committed
	else if (delta > 64)
		return (basetic & ~UINT8_MAX) - 256 + low;
Alam Ed Arias's avatar
Alam Ed Arias committed
	else //if (delta < -64)
		return (basetic & ~UINT8_MAX) + 256 + low;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// -----------------------------------------------------------------
// 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);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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);
	}
}

void SendNetXCmd3(netxcmd_t id, const void *param, size_t nparam)
{
	if (localtextcmd3[0]+2+nparam > MAXTEXTCMD)
	{
		I_Error("No more place in the buffer for netcmd %d\n",id);
		return;
	}
	localtextcmd3[0]++;
	localtextcmd3[localtextcmd3[0]] = (UINT8)id;
	if (param && nparam)
	{
		M_Memcpy(&localtextcmd3[localtextcmd3[0]+1], param, nparam);
		localtextcmd3[0] = (UINT8)(localtextcmd3[0] + (UINT8)nparam);
	}
}

void SendNetXCmd4(netxcmd_t id, const void *param, size_t nparam)
{
	if (localtextcmd4[0]+2+nparam > MAXTEXTCMD)
	{
		I_Error("No more place in the buffer for netcmd %d\n",id);
		return;
	}
	localtextcmd4[0]++;
	localtextcmd4[localtextcmd4[0]] = (UINT8)id;
	if (param && nparam)
	{
		M_Memcpy(&localtextcmd4[localtextcmd4[0]+1], param, nparam);
		localtextcmd4[0] = (UINT8)(localtextcmd4[0] + (UINT8)nparam);
	}
}

Alam Ed Arias's avatar
Alam Ed Arias committed
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));
Alam Ed Arias's avatar
Alam Ed Arias committed
						(listnetxcmd[id])(&curpos, i);
						DEBFILE("done\n");
					}
					else
					{
						if (server)
						{
							XBOXSTATIC UINT8 buf[3];

							buf[0] = (UINT8)i;
							buf[1] = KICK_MSG_CON_FAIL;
							SendNetXCmd(XD_KICK, &buf, 2);
							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]);
	// 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
		D_FreeTextcmd(gametic);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void D_Clearticcmd(tic_t tic)
{
	INT32 i;

	D_FreeTextcmd(tic);

	for (i = 0; i < MAXPLAYERS; i++)
		netcmds[tic%TICQUEUE][i].angleturn = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed

	DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%TICQUEUE));
Alam Ed Arias's avatar
Alam Ed Arias committed
}

void D_ResetTiccmds(void)
{
	INT32 i;

	memset(&localcmds, 0, sizeof(ticcmd_t));
	memset(&localcmds2, 0, sizeof(ticcmd_t));
Sal's avatar
Sal committed
	memset(&localcmds3, 0, sizeof(ticcmd_t));
	memset(&localcmds4, 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);
}

Alam Ed Arias's avatar
Alam Ed Arias committed
// -----------------------------------------------------------------
// 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
// -----------------------------------------------------------------

Alam Ed Arias's avatar
Alam Ed Arias committed
// -----------------------------------------------------------------
// 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);

	for (j = 0; j < NUMPOWERS; ++j)
		rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]);
		rsp->kartstuff[j] = LONG(players[i].kartstuff[j]); // SRB2kart
Alam Ed Arias's avatar
Alam Ed Arias committed

Sal's avatar
Sal committed
	rsp->frameangle = (angle_t)LONG(players[i].frameangle); // SRB2kart

Alam Ed Arias's avatar
Alam Ed Arias committed
	// Score is resynched in the rspfirm resync packet
	rsp->health = 0; // resynched with mo health
	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);
	// Just in case Lua does something like
	// modify these at runtime
Sal's avatar
Sal committed
	rsp->kartspeed = (UINT8)players[i].kartspeed;
	rsp->kartweight = (UINT8)players[i].kartweight;
Alam Ed Arias's avatar
Alam Ed Arias committed
	rsp->charflags = (UINT32)LONG(players[i].charflags);

	rsp->speed = (fixed_t)LONG(players[i].speed);
	rsp->jumping = players[i].jumping;
	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;
	rsp->skidtime = (tic_t)LONG(players[i].skidtime);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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);

	rsp->maxlink = LONG(players[i].maxlink);
	rsp->dashspeed = (fixed_t)LONG(players[i].dashspeed);
	rsp->dashtime = LONG(players[i].dashtime);
	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);

Sal's avatar
Sal committed
	rsp->jointime = (tic_t)LONG(players[i].jointime);
Sal's avatar
Sal committed
	rsp->spectatorreentry = (tic_t)LONG(players[i].spectatorreentry);
Sal's avatar
Sal committed

Sal's avatar
Sal committed
	rsp->grieftime = (tic_t)LONG(players[i].grieftime);
	rsp->griefstrikes = players[i].griefstrikes;
Sal's avatar
Sal committed

	rsp->splitscreenindex = players[i].splitscreenindex;
Alam Ed Arias's avatar
Alam Ed Arias committed
	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);
Sal's avatar
Sal committed
	rsp->x = (fixed_t)LONG(players[i].mo->x);
	rsp->y = (fixed_t)LONG(players[i].mo->y);
	rsp->z = (fixed_t)LONG(players[i].mo->z);
	rsp->momx = (fixed_t)LONG(players[i].mo->momx);
	rsp->momy = (fixed_t)LONG(players[i].mo->momy);
	rsp->momz = (fixed_t)LONG(players[i].mo->momz);
	rsp->friction = (fixed_t)LONG(players[i].mo->friction);
	rsp->movefactor = (fixed_t)LONG(players[i].mo->movefactor);
Alam Ed Arias's avatar
Alam Ed Arias committed

	rsp->tics = LONG(players[i].mo->tics);
	rsp->statenum = (statenum_t)LONG(players[i].mo->state-states); // :(
Sal's avatar
Sal committed
	rsp->flags = (UINT32)LONG(players[i].mo->flags);
	rsp->flags2 = (UINT32)LONG(players[i].mo->flags2);
	rsp->eflags = (UINT16)SHORT(players[i].mo->eflags);
Sal's avatar
Sal committed

	rsp->radius = (fixed_t)LONG(players[i].mo->radius);
	rsp->height = (fixed_t)LONG(players[i].mo->height);
	rsp->scale = (fixed_t)LONG(players[i].mo->scale);
	rsp->destscale = (fixed_t)LONG(players[i].mo->destscale);
	rsp->scalespeed = (fixed_t)LONG(players[i].mo->scalespeed);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

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);

	for (j = 0; j < NUMPOWERS; ++j)
		players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]);
		players[i].kartstuff[j] = LONG(rsp->kartstuff[j]); // SRB2kart
Alam Ed Arias's avatar
Alam Ed Arias committed

Sal's avatar
Sal committed
	players[i].frameangle = (angle_t)LONG(rsp->frameangle); // SRB2kart

Alam Ed Arias's avatar
Alam Ed Arias committed
	// Score is resynched in the rspfirm resync packet
	players[i].health = rsp->health;
	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);
	// Just in case Lua does something like
	// modify these at runtime
Sal's avatar
Sal committed
	players[i].kartspeed = (UINT8)rsp->kartspeed;
	players[i].kartweight = (UINT8)rsp->kartweight;
Alam Ed Arias's avatar
Alam Ed Arias committed
	players[i].charflags = (UINT32)LONG(rsp->charflags);

	players[i].speed = (fixed_t)LONG(rsp->speed);
	players[i].jumping = rsp->jumping;
	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;
	players[i].skidtime = (tic_t)LONG(rsp->skidtime);
Alam Ed Arias's avatar
Alam Ed Arias committed
	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);

	players[i].maxlink = LONG(rsp->maxlink);
	players[i].dashspeed = (fixed_t)LONG(rsp->dashspeed);
	players[i].dashtime = LONG(rsp->dashtime);
	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);

Sal's avatar
Sal committed
	players[i].jointime = (tic_t)LONG(rsp->jointime);
Sal's avatar
Sal committed
	players[i].spectatorreentry = (tic_t)LONG(rsp->spectatorreentry);
Sal's avatar
Sal committed

Sal's avatar
Sal committed
	players[i].grieftime = (tic_t)LONG(rsp->grieftime);
	players[i].griefstrikes = rsp->griefstrikes;
Sal's avatar
Sal committed

	players[i].splitscreenindex = rsp->splitscreenindex;
Alam Ed Arias's avatar
Alam Ed Arias committed
	//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->health = LONG(rsp->health);
Sal's avatar
Sal committed

	players[i].mo->angle = (angle_t)LONG(rsp->angle);
	players[i].mo->x = (fixed_t)LONG(rsp->x);
	players[i].mo->y = (fixed_t)LONG(rsp->y);
	players[i].mo->z = (fixed_t)LONG(rsp->z);
	players[i].mo->momx = (fixed_t)LONG(rsp->momx);
	players[i].mo->momy = (fixed_t)LONG(rsp->momy);
	players[i].mo->momz = (fixed_t)LONG(rsp->momz);
	players[i].mo->friction = (fixed_t)LONG(rsp->friction);
	players[i].mo->movefactor = (fixed_t)LONG(rsp->movefactor);

Alam Ed Arias's avatar
Alam Ed Arias committed
	players[i].mo->tics = LONG(rsp->tics);
Sal's avatar
Sal committed
	P_SetMobjStateNF(players[i].mo, (statenum_t)LONG(rsp->statenum));
	players[i].mo->flags = (UINT32)LONG(rsp->flags);
	players[i].mo->flags2 = (UINT32)LONG(rsp->flags2);
	players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);

	players[i].mo->radius = (fixed_t)LONG(rsp->radius);
	players[i].mo->height = (fixed_t)LONG(rsp->height);
Alam Ed Arias's avatar
Alam Ed Arias committed
	// P_SetScale is redundant for this, as all related variables are already restored properly.
Sal's avatar
Sal committed
	players[i].mo->scale = (fixed_t)LONG(rsp->scale);
	players[i].mo->destscale = (fixed_t)LONG(rsp->destscale);
	players[i].mo->scalespeed = (fixed_t)LONG(rsp->scalespeed);
Alam Ed Arias's avatar
Alam Ed Arias committed

	// And finally, SET THE MOBJ SKIN damn it.
	players[i].mo->skin = &skins[players[i].skin];
	players[i].mo->color = players[i].skincolor;

	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)
			{
Alam Ed Arias's avatar
Alam Ed Arias committed
				// 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))
Alam Ed Arias's avatar
Alam Ed Arias committed
					continue;
				rst->flagplayer[i] = (SINT8)j;
				break;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			if (j == MAXPLAYERS) // fine, no I_Error
			{
Alam Ed Arias's avatar
Alam Ed Arias committed
				CONS_Alert(CONS_ERROR, "One of the flags has gone completely missing...\n");
Alam Ed Arias's avatar
Alam Ed Arias committed
				rst->flagplayer[i] = -2;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			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
Alam Ed Arias's avatar
Alam Ed Arias committed
	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
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		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
Alam Ed Arias's avatar
Alam Ed Arias committed
	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
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		if (!playeringame[p->flagplayer[1]])
			 I_Error("Invalid blue flag player %d who isn't in the game!", (INT32)p->flagplayer[1]);
Alam Ed Arias's avatar
Alam Ed Arias committed
		players[p->flagplayer[1]].gotflag = GF_BLUEFLAG;
Alam Ed Arias's avatar
Alam Ed Arias committed
		if (blueflag)
		{
			P_RemoveMobj(blueflag);
			blueflag = NULL;
		}
	}
	else
	{
		if (!blueflag)
			blueflag = P_SpawnMobj(0,0,0,MT_BLUEFLAG);

		P_UnsetThingPosition(blueflag);
Alam Ed Arias's avatar
Alam Ed Arias committed
		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]);
Alam Ed Arias's avatar
Alam Ed Arias committed
		P_SetThingPosition(blueflag);
	}
}

static inline void resynch_write_others(resynchend_pak *rst)
{
	UINT8 i;

Alam Ed Arias's avatar
Alam Ed Arias committed

	for (i = 0; i < MAXPLAYERS; ++i)
	{
		if (!playeringame[i])
		{
Alam Ed Arias's avatar
Alam Ed Arias committed
			rst->score[i] = 0;
			rst->marescore[i] = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
			rst->realtime[i] = 0;
			rst->laps[i] = 0;
			continue;
		}

		if (!players[i].spectator)
			rst->ingame |= (1<<i);
		rst->ctfteam[i] = (INT32)LONG(players[i].ctfteam);
Alam Ed Arias's avatar
Alam Ed Arias committed
		rst->score[i] = (UINT32)LONG(players[i].score);
		rst->marescore[i] = (UINT32)LONG(players[i].marescore);
Alam Ed Arias's avatar
Alam Ed Arias committed
		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);

	for (i = 0; i < MAXPLAYERS; ++i)
	{
		// We don't care if they're in the game or not, just write all the data.
		players[i].spectator = !(loc_ingame & (1<<i));
		players[i].ctfteam = (INT32)LONG(p->ctfteam[i]); // no, 0 does not mean spectator, at least not in Match
Alam Ed Arias's avatar
Alam Ed Arias committed
		players[i].score = (UINT32)LONG(p->score[i]);
		players[i].marescore = (UINT32)LONG(p->marescore[i]);
Alam Ed Arias's avatar
Alam Ed Arias committed
		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;
	memset(resynch_sent[node], 0, MAXPLAYERS);
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void SV_RequireResynch(INT32 node)
{
	INT32 i;

	resynch_delay[node] = 10; // Delay before you can fail sync again