raze/source/sw/src/network.cpp
pogokeen 222e593437 Change some clocks to support sub-tick precision using a new class, ClockTicks.
Using that higher precision, interpolate at a higher granularity.
Further, truncate the target interpolation time to vertical blank boundaries to avoid producing temporal artifacts.
Fix issues caused by interpolation calculations being handled differently in multiple places (and fix cases where smoothratios were being thrown away only to be redone without checking all proper conditions).
Ensure ClockTicks changes do not break other targets (EKenBuild, VoidSW), but note any interpolation there is not similarly updated.

git-svn-id: https://svn.eduke32.com/eduke32@8050 1a8010ca-5511-0410-912e-c29ae57300e0

# Conflicts:
#	source/build/src/build.cpp
#	source/build/src/sdlayer.cpp
2019-09-20 21:09:14 +02:00

1626 lines
43 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "build.h"
#include "baselayer.h"
#include "mmulti.h"
#include "keys.h"
#include "game.h"
#include "tags.h"
#include "names2.h"
#include "network.h"
#include "pal.h"
#include "demo.h"
#include "weapon.h"
#include "text.h"
#include "menus.h"
/*
SYNC BUG NOTES:
1. Look at Prediction code first when player movement is involved. If the
prediction code changes any variable not in the Player or Players
Sprite/User structure that effects movement then there will be a Out Of Sync
problem.
EXAMPLE: Prediction player was updating a sop->drive_oangvel making it
invalid for movement. Look at DoPlayerBoatTurn for comment.
2. Changing movement variables in the draw code. Because the draw code is
called at a variable rate this don't work. This includes using RANDOM_RANGE
or RANDOM_P2 in the draw code. This updates the random seed variable and is
only for movement code. Use STD_RANDOM_RANGE for draw code.
3. Plain old bugs such as using uninitialized local variables.
*/
//#undef MAXSYNCBYTES
//#define MAXSYNCBYTES 16
static uint8_t tempbuf[576], packbuf[576];
int PlayClock;
extern SWBOOL PauseKeySet;
extern char CommPlayerName[32];
gNET gNet;
extern short PlayerQuitMenuLevel;
#define TIMERUPDATESIZ 32
//SW_PACKET fsync;
//Local multiplayer variables
// should move this to a local scope of faketimerhandler - do it when able to test
SW_PACKET loc;
//SW_PACKET oloc;
SWBOOL ready2send = 0;
SWBOOL CommEnabled = FALSE;
uint8_t CommPlayers = 0;
int movefifoplc, movefifosendplc; //, movefifoend[MAX_SW_PLAYERS];
unsigned int MoveThingsCount;
//int myminlag[MAX_SW_PLAYERS];
int mymaxlag, otherminlag, bufferjitter = 1;
extern char sync_first[MAXSYNCBYTES][60];
extern int sync_found;
//
// Tic Duplication - so you can move multiple times per packet
//
typedef struct
{
int32_t vel;
int32_t svel;
int32_t angvel;
int32_t aimvel;
int32_t bits;
} SW_AVERAGE_PACKET;
int MovesPerPacket = 1;
SW_AVERAGE_PACKET AveragePacket;
// GAME.C sync state variables
uint8_t syncstat[MAXSYNCBYTES];
//int syncvalhead[MAX_SW_PLAYERS];
int syncvaltail, syncvaltottail;
void GetSyncInfoFromPacket(uint8_t *packbuf, int packbufleng, int *j, int otherconnectindex);
// when you set totalclock to 0 also set this one
int ototalclock;
int smoothratio;
int save_totalclock;
// must start out as 0
SWBOOL GamePaused = FALSE;
SWBOOL NetBroadcastMode = TRUE;
SWBOOL NetModeOverride = FALSE;
void netsendpacket(int ind, uint8_t* buf, int len)
{
uint8_t bbuf[sizeof(packbuf) + sizeof(PACKET_PROXY)];
PACKET_PROXYp prx = (PACKET_PROXYp)bbuf;
int i;
// send via master if in M/S mode and we are not the master, and the recipient is not the master and not ourselves
if (!NetBroadcastMode && myconnectindex != connecthead && ind != myconnectindex && ind != connecthead)
{
if ((unsigned)len > sizeof(packbuf))
{
buildprintf("netsendpacket(): packet length > %d!\n",(int)sizeof(packbuf));
len = sizeof(packbuf);
}
buildprintf("netsendpacket() sends proxy to %d\nPlayerIndex=%d Contents:",connecthead,ind);
for (i=0; i<len; i++)
buildprintf(" %02x", buf[i]);
buildputs("\n");
prx->PacketType = PACKET_TYPE_PROXY;
prx->PlayerIndex = (uint8_t)ind;
memcpy(&prx[1], buf, len); // &prx[1] == (char*)prx + sizeof(PACKET_PROXY)
len += sizeof(PACKET_PROXY);
sendpacket(connecthead, bbuf, len);
return;
}
sendpacket(ind, buf, len);
buildprintf("netsendpacket() sends normal to %d\nContents:",ind);
for (i=0; i<len; i++)
buildprintf(" %02x", buf[i]);
buildputs("\n");
}
void netbroadcastpacket(uint8_t* buf, int len)
{
int i;
uint8_t bbuf[sizeof(packbuf) + sizeof(PACKET_PROXY)];
PACKET_PROXYp prx = (PACKET_PROXYp)bbuf;
// broadcast via master if in M/S mode and we are not the master
if (!NetBroadcastMode && myconnectindex != connecthead)
{
if ((unsigned)len > sizeof(packbuf))
{
buildprintf("netbroadcastpacket(): packet length > %d!\n",(int)sizeof(packbuf));
len = sizeof(packbuf);
}
buildprintf("netbroadcastpacket() sends proxy to %d\nPlayerIndex=255 Contents:",connecthead);
for (i=0; i<len; i++)
buildprintf(" %02x", buf[i]);
buildputs("\n");
prx->PacketType = PACKET_TYPE_PROXY;
prx->PlayerIndex = (uint8_t)(-1);
memcpy(&prx[1], buf, len);
len += sizeof(PACKET_PROXY);
sendpacket(connecthead, bbuf, len);
return;
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (i == myconnectindex) continue;
sendpacket(i, buf, len);
buildprintf("netsendpacket() sends normal to %d\n",i);
}
buildputs("Contents:");
for (i=0; i<len; i++)
buildprintf(" %02x", buf[i]);
buildputs("\n");
}
int netgetpacket(int *ind, uint8_t* buf)
{
int i;
int len;
PACKET_PROXYp prx;
len = getpacket(ind, buf);
if ((unsigned)len < sizeof(PACKET_PROXY) || buf[0] != PACKET_TYPE_PROXY)
{
if (len > 0)
{
buildprintf("netgetpacket() gets normal from %d\nContents:",*ind);
for (i=0; i<len; i++)
buildprintf(" %02x", buf[i]);
buildputs("\n");
}
return len;
}
prx = (PACKET_PROXYp)buf;
buildprintf("netgetpacket() got proxy from %d\nPlayerIndex=%d Contents:",*ind,prx->PlayerIndex);
for (i=0; i<len-(int)sizeof(PACKET_PROXY); i++)
buildprintf(" %02x", *(((char *)&prx[1])+i));
buildputs("\n");
if (myconnectindex == connecthead)
{
// I am the master
if (prx->PlayerIndex == (uint8_t)(-1))
{
// broadcast
// Rewrite the player index to be the sender's connection number
prx->PlayerIndex = (uint8_t)*ind;
// Transmit to all the other players except ourselves and the sender
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (i == myconnectindex || i == *ind) continue;
buildprintf("netgetpacket(): distributing to %d\n", i);
sendpacket(i, buf, len);
}
// Return the packet payload to the caller
len -= sizeof(PACKET_PROXY);
memmove(buf, &prx[1], len);
return len;
}
else
{
// proxy send to a specific player
i = prx->PlayerIndex;
// Rewrite the player index to be the sender's connection number
prx->PlayerIndex = (uint8_t)*ind;
// Transmit to the intended recipient
if (i == myconnectindex)
{
len -= sizeof(PACKET_PROXY);
memmove(buf, &prx[1], len);
return len;
}
buildprintf("netgetpacket(): forwarding to %d\n", i);
sendpacket(i, buf, len);
return 0; // nothing for us to do
}
}
else if (*ind == connecthead)
{
// I am a slave, and the proxy message came from the master
*ind = prx->PlayerIndex;
len -= sizeof(PACKET_PROXY);
memmove(buf, &prx[1], len);
return len;
}
else
{
buildprintf("netgetpacket(): Got a proxy message from %d instead of %d\n",*ind,connecthead);
}
return 0;
}
int EncodeBits(SW_PACKET *pak, SW_PACKET *old_pak, uint8_t* buf)
{
uint8_t* base_ptr = buf;
unsigned i;
// skipping the bits field sync test fake byte (Ed. Ken)
*buf = 0;
buf++;
if (pak->vel != old_pak->vel)
{
*((short *)buf) = pak->vel;
buf += sizeof(pak->vel);
SET(*base_ptr, BIT(0));
}
if (pak->svel != old_pak->svel)
{
*((short *)buf) = pak->svel;
buf += sizeof(pak->svel);
SET(*base_ptr, BIT(1));
}
if (pak->angvel != old_pak->angvel)
{
*((char *)buf) = pak->angvel;
buf += sizeof(pak->angvel);
SET(*base_ptr, BIT(2));
}
if (pak->aimvel != old_pak->aimvel)
{
*((char *)buf) = pak->aimvel;
buf += sizeof(pak->aimvel);
SET(*base_ptr, BIT(3));
}
//won't work if > 4 bytes
for (i = 0; i < sizeof(pak->bits); i++)
{
if (TEST(pak->bits ^ old_pak->bits, 0xff<<(i<<3)))
{
*buf = (pak->bits>>(i<<3));
buf++;
SET(*base_ptr, BIT(i+4));
}
}
return buf - base_ptr;
}
int DecodeBits(SW_PACKET *pak, SW_PACKET *old_pak, uint8_t* buf)
{
uint8_t* base_ptr = buf;
unsigned i;
// skipping the bits field sync test fake byte (Ed. Ken)
buf++;
*pak = *old_pak;
if (TEST(*base_ptr, BIT(0)))
{
pak->vel = *(short *)buf;
buf += sizeof(pak->vel);
}
if (TEST(*base_ptr, BIT(1)))
{
pak->svel = *(short *)buf;
buf += sizeof(pak->svel);
}
if (TEST(*base_ptr, BIT(2)))
{
pak->angvel = *(char *)buf;
buf += sizeof(pak->angvel);
}
if (TEST(*base_ptr, BIT(3)))
{
pak->aimvel = *(char *)buf;
buf += sizeof(pak->aimvel);
}
//won't work if > 4 bytes
for (i = 0; i < sizeof(pak->bits); i++)
{
if (TEST(*base_ptr, BIT(i+4)))
{
RESET(pak->bits, 0xff<<(i<<3));
SET(pak->bits, ((int)(*buf))<<(i<<3));
buf++;
}
}
return buf - base_ptr;
}
void
PauseGame(void)
{
if (PauseKeySet)
return;
if (DemoPlaying || DemoRecording)
return;
if (GamePaused)
return;
if (numplayers < 2)
GamePaused = TRUE;
}
void
ResumeGame(void)
{
if (PauseKeySet)
return;
if (DemoPlaying || DemoRecording)
return;
if (numplayers < 2)
GamePaused = FALSE;
}
void
PauseAction(void)
{
ready2send = 0;
save_totalclock = (int32_t) totalclock;
}
void
ResumeAction(void)
{
ready2send = 1;
totalclock = save_totalclock;
}
void
SW_SendMessage(short pnum, const char *text)
{
if (!CommEnabled)
return;
tempbuf[0] = PACKET_TYPE_MESSAGE;
strcpy((char *)&tempbuf[1], text);
netsendpacket(pnum, tempbuf, strlen(text) + 2);
}
void
InitNetPlayerOptions(void)
{
short pnum;
PLAYERp pp = Player + myconnectindex;
PACKET_OPTIONS p;
// if you don't have a name :(
if (!CommPlayerName[0])
sprintf(CommPlayerName, "PLAYER %d", myconnectindex + 1);
Bstrupr(CommPlayerName);
strcpy(pp->PlayerName, CommPlayerName);
// myconnectindex palette
pp->TeamColor = gs.NetColor;
pp->SpriteP->pal = PALETTE_PLAYER0 + pp->TeamColor;
User[pp->SpriteP - sprite]->spal = pp->SpriteP->pal;
if (CommEnabled)
{
p.PacketType = PACKET_TYPE_PLAYER_OPTIONS;
p.AutoRun = gs.AutoRun;
p.Color = gs.NetColor;
strcpy(p.PlayerName, CommPlayerName);
//TRAVERSE_CONNECT(pnum)
{
//if (pnum != myconnectindex)
{
//netsendpacket(pnum, (char *)(&p), sizeof(p));
netbroadcastpacket((uint8_t*)(&p), sizeof(p));
}
}
}
}
void
SendMulitNameChange(char *new_name)
{
short pnum;
PLAYERp pp = Player + myconnectindex;
PACKET_NAME_CHANGE p;
if (!CommEnabled)
return;
Bstrupr(new_name);
strcpy(pp->PlayerName, new_name);
strcpy(CommPlayerName, new_name);
SetRedrawScreen(pp);
//TRAVERSE_CONNECT(pnum)
{
//if (pnum != myconnectindex)
{
p.PacketType = PACKET_TYPE_NAME_CHANGE;
strcpy(p.PlayerName, pp->PlayerName);
//netsendpacket(pnum, (char *)(&p), sizeof(p));
netbroadcastpacket((uint8_t*)(&p), sizeof(p));
}
}
}
void
SendVersion(int version)
{
short pnum;
PLAYERp pp = Player + myconnectindex;
PACKET_VERSION p;
if (!CommEnabled)
return;
pp->PlayerVersion = version;
//TRAVERSE_CONNECT(pnum)
{
//if (pnum != myconnectindex)
{
p.PacketType = PACKET_TYPE_VERSION;
p.Version = version;
//netsendpacket(pnum, (char *)(&p), sizeof(p));
netbroadcastpacket((uint8_t*)(&p), sizeof(p));
}
}
}
void
CheckVersion(int GameVersion)
{
short pnum;
PACKET_VERSION p;
#define VERSION_MSG "You cannot play with different versions!"
if (!CommEnabled)
return;
TRAVERSE_CONNECT(pnum)
{
if (pnum != myconnectindex)
{
if (GameVersion != Player[pnum].PlayerVersion)
{
buildprintf("CheckVersion(): player %d has version %d, expecting %d\n",
pnum, Player[pnum].PlayerVersion, GameVersion);
adduserquote(VERSION_MSG);
adduserquote(VERSION_MSG);
adduserquote(VERSION_MSG);
adduserquote(VERSION_MSG);
adduserquote(VERSION_MSG);
adduserquote(VERSION_MSG);
if (!Player[pnum].PlayerVersion)
{
SW_SendMessage(pnum, VERSION_MSG);
SW_SendMessage(pnum, VERSION_MSG);
SW_SendMessage(pnum, VERSION_MSG);
SW_SendMessage(pnum, VERSION_MSG);
SW_SendMessage(pnum, VERSION_MSG);
SW_SendMessage(pnum, VERSION_MSG);
}
}
}
}
}
void
Connect(void)
{
int players_found, i, yline;
short other;
if (CommEnabled)
{
#if 0
int x1, x2, y1, y2;
int screensize = xdim;
extern short BorderTest[];
// put up a tile
x1 = (xdim >> 1) - (screensize >> 1);
x2 = x1 + screensize - 1;
y1 = ((ydim) >> 1) - (((screensize * (ydim)) / xdim) >> 1);
y2 = y1 + ((screensize * (ydim)) / xdim) - 1;
rotatespritetile(0L, 0L, BorderTest[gs.BorderTile], 0, x1, y1, x2, y2, 0);
nextpage();
#endif
screenpeek = myconnectindex;
}
//InitTimingVars(); // resettiming();
}
int wfe_Clock;
SWBOOL (*wfe_ExitCallback)(void);
void
waitforeverybody(void)
{
int i, size = 1;
short other;
if (!CommEnabled)
return;
buildprintf("waitforeverybody() #%d\n", Player[myconnectindex].playerreadyflag + 1);
//tenDbLprintf(gTenLog, 3, "in w4e");
//tenDbFlushLog(gTenLog);
tempbuf[0] = PACKET_TYPE_PLAYER_READY;
#ifdef DEBUG
tempbuf[1] = Player[myconnectindex].playerreadyflag + 1;
size++;
#endif
if (!NetBroadcastMode && myconnectindex != connecthead)
netsendpacket(connecthead, tempbuf, size);
else
netbroadcastpacket(tempbuf, size);
#if 0
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (i != myconnectindex)
{
DSPRINTF(ds,"Ready packet sent to %d", i);
DebugWriteString(ds);
}
}
#endif
//KEY_PRESSED(KEYSC_ESC) = FALSE;
Player[myconnectindex].playerreadyflag++;
while (TRUE)
{
if (PlayerQuitMenuLevel >= 0)
{
//DSPRINTF(ds,"%d, Player Quit Menu Level %d", myconnectindex, PlayerQuitMenuLevel);
//DebugWriteString(ds);
MenuCommPlayerQuit(PlayerQuitMenuLevel);
PlayerQuitMenuLevel = -1;
}
handleevents();
getpackets();
if (quitevent || (wfe_ExitCallback && wfe_ExitCallback()))
{
// allow exit
//if (KEY_PRESSED(KEYSC_ESC))
{
short pnum;
//TRAVERSE_CONNECT(pnum)
{
//if (pnum != myconnectindex)
{
tempbuf[0] = PACKET_TYPE_MENU_LEVEL_QUIT;
//netsendpacket(pnum, tempbuf, 1);
netbroadcastpacket(tempbuf, 1);
}
}
TerminateGame();
exit(0);
}
}
#if 0
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
DSPRINTF(ds,"myindex %d, myready %d, Player %d, Ready %d", myconnectindex, Player[myconnectindex].playerreadyflag, i, Player[i].playerreadyflag);
DebugWriteString(ds);
}
#endif
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (Player[i].playerreadyflag < Player[myconnectindex].playerreadyflag)
break;
if ((!NetBroadcastMode) && (myconnectindex != connecthead)) { i = -1; break; } //slaves in M/S mode only wait for master
}
if (i < 0)
{
return;
}
}
}
SWBOOL MyCommPlayerQuit(void)
{
PLAYERp pp;
short i;
short prev_player = 0;
extern SWBOOL QuitFlag;
short found = FALSE;
short quit_player_index = 0;
TRAVERSE_CONNECT(i)
{
if (TEST_SYNC_KEY(Player + i, SK_QUIT_GAME))
{
short pnum;
found = TRUE;
quit_player_index = i;
if (i != myconnectindex)
{
sprintf(ds,"%s has quit the game.",Player[i].PlayerName);
adduserquote(ds);
}
}
}
if (found)
{
TRAVERSE_CONNECT(i)
{
pp = Player + i;
if (i == quit_player_index)
{
PLAYERp qpp = Player + quit_player_index;
SET(qpp->SpriteP->cstat, CSTAT_SPRITE_INVISIBLE);
RESET(qpp->SpriteP->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);
InitBloodSpray(qpp->PlayerSprite,TRUE,-2);
InitBloodSpray(qpp->PlayerSprite,FALSE,-2);
qpp->SpriteP->ang = NORM_ANGLE(qpp->SpriteP->ang + 1024);
InitBloodSpray(qpp->PlayerSprite,FALSE,-1);
InitBloodSpray(qpp->PlayerSprite,TRUE,-1);
}
// have to reorder the connect list
if (!TEST_SYNC_KEY(pp, SK_QUIT_GAME))
{
prev_player = i;
continue;
}
// if I get my own messages get out to DOS QUICKLY!!!
if (i == myconnectindex)
{
QuitFlag = TRUE;
ready2send = 0;
return TRUE;
}
// for COOP mode
if (screenpeek == i)
{
screenpeek = connectpoint2[i];
if (screenpeek < 0)
screenpeek = connecthead;
}
DSPRINTF(ds,"MyCommPlayerQuit %d", quit_player_index);
DebugWriteString(ds);
if (i == connecthead)
connecthead = connectpoint2[connecthead];
else
connectpoint2[prev_player] = connectpoint2[i];
numplayers--;
CommPlayers--;
}
}
return FALSE;
}
SWBOOL MenuCommPlayerQuit(short quit_player)
{
PLAYERp pp;
short i;
short prev_player = 0;
short pnum;
// tell everyone else you left the game
TRAVERSE_CONNECT(pnum)
{
if (pnum != quit_player)
{
sprintf(ds,"%s has quit the game.",Player[myconnectindex].PlayerName);
SW_SendMessage(pnum, ds);
}
}
TRAVERSE_CONNECT(i)
{
pp = Player + i;
// have to reorder the connect list
if (i != quit_player)
{
prev_player = i;
continue;
}
// for COOP mode
if (screenpeek == i)
{
screenpeek = connectpoint2[i];
if (screenpeek < 0)
screenpeek = connecthead;
}
DSPRINTF(ds,"MenuPlayerQuit %d", quit_player);
DebugWriteString(ds);
if (i == connecthead)
connecthead = connectpoint2[connecthead];
else
connectpoint2[prev_player] = connectpoint2[i];
numplayers--;
CommPlayers--;
}
return FALSE;
}
void ErrorCorrectionQuit(void)
{
int oldtotalclock;
short i,j;
if (CommPlayers > 1)
{
for (j = 0; j < MAX_SW_PLAYERS; j++)
{
oldtotalclock = (int32_t) totalclock;
while (totalclock < oldtotalclock + synctics)
{
handleevents();
getpackets();
}
tempbuf[0] = PACKET_TYPE_NULL_PACKET;
netbroadcastpacket(tempbuf, 1);
}
}
}
void
InitNetVars(void)
{
short pnum;
PLAYERp pp;
memset(&loc, 0, sizeof(loc));
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
pp->movefifoend = 0;
Player[pnum].syncvalhead = 0;
memset(pp->inputfifo,0,sizeof(pp->inputfifo));
}
movefifoplc = 0;
movefifosendplc = 0;
syncvaltail = 0;
syncvaltottail = 0;
predictmovefifoplc = 0;
memset(&syncstat, 0, sizeof(syncstat));
syncstate = 0;
memset(sync_first, 0, sizeof(sync_first));
sync_found = FALSE;
TRAVERSE_CONNECT(pnum)
{
Player[pnum].myminlag = 0;
}
otherminlag = mymaxlag = 0;
}
void
InitTimingVars(void)
{
PlayClock = 0;
// resettiming();
totalsynctics = 0;
totalclock = 0;
ototalclock = 0;
randomseed = 17L;
MoveSkip8 = 2;
MoveSkip2 = 0;
MoveSkip4 = 1; // start slightly offset so these
// don't move the same
// as the Skip2's
MoveThingsCount = 0;
// CTW REMOVED
//if (gTenActivated)
// tenResetClock();
// CTW REMOVED END
}
void
AddSyncInfoToPacket(int *j)
{
int sb;
int count = 0;
// sync testing
while (Player[myconnectindex].syncvalhead != syncvaltail && count++ < 4)
{
for (sb = 0; sb < NumSyncBytes; sb++)
packbuf[(*j)++] = Player[myconnectindex].syncval[syncvaltail & (SYNCFIFOSIZ - 1)][sb];
syncvaltail++;
}
}
void
faketimerhandler(void)
{
short other, packbufleng;
int i, j, k, l;
PLAYERp pp;
short pnum;
void getinput(SW_PACKET *);
extern SWBOOL BotMode;
#if 0
if (KEY_PRESSED(KEYSC_PERIOD))
{
extern unsigned int MoveThingsCount;
MoveThingsCount++;
MoveThingsCount--;
return;
}
#endif
timerUpdate();
if ((totalclock < ototalclock + synctics))
return;
if (!ready2send)
return;
ototalclock += synctics;
getpackets();
// TENSW: this way we are guaranteed that the most advanced player is no more
// than 200 frames ahead of the most laggy. We're more healthy if the queue
// doesn't overflow, that's for sure.
if (Player[myconnectindex].movefifoend - movefifoplc >= 100)
return;
getinput(&loc);
AveragePacket.vel += loc.vel;
AveragePacket.svel += loc.svel;
AveragePacket.angvel += loc.angvel;
AveragePacket.aimvel += loc.aimvel;
SET(AveragePacket.bits, loc.bits);
pp = Player + myconnectindex;
if (pp->movefifoend & (MovesPerPacket-1))
{
memcpy(&pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend-1) & (MOVEFIFOSIZ - 1)],
sizeof(SW_PACKET));
pp->movefifoend++;
return;
}
loc.vel = AveragePacket.vel / MovesPerPacket;
loc.svel = AveragePacket.svel / MovesPerPacket;
loc.angvel = AveragePacket.angvel / MovesPerPacket;
loc.aimvel = AveragePacket.aimvel / MovesPerPacket;
loc.bits = AveragePacket.bits;
memset(&AveragePacket, 0, sizeof(AveragePacket));
pp->inputfifo[Player[myconnectindex].movefifoend & (MOVEFIFOSIZ - 1)] = loc;
pp->movefifoend++;
#if 0
// AI Bot stuff
if (numplayers > 1)
{
if (gNet.MultiGameType == MULTI_GAME_AI_BOTS)
{
for (i=connecthead; i>=0; i=connectpoint2[i])
{
if (i != myconnectindex)
{
if (BotMode && Player[i].IsAI == 1) // Skip it if this player is not computer controlled!
{
computergetinput(i,&Player[i].inputfifo[Player[i].movefifoend&(MOVEFIFOSIZ-1)]);
Player[i].movefifoend++;
}
}
}
}
}
// AI Bot stuff
#endif
if (!CommEnabled)
{
TRAVERSE_CONNECT(i)
{
if (i != myconnectindex)
{
if (BotMode && Player[i].IsAI == 1)
{
computergetinput(i,&Player[i].inputfifo[Player[i].movefifoend&(MOVEFIFOSIZ-1)]);
}
else
memset(&Player[i].inputfifo[Player[i].movefifoend & (MOVEFIFOSIZ - 1)], 0, sizeof(Player[i].inputfifo[0]));
Player[i].movefifoend++;
}
}
return;
}
TRAVERSE_CONNECT(i)
{
if (i != myconnectindex)
{
k = (Player[myconnectindex].movefifoend - 1) - Player[i].movefifoend;
Player[i].myminlag = min(Player[i].myminlag, k);
mymaxlag = max(mymaxlag, k);
}
}
if (((Player[myconnectindex].movefifoend - 1) & (TIMERUPDATESIZ - 1)) == 0)
{
i = mymaxlag - bufferjitter;
mymaxlag = 0;
if (i > 0)
bufferjitter += ((2 + i) >> 2);
else if (i < 0)
bufferjitter -= ((2 - i) >> 2);
}
if (NetBroadcastMode)
{
packbuf[0] = PACKET_TYPE_BROADCAST;
j = 1;
if (((Player[myconnectindex].movefifoend - 1) & (TIMERUPDATESIZ - 1)) == 0 /* CTW REMOVED && !gTenActivated */)
{
if (myconnectindex == connecthead)
{
for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
packbuf[j++] = min(max(Player[i].myminlag, -128), 127);
}
else
{
i = Player[connecthead].myminlag - otherminlag;
if (labs(i) > 2)
{
////DSPRINTF(ds,"lag correction: %d,%d,%d",i,Player[connecthead].myminlag,otherminlag);
//MONO_PRINT(ds);
if (labs(i) > 8)
{
if (i < 0)
i++;
i >>= 1;
}
else
{
if (i < 0)
i = -1;
if (i > 0)
i = 1;
}
totalclock -= synctics * i;
otherminlag += i;
}
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
Player[i].myminlag = 0x7fffffff;
}
pp = Player + myconnectindex;
#if !BIT_CODEC
memcpy(&packbuf[j], &pp->inputfifo[(Player[myconnectindex].movefifoend - 1) & (MOVEFIFOSIZ - 1)], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
j += EncodeBits(&pp->inputfifo[(Player[myconnectindex].movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(Player[myconnectindex].movefifoend - 2) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
#endif
#if SYNC_TEST
AddSyncInfoToPacket(&j);
#endif
netbroadcastpacket(packbuf, j);
return;
} // NetBroadcastMode
// SLAVE CODE
if (myconnectindex != connecthead) // I am the Slave
{
if (((Player[myconnectindex].movefifoend - 1) & (TIMERUPDATESIZ - 1)) == 0)
{
i = Player[connecthead].myminlag - otherminlag;
if (labs(i) > 2)
{
if (labs(i) > 8)
{
if (i < 0)
i++;
i >>= 1;
}
else
{
if (i < 0)
i = -1;
if (i > 0)
i = 1;
}
totalclock -= synctics * i;
otherminlag += i;
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
Player[i].myminlag = 0x7fffffff;
}
}
packbuf[0] = PACKET_TYPE_SLAVE_TO_MASTER;
j = 1;
pp = Player + myconnectindex;
#if !BIT_CODEC
memcpy(&packbuf[j], &pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
j += EncodeBits(&pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend - 2) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
#endif
#if SYNC_TEST
AddSyncInfoToPacket(&j);
#endif
netsendpacket(connecthead, packbuf, j);
return;
}
// This allows packet-resends
//for (i = connecthead; i >= 0; i = connectpoint2[i])
// {
// if ( /* (!playerquitflag[i]) && */ (Player[i].movefifoend <= movefifosendplc))
// {
// packbuf[0] = 127;
// for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
// {
// /* if (!playerquitflag[i]) */ sendpacket(i, packbuf, 1);
// }
// return;
// }
// }
// I am MASTER...
while (1)
{
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (/* (!playerquitflag[i]) && */ (Player[i].movefifoend <= movefifosendplc))
return;
}
packbuf[0] = PACKET_TYPE_MASTER_TO_SLAVE;
j = 1;
// Fix timers and buffer/jitter value
if ((movefifosendplc & (TIMERUPDATESIZ - 1)) == 0)
{
for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
{
/* if (!playerquitflag[i]) */
packbuf[j++] = min(max(Player[i].myminlag, -128), 127);
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
Player[i].myminlag = 0x7fffffff;
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
/* if (playerquitflag[i]) continue; */
pp = Player + i;
#if !BIT_CODEC
memcpy(&packbuf[j], &pp->inputfifo[movefifosendplc & (MOVEFIFOSIZ - 1)], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
j += EncodeBits(&pp->inputfifo[(movefifosendplc) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(movefifosendplc - 1) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
#endif
//pp->movefifoend++;
}
#if SYNC_TEST
AddSyncInfoToPacket(&j);
#endif
for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
/* if (!playerquitflag[i])*/
{
netsendpacket(i, packbuf, j);
/*
pp = Player + i;
if (TEST(pp->inputfifo[movefifosendplc & (MOVEFIFOSIZ - 1)].bits,QUITBIT)
playerquitflag[i] = 1;
*/
}
movefifosendplc += MovesPerPacket;
}
}
void
checkmasterslaveswitch(void)
{
}
void
getpackets(void)
{
int otherconnectindex, packbufleng;
int i, j, k, l, fifoCheck, sb;
PLAYERp pp;
SW_PACKET tempinput;
timerUpdate();
if (!CommEnabled)
return;
while ((packbufleng = netgetpacket(&otherconnectindex, packbuf)) > 0)
{
switch (packbuf[0])
{
case PACKET_TYPE_BROADCAST:
case SERVER_GENERATED_BROADCAST:
////DSPRINTF(ds,"Receive Broadcast %d, ready2send %d",otherconnectindex, ready2send);
//MONO_PRINT(ds);
//ASSERT(ready2send);
//if (!ready2send)
// break;
j = 1;
if ((Player[otherconnectindex].movefifoend & (TIMERUPDATESIZ - 1)) == 0)
{
if (otherconnectindex == connecthead)
{
for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
{
if (i == myconnectindex)
otherminlag = (int)((signed char) packbuf[j]);
j++;
}
}
}
pp = Player + otherconnectindex;
#if !BIT_CODEC
memcpy(&pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)], &packbuf[j], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
j += DecodeBits(&pp->inputfifo[(pp->movefifoend) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
#endif
pp->movefifoend++;
// Packet Duplication
for (i = 1; i < MovesPerPacket; i++)
{
memcpy(
&pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend-1) & (MOVEFIFOSIZ - 1)],
sizeof(SW_PACKET));
pp->movefifoend++;
}
#if SYNC_TEST
GetSyncInfoFromPacket(packbuf, packbufleng, &j, otherconnectindex);
#endif
//DSPRINTF(ds,"Receive packet size %d",j);
//MONO_PRINT(ds);
break;
case PACKET_TYPE_MASTER_TO_SLAVE:
// Here slave is receiving
j = 1;
if ((Player[otherconnectindex].movefifoend & (TIMERUPDATESIZ - 1)) == 0)
{
for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
{
// if (playerquitflag[i]) continue;
if (i == myconnectindex)
otherminlag = (int)((signed char) packbuf[j]);
j++;
}
}
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
/* if (playerquitflag[i]) continue;) */
pp = Player + i;
#if !BIT_CODEC
if (i != myconnectindex)
memcpy(&pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)], &packbuf[j], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
if (i == myconnectindex)
{
j += DecodeBits(&tempinput,
&pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
}
else
{
j += DecodeBits(&pp->inputfifo[(pp->movefifoend) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
}
#endif
pp->movefifoend++;
}
while (j != packbufleng)
{
for (i = connecthead; i >= 0; i = connectpoint2[i])
{
if (i != myconnectindex)
{
for (sb = 0; sb < NumSyncBytes; sb++)
{
Player[i].syncval[Player[i].syncvalhead & (SYNCFIFOSIZ - 1)][sb] = packbuf[j + sb];
}
Player[i].syncvalhead++;
}
}
j += NumSyncBytes;
}
// #if SYNC_TEST //This doesn't work right in this case
// GetSyncInfoFromPacket(packbufleng, &j, otherconnectindex);
// #endif
for (i=connecthead; i>=0; i=connectpoint2[i])
if (i != myconnectindex)
for (j=1; j<MovesPerPacket; j++)
{
pp = Player + i;
memcpy(
&pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(pp->movefifoend-1) & (MOVEFIFOSIZ - 1)],
sizeof(SW_PACKET));
pp->movefifoend++;
}
break;
case PACKET_TYPE_SLAVE_TO_MASTER:
// Here master is receiving
pp = Player + otherconnectindex;
j = 1;
#if !BIT_CODEC
memcpy(&pp->inputfifo[Player[otherconnectindex].movefifoend & (MOVEFIFOSIZ - 1)], &packbuf[j], sizeof(SW_PACKET));
j += sizeof(SW_PACKET);
#else
j += DecodeBits(&pp->inputfifo[(Player[otherconnectindex].movefifoend) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(Player[otherconnectindex].movefifoend - 1) & (MOVEFIFOSIZ - 1)],
&packbuf[j]);
#endif
Player[otherconnectindex].movefifoend++;
#if SYNC_TEST
GetSyncInfoFromPacket(packbuf, packbufleng, &j, otherconnectindex);
#endif
// Tic duping
for (i = 1; i < MovesPerPacket; i++)
{
memcpy(&pp->inputfifo[(Player[otherconnectindex].movefifoend) & (MOVEFIFOSIZ - 1)],
&pp->inputfifo[(Player[otherconnectindex].movefifoend - 1) & (MOVEFIFOSIZ - 1)],
sizeof(SW_PACKET));
Player[otherconnectindex].movefifoend++;
}
break;
case PACKET_TYPE_MESSAGE:
{
PLAYERp tp = Player + myconnectindex;
pp = Player + otherconnectindex;
// retransmit if master and the message is not addressed to us
if (!NetBroadcastMode && myconnectindex == connecthead && packbuf[1] != myconnectindex)
{
netsendpacket(packbuf[1], packbuf, packbufleng);
break;
}
PlaySound(DIGI_PMESSAGE,&tp->posx,&tp->posy,&tp->posz,v3df_dontpan);
memcpy(ds,&packbuf[3],packbufleng-3);
ds[packbufleng-3] = 0;
//sprintf(ds, "%s",&packbuf[3]);
adduserquote(ds);
break;
}
case PACKET_TYPE_RTS:
{
PACKET_RTSp p;
p = (PACKET_RTSp)packbuf;
PlaySoundRTS(p->RTSnum);
break;
}
case PACKET_TYPE_NEW_GAME:
{
extern SWBOOL NewGame, ShortGameMode, DemoInitOnce;
PACKET_NEW_GAMEp p;
extern short TimeLimitTable[];
pp = Player + otherconnectindex;
// Dukes New Game Packet
//level_number //volume_number //player_skill //monsters_off //respawn_monsters
//respawn_items //respawn_inventory //coop //marker //friendlyfire //boardname
p = (PACKET_NEW_GAMEp)packbuf;
ready2send = 0;
Level = p->Level;
Skill = p->Skill;
gNet.HurtTeammate = p->HurtTeammate;
gNet.SpawnMarkers = p->SpawnMarkers;
gNet.TeamPlay = p->TeamPlay;
gNet.AutoAim = p->AutoAim;
gNet.Nuke = p->Nuke;
gNet.KillLimit = p->KillLimit*10;
gNet.TimeLimit = TimeLimitTable[p->TimeLimit]*60*120;
if (ShortGameMode)
{
gNet.KillLimit /= 10;
gNet.TimeLimit /= 2;
}
gNet.TimeLimitClock = gNet.TimeLimit;
gNet.MultiGameType = p->GameType+1;
// settings for No Respawn Commbat mode
if (gNet.MultiGameType == MULTI_GAME_COMMBAT_NO_RESPAWN)
{
gNet.MultiGameType = MULTI_GAME_COMMBAT;
gNet.NoRespawn = TRUE;
}
else
{
gNet.NoRespawn = FALSE;
}
ExitLevel = TRUE;
NewGame = TRUE;
// restart demo for multi-play mode
DemoInitOnce = FALSE;
ResetMenuInput();
// send a dummy packet to see when it arrives
//tempbuf[0] = PACKET_TYPE_DUMMY;
//sendpacket(otherconnectindex, tempbuf, 1);
////DSPRINTF(ds,"Level %d, Skill %d, AutoAim %d",Level, Skill, gs.AutoAim);
//MONO_PRINT(ds);
break;
}
case PACKET_TYPE_DUMMY:
////DSPRINTF(ds,"Got Dummy Packet!!!");
//MONO_PRINT(ds);
break;
case PACKET_TYPE_VERSION:
{
PACKET_VERSIONp p;
pp = Player + otherconnectindex;
p = (PACKET_VERSIONp)packbuf;
//tenDbLprintf(gTenLog, 3, "rcv pid %d version %lx", (int) otherconnectindex, (int) p->Version);
pp->PlayerVersion = p->Version;
break;
}
case PACKET_TYPE_PLAYER_OPTIONS:
{
PACKET_OPTIONSp p;
pp = Player + otherconnectindex;
p = (PACKET_OPTIONSp)packbuf;
// auto run
if (p->AutoRun)
SET(pp->Flags, PF_LOCK_RUN);
else
RESET(pp->Flags, PF_LOCK_RUN);
// palette
pp->TeamColor = p->Color;
pp->SpriteP->pal = PALETTE_PLAYER0 + pp->TeamColor;
User[pp->SpriteP - sprite]->spal = pp->SpriteP->pal;
// names
strcpy(pp->PlayerName, p->PlayerName);
break;
}
case PACKET_TYPE_NAME_CHANGE:
{
PACKET_NAME_CHANGEp p;
pp = Player + otherconnectindex;
p = (PACKET_NAME_CHANGEp)packbuf;
// someone else has changed their name
DSPRINTF(ds,"Recieved name: %s",p->PlayerName);
MONO_PRINT(ds);
strcpy(pp->PlayerName, p->PlayerName);
//strcpy(CommPlayerName, p->PlayerName);
SetRedrawScreen(Player+myconnectindex);
break;
}
case PACKET_TYPE_MENU_LEVEL_QUIT:
{
PlayerQuitMenuLevel = otherconnectindex;
break;
}
case PACKET_TYPE_PLAYER_READY:
Player[otherconnectindex].playerreadyflag++;
// It's important to return from getpackets() when a ready packet comes in and
// you are inside waitforeverybody(). Otherwise multiple ready packets can come
// in inside one waitforeverybody() which causes havoc if that w4e is protecting
// an initialization step which (heh) for example resets the playerreadyflag count.
return;
//break;
case PACKET_TYPE_DONT_USE:
break;
case PACKET_TYPE_NULL_PACKET:
break;
case PACKET_TYPE_PROXY:
buildputs("getpackets(): nested proxy packets!?\n");
break;
default:
DSPRINTF(ds,"Packet type unknown %d",packbuf[0]);
MONO_PRINT(ds);
}
}
}