zduke/source/engine/mmulti-mine.cpp

515 lines
11 KiB
C++
Raw Permalink Normal View History

2020-07-17 08:59:23 +00:00
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// [RH] Modified the packet headers:
//
// It used to be: AA BB CC ... YY ZZ
// Where AA = Sending subpacket num
// BB = 1-bit subpacket count + Some error correcting stuff I don't understand
// CC = Last received subpacket num
// YY ZZ = CRC-16
//
// It's now this: AA BB [CC] [[EE [FF]] ...]
// Where AA = Sending subpacket num (low 8 bits)
// BB = Bits 0-6: Number of subpackets
// Bit 7: Retransmit request is present if set
// CC = Retransmit subpacket num (low 8 bits; only present if bit 7 of BB is set)
//
// Every subpacket except for the last one is prefaced by a 1- or 2-byte length count:
// EE = Bits 0-6: Low 7 bits of subpacket length
// Bit 7: FF is present if set
// FF = Bits 7-15 of subpacket length (only present if bit 7 of EE is set)
//
// The last subpacket does not have a length prefix because its length can be
// deduced from the size of the packet.
//
// There is no CRC because UDP already has attaches its own checksum to the packet.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <process.h>
#include <stdarg.h>
#include "pragmas.h"
#include "doomtype.h"
#include "multi.h"
#include "m_argv.h"
#include "i_system.h"
#define BAKSIZ 16384
#define RESENDCOUNT 10
#define SIMULATEERRORS 0
#define SHOWSENDPACKETS 1
#define SHOWGETPACKETS 1
#define PRINTERRORS 1
void dosendpackets(long other);
void sendpacket(long other, BYTE *bufptr, long messleng);
extern void I_InitNetwork (void);
extern void I_NetCmd (void);
static int incnt[MAXPLAYERS], outcntplc[MAXPLAYERS], outcntend[MAXPLAYERS];
static int misscnt[MAXPLAYERS];
static int lastcntsent[MAXPLAYERS]; // Last cnt the other node is known to have received
static bool RemoteResend[MAXPLAYERS];
static int ResendCount[MAXPLAYERS];
static BYTE errorgotnum[MAXPLAYERS];
static BYTE errorfixnum[MAXPLAYERS];
static BYTE errorresendnum[MAXPLAYERS];
static BYTE lastpacket[MAXPACKETSIZE], inlastpacket = 0;
static int lastpacketpos = 0;
static short lastpacketfrom, lastpacketleng;
extern volatile long totalclock; //MUST EXTERN 1 ANNOYING VARIABLE FROM GAME
static long timeoutcount = 60, resendagaincount = 4, lastsendtime[MAXPLAYERS];
static short bakpacketptr[MAXPLAYERS][256], bakpacketlen[MAXPLAYERS][256];
static BYTE bakpacketbuf[BAKSIZ];
static long bakpacketplc = 0;
short myconnectindex, numplayers;
short connecthead, connectpoint2[MAXPLAYERS];
BYTE syncstate = 0;
extern int _argc;
extern char **_argv;
FILE *debugfile;
void callcommit()
{
I_NetCmd ();
}
void initmultiplayers(BYTE damultioption, BYTE dacomrateoption, BYTE dapriority)
{
int i;
char *v;
I_InitNetwork ();
for(i = 0; i < MAXPLAYERS; i++)
{
incnt[i] = 0L;
outcntplc[i] = 0L;
outcntend[i] = 0L;
bakpacketlen[i][255] = -1;
}
if (gcom->numplayers == 1)
{
delete gcom;
gcom = NULL;
numplayers = 1; myconnectindex = 0;
connecthead = 0; connectpoint2[0] = -1;
return;
}
v = Args.CheckValue ("-debugfile");
if (v != NULL)
{
debugfile = fopen (v, "w");
}
numplayers = gcom->numplayers;
myconnectindex = gcom->myconnectindex;
#if (SIMULATEERRORS != 0)
srand(myconnectindex*24572457+345356);
#endif
connecthead = 0;
for(i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
connectpoint2[numplayers-1] = -1;
for(i=0;i<numplayers;i++) lastsendtime[i] = totalclock;
}
void setpackettimeout(long datimeoutcount, long daresendagaincount)
{
long i;
timeoutcount = datimeoutcount;
resendagaincount = daresendagaincount;
for(i=0;i<numplayers;i++) lastsendtime[i] = totalclock;
}
void uninitmultiplayers()
{
if (debugfile)
{
fclose (debugfile);
debugfile = NULL;
}
}
void sendlogon()
{
}
void sendlogoff()
{
long i;
BYTE tempbuf[2];
tempbuf[0] = 255;
tempbuf[1] = (BYTE)myconnectindex;
for(i=connecthead;i>=0;i=connectpoint2[i])
if (i != myconnectindex)
sendpacket(i,tempbuf,2L);
}
int getoutputcirclesize()
{
return(0);
}
void setsocket(short newsocket)
{
}
void sendpacket(long other, BYTE *bufptr, long messleng)
{
long i, j;
if (numplayers < 2) return;
i = 0;
if (bakpacketlen[other][(outcntend[other]-1)&255] == messleng)
{
j = bakpacketptr[other][(outcntend[other]-1)&255];
for(i=messleng-1;i>=0;i--)
if (bakpacketbuf[(i+j)&(BAKSIZ-1)] != bufptr[i]) break;
}
bakpacketlen[other][outcntend[other]&255] = (short)messleng;
if (i < 0) //Point to last packet to save space on bakpacketbuf
bakpacketptr[other][outcntend[other]&255] = (short)j;
else
{
bakpacketptr[other][outcntend[other]&255] = (short)bakpacketplc;
for(i=0;i<messleng;i++)
bakpacketbuf[(bakpacketplc+i)&(BAKSIZ-1)] = bufptr[i];
bakpacketplc = ((bakpacketplc+messleng)&(BAKSIZ-1));
}
outcntend[other]++;
lastsendtime[other] = totalclock;
dosendpackets(other);
}
void dosendpackets(long other)
{
int i, j, k, messleng, count;
BYTE t;
count = outcntend[other] - outcntplc[other];
if (count <= 0) return;
k = 0;
gcom->buffer[k++] = outcntplc[other] & 255;
t = count - 1;
if (RemoteResend[other])
{
#if (PRINTERRORS)
if (debugfile)
fprintf(debugfile, " MeWant %ld\n",incnt[other]&255);
#endif
t |= 128;
}
gcom->buffer[k++] = t;
//if (t & 128)
{
gcom->buffer[k++] = incnt[other] & 255;
}
while (count > 0)
{
j = bakpacketptr[other][outcntplc[other]&255];
messleng = bakpacketlen[other][outcntplc[other]&255];
// If not the last subpacket, prepend a length to it
if (count > 1)
{
t = messleng & 127;
if (messleng > 127)
{
t |= 128;
}
gcom->buffer[k++] = t;
if (t & 128)
{
gcom->buffer[k++] = messleng >> 7;
}
}
for(i = 0; i < messleng; i++)
gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
outcntplc[other]++;
count--;
}
gcom->other = (short)other;
gcom->numbytes = (short)k;
#if (SHOWSENDPACKETS)
if (debugfile)
{
fprintf(debugfile, "%8ld:Send(%ld): ", totalclock, gcom->other);
fprintf(debugfile, "[%5d+%2d %5d] ", gcom->buffer[0],
(gcom->buffer[1]&127) + 1,
incnt[other]);
for(i=0;i<gcom->numbytes;i++) fprintf(debugfile, "%2x ",gcom->buffer[i]);
fprintf(debugfile, "\n");
}
#endif
#if (SIMULATEERRORS != 0)
//if (!(rand()&SIMULATEERRORS)) gcom->buffer[rand()%gcom->numbytes] = (rand()&255);
int n = rand()&SIMULATEERRORS;
if (n && debugfile)
{
fprintf(debugfile, "simulating packet drop\n");
}
if (n)
#endif
{ gcom->command = 1; callcommit(); }
}
int ExpandTics (int maketic, int low)
{
int delta;
delta = low - (maketic&0xff);
if (delta >= -64 && delta <= 64)
return (maketic&~0xff) + low;
if (delta > 64)
return (maketic&~0xff) - 256 + low;
if (delta < -64)
return (maketic&~0xff) + 256 + low;
I_Error ("ExpandTics: strange value %i at maketic %i", low, maketic);
return 0;
}
short getpacket (short *other, BYTE *bufptr)
{
int i, k, messleng;
int recvcnt, recvpackets;
if (numplayers < 2) return -1;
for(i=connecthead;i>=0;i=connectpoint2[i])
{
if (i != myconnectindex)
{
if (totalclock < lastsendtime[i]) lastsendtime[i] = totalclock;
if (totalclock > lastsendtime[i]+timeoutcount)
{
#if (PRINTERRORS)
if (debugfile)
fprintf(debugfile, " TimeOut!\n");
#endif
errorgotnum[i] = errorfixnum[i]+1;
if ((outcntplc[i] == outcntend[i]) && (outcntplc[i] > 0))
{ outcntplc[i]--; lastsendtime[i] = totalclock; }
else
lastsendtime[i] += resendagaincount;
dosendpackets(i);
}
}
}
if (inlastpacket > 0)
{
// Get next subpacket from previously received packet
int len;
if (--inlastpacket > 0)
{
len = lastpacket[lastpacketpos++];
if (len & 128)
{
len = (len & 127) | (lastpacket[lastpacketpos++] << 7);
}
}
else
{
len = lastpacketleng - lastpacketpos;
}
*other = lastpacketfrom;
memcpy (bufptr, lastpacket + lastpacketpos, len);
lastpacketpos += len;
return len;
}
retry:
gcom->command = 2;
callcommit();
if (gcom->other < 0) return -1;
messleng = gcom->numbytes;
// [RH] Make sure packet is long enough
if (messleng < 2 || messleng < 2 + (gcom->buffer[1] & 127) + (gcom->buffer[1] >> 7))
{
#if PRINTERRORS
if (debugfile)
fprintf(debugfile, "Superfluous %d byte packet received from %d\n",
messleng, gcom->other);
#endif
goto retry;
}
*other = gcom->other;
recvpackets = (gcom->buffer[1] & 127) + 1;
if (recvpackets > 1)
{
recvcnt = ExpandTics (incnt[*other], gcom->buffer[0]);
}
else
{
recvcnt = ExpandTics (misscnt[*other], gcom->buffer[0]);
}
misscnt[*other] = recvcnt;
lastcntsent[*other] = ExpandTics (lastcntsent[*other], gcom->buffer[2]);
k = 3;
#if (SHOWGETPACKETS)
if (debugfile)
{
fprintf(debugfile, "%8ld: Get(%ld): ", totalclock, gcom->other);
fprintf(debugfile, "[%5d %2d %5d] ", recvcnt, recvpackets, lastcntsent[*other]);
for(i=0;i<gcom->numbytes;i++) fprintf(debugfile, "%2x ",gcom->buffer[i]);
fprintf(debugfile, "\n");
}
#endif
// check for retransmit request
if (ResendCount[*other] <= 0 && (gcom->buffer[1] & 128))
{
outcntplc[*other] = lastcntsent[*other];
if (debugfile)
fprintf (debugfile,"retransmit from %i\n", outcntplc[*other]);
ResendCount[*other] = RESENDCOUNT;
}
else
{
ResendCount[*other]--;
}
// Check for out-of-order/duplicated packet
if (recvcnt + recvpackets <= incnt[*other])
{
if (debugfile)
{
if (recvcnt + recvpackets < incnt[*other])
{
fprintf (debugfile, "Out of order packet from %d: %d + %d\n",
*other, recvcnt, recvpackets);
}
else
{
fprintf (debugfile, "Duplicate packet from %d: %d + %d\n",
*other, recvcnt, recvpackets);
}
}
goto retry;
}
// Check for missing packet
if (recvcnt > incnt[*other])
{
if (debugfile)
{
fprintf (debugfile, "Missed packet(s) from %d: [%d,%d]\n",
*other, incnt[*other], recvcnt - 1);
}
if (!RemoteResend[*other])
{
lastsendtime[i] = totalclock - timeoutcount;
RemoteResend[*other] = true;
}
goto retry;
}
// It's good!
RemoteResend[*other] = false;
if (recvpackets > 1)
{
messleng = gcom->buffer[k++];
if (messleng & 128)
{
messleng = (messleng & 127) | (gcom->buffer[k++] << 7);
}
}
else
{
messleng = gcom->numbytes - k;
}
memcpy (bufptr, &gcom->buffer[k], messleng);
if (recvpackets > 1)
{
k += messleng;
lastpacketleng = gcom->numbytes - k;
inlastpacket = recvpackets - 1;
lastpacketfrom = *other;
lastpacketpos = 0;
memcpy (lastpacket, &gcom->buffer[k], lastpacketleng);
}
incnt[*other] += recvpackets;
return messleng;
}
void flushpackets()
{
/*long i;
if (numplayers < 2) return;
do
{
gcom->command = 2;
callcommit();
} while (gcom->other >= 0);
for(i=connecthead;i>=0;i=connectpoint2[i])
{
incnt[i] = 0L;
outcntplc[i] = 0L;
outcntend[i] = 0L;
errorgotnum[i] = 0;
errorfixnum[i] = 0;
errorresendnum[i] = 0;
lastsendtime[i] = totalclock;
}*/
}
void genericmultifunction(int other, BYTE *bufptr, int messleng, int command)
{
if (numplayers < 2) return;
gcom->command = command;
gcom->numbytes = messleng < MAXPACKETSIZE ? messleng : MAXPACKETSIZE;
copybuf(bufptr,gcom->buffer,(gcom->numbytes+3)>>2);
gcom->other = other+1;
callcommit();
}