Newer
Older
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 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 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
#include "i_net.h"
#include "i_system.h"
#include "i_video.h"
#include "d_net.h"
#include "d_main.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
#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"
#ifdef CLIENT_LOADINGSCREEN
// cl loading screen
#include "v_video.h"
#include "f_finale.h"
#endif
#ifdef _XBOX
#include "sdl12/SRB2XBOX/xboxhelp.h"
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
//
// 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
// 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
boolean server = true; // true or false but !server == client

LJ Sonic
committed
#define client (!server)
INT32 serverplayer = 0;
char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)

LJ Sonic
committed
// Server specific vars

LJ Sonic
committed
// Minimum timeout for sending the savegame
// The actual timeout will be longer depending on the savegame length

LJ Sonic
committed
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?
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
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20
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;
// 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
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;

LJ Sonic
committed
// Client specific
static ticcmd_t localcmds;
static ticcmd_t localcmds2;
static boolean cl_packetmissed;
// here it is for the secondary local player (splitscreen)
static UINT8 mynode; // my address pointofview server
static UINT8 localtextcmd[MAXTEXTCMD];
static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
static UINT8 localtextcmd3[MAXTEXTCMD]; // splitscreen == 2
static UINT8 localtextcmd4[MAXTEXTCMD]; // splitscreen == 3
static tic_t neededtic;
SINT8 servernode = 0; // the number of the server node
char connectedservername[MAXSERVERNAME];
/// \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;
// 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;
static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
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};
consvar_t cv_httpsource = {"http_source", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kicktime = {"kicktime", "10", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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)
/** 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
tic_t ExpandTics(INT32 low, tic_t basetic)
return (basetic & ~UINT8_MAX) - 256 + low;
return (basetic & ~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)
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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);
}
}
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
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);
}
}
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
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)
{
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]);
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 aknowledged,
// because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it

LJ Sonic
committed
if (client)
}
static void D_Clearticcmd(tic_t tic)
{
INT32 i;
D_FreeTextcmd(tic);
for (i = 0; i < MAXPLAYERS; i++)
DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%TICQUEUE));
void D_ResetTiccmds(void)
{
INT32 i;
memset(&localcmds, 0, sizeof(ticcmd_t));
memset(&localcmds2, 0, sizeof(ticcmd_t));
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);
}
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
// -----------------------------------------------------------------
// 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
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// 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]);
for (j = 0; j < NUMKARTSTUFF; ++j)
rsp->kartstuff[j] = LONG(players[i].kartstuff[j]); // SRB2kart
rsp->frameangle = (angle_t)LONG(players[i].frameangle); // SRB2kart
// 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
rsp->kartspeed = (UINT8)players[i].kartspeed;
rsp->kartweight = (UINT8)players[i].kartweight;
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);
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
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);
rsp->spectatorreentry = (tic_t)LONG(players[i].spectatorreentry);
rsp->grieftime = (tic_t)LONG(players[i].grieftime);
rsp->griefstrikes = players[i].griefstrikes;
rsp->splitscreenindex = players[i].splitscreenindex;
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);
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);
rsp->tics = LONG(players[i].mo->tics);
rsp->statenum = (statenum_t)LONG(players[i].mo->state-states); // :(
rsp->flags = (UINT32)LONG(players[i].mo->flags);
rsp->flags2 = (UINT32)LONG(players[i].mo->flags2);
rsp->eflags = (UINT16)SHORT(players[i].mo->eflags);
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);
}
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]);
for (j = 0; j < NUMKARTSTUFF; ++j)
players[i].kartstuff[j] = LONG(rsp->kartstuff[j]); // SRB2kart
players[i].frameangle = (angle_t)LONG(rsp->frameangle); // SRB2kart
// 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
players[i].kartspeed = (UINT8)rsp->kartspeed;
players[i].kartweight = (UINT8)rsp->kartweight;
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);
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
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);
players[i].spectatorreentry = (tic_t)LONG(rsp->spectatorreentry);
players[i].grieftime = (tic_t)LONG(rsp->grieftime);
players[i].griefstrikes = rsp->griefstrikes;
players[i].splitscreenindex = rsp->splitscreenindex;
//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);
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);
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);
// P_SetScale is redundant for this, as all related variables are already restored properly.
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);
// 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)
{
// 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))
continue;
rst->flagplayer[i] = (SINT8)j;
break;
}
CONS_Alert(CONS_ERROR, "One of the flags has gone completely missing...\n");
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
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
{
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
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
{
if (!playeringame[p->flagplayer[1]])
I_Error("Invalid blue flag player %d who isn't in the game!", (INT32)p->flagplayer[1]);
if (blueflag)
{
P_RemoveMobj(blueflag);
blueflag = NULL;
}
}
else
{
if (!blueflag)
blueflag = P_SpawnMobj(0,0,0,MT_BLUEFLAG);
P_UnsetThingPosition(blueflag);
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]);
P_SetThingPosition(blueflag);
}
}
static inline void resynch_write_others(resynchend_pak *rst)
{
UINT8 i;
Monster Iestyn
committed
rst->ingame = 0;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
{
Monster Iestyn
committed
rst->ctfteam[i] = 0;
rst->marescore[i] = 0;
rst->realtime[i] = 0;
rst->laps[i] = 0;
continue;
}
if (!players[i].spectator)
rst->ingame |= (1<<i);
Monster Iestyn
committed
rst->ctfteam[i] = (INT32)LONG(players[i].ctfteam);
rst->marescore[i] = (UINT32)LONG(players[i].marescore);
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));
Monster Iestyn
committed
players[i].ctfteam = (INT32)LONG(p->ctfteam[i]); // no, 0 does not mean spectator, at least not in Match
players[i].marescore = (UINT32)LONG(p->marescore[i]);
players[i].realtime = (tic_t)LONG(p->realtime[i]);
players[i].laps = p->laps[i];
}
}
static void SV_InitResynchVars(INT32 node)
{
resynch_delay[node] = TICRATE; // initial one second delay
resynch_score[node] = 0; // clean slate
resynch_status[node] = 0x00;
resynch_inprogress[node] = false;
memset(resynch_sent[node], 0, MAXPLAYERS);
}
static void SV_RequireResynch(INT32 node)
{
INT32 i;
resynch_delay[node] = 10; // Delay before you can fail sync again