doom

a minimalistic implementation of doom
git clone git://ssnf.xyz/doom
Log | Files | Refs

d_net.c (11791B)


      1 #include <limits.h>
      2 
      3 #include "m_menu.h"
      4 #include "i_system.h"
      5 #include "i_video.h"
      6 #include "i_net.h"
      7 #include "g_game.h"
      8 #include "doomdef.h"
      9 #include "doomstat.h"
     10 
     11 #define	NCMD_EXIT		0x80000000
     12 #define	NCMD_RETRANSMIT		0x40000000
     13 #define	NCMD_SETUP		0x20000000
     14 #define	NCMD_KILL		0x10000000
     15 #define	NCMD_CHECKSUM	 	0x0fffffff
     16 #define	RESENDCOUNT	10
     17 #define	PL_DRONE	0x80
     18 
     19 doomcom_t*	doomcom;
     20 doomdata_t*	netbuffer;
     21 ticcmd_t	localcmds[BACKUPTICS];
     22 ticcmd_t		netcmds[MAXPLAYERS][BACKUPTICS];
     23 int		 	nettics[MAXNETNODES];
     24 boolean		nodeingame[MAXNETNODES];
     25 boolean		remoteresend[MAXNETNODES];
     26 int		resendto[MAXNETNODES];
     27 int		resendcount[MAXNETNODES];
     28 int		nodeforplayer[MAXPLAYERS];
     29 int			 maketic;
     30 int		lastnettic;
     31 int		skiptics;
     32 int		ticdup;
     33 int		maxsend;
     34 
     35 void D_ProcessEvents ();
     36 void G_BuildTiccmd (ticcmd_t *cmd);
     37 void D_DoAdvanceDemo ();
     38 
     39 boolean		reboundpacket;
     40 doomdata_t	reboundstore;
     41 
     42 long
     43 NetbufferSize()
     44 {
     45 	return (long)&(((doomdata_t*)0)->cmds[netbuffer->numtics]);
     46 }
     47 
     48 uint
     49 NetbufferChecksum()
     50 {
     51 	long i, l;
     52 	uint c;
     53 
     54 	c = 0x1234567;
     55 	return 0; /*Linux, fixme. Endianess?*/
     56 	l = (NetbufferSize() - (long)&(((doomdata_t *)0)->retransmitfrom))/4;
     57 	for (i = 0; i < l; ++i)
     58 		c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
     59 	return c & NCMD_CHECKSUM;
     60 }
     61 
     62 int
     63 ExpandTics(int low)
     64 {
     65 	int	delta;
     66 
     67 	delta = low - (maketic & 0xff);
     68 	if (delta >= -64 && delta <= 64)
     69 		return (maketic&~0xff) + low;
     70 	if (delta > 64)
     71 		return (maketic&~0xff) - 256 + low;
     72 	if (delta < -64)
     73 		return (maketic&~0xff) + 256 + low;
     74 	I_Error("ExpandTics: strange value %i at maketic %i",low,maketic);
     75 	return 0;
     76 }
     77 
     78 void
     79 HSendPacket(int node, int flags)
     80 {
     81 	int i, realretrans;
     82 
     83 	netbuffer->checksum = NetbufferChecksum() | flags;
     84 	if (!node) {
     85 		reboundstore = *netbuffer;
     86 		reboundpacket = true;
     87 		return;
     88 	}
     89 	if (demoplayback)
     90 		return;
     91 	if (!netgame)
     92 		I_Error("Tried to transmit to another node");
     93 	doomcom->command = CMD_SEND;
     94 	doomcom->remotenode = node;
     95 	doomcom->datalength = NetbufferSize ();
     96 	if (debugfile) {
     97 		if (netbuffer->checksum & NCMD_RETRANSMIT)
     98 			realretrans = ExpandTics(netbuffer->retransmitfrom);
     99 		else
    100 			realretrans = -1;
    101 		fprintf (debugfile,"send (%i + %i, R %i) [%i] ", ExpandTics(netbuffer->starttic), netbuffer->numtics, realretrans, doomcom->datalength);
    102 		for (i = 0; i < doomcom->datalength; ++i)
    103 			fprintf(debugfile,"%i ",((byte *)netbuffer)[i]);
    104 		fprintf(debugfile,"\n");
    105 	}
    106 	I_NetCmd();
    107 }
    108 
    109 boolean
    110 HGetPacket()
    111 {
    112 	int i, realretrans;
    113 
    114 	if (reboundpacket) {
    115 		*netbuffer = reboundstore;
    116 		doomcom->remotenode = 0;
    117 		reboundpacket = false;
    118 		return true;
    119 	}
    120 	if (!netgame)
    121 		return false;
    122 	if (demoplayback)
    123 		return false;
    124 	doomcom->command = CMD_GET;
    125 	I_NetCmd();
    126 	if (doomcom->remotenode == -1)
    127 		return false;
    128 	if (doomcom->datalength != NetbufferSize ()) {
    129 		if (debugfile)
    130 			fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
    131 		return false;
    132 	}
    133 	if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM)) {
    134 		if (debugfile)
    135 			fprintf (debugfile,"bad packet checksum\n");
    136 		return false;
    137 	}
    138 	if (debugfile) {
    139 		if (netbuffer->checksum & NCMD_SETUP)
    140 			fprintf (debugfile,"setup packet\n");
    141 		else {
    142 			if (netbuffer->checksum & NCMD_RETRANSMIT)
    143 				realretrans = ExpandTics (netbuffer->retransmitfrom);
    144 			else
    145 				realretrans = -1;
    146 			fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",  doomcom->remotenode, ExpandTics(netbuffer->starttic), netbuffer->numtics, realretrans, doomcom->datalength);
    147 			for (i = 0; i < doomcom->datalength; ++i)
    148 				fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
    149 			fprintf (debugfile,"\n");
    150 		}
    151 	}
    152 	return true;
    153 }
    154 
    155 char exitmsg[80];
    156 
    157 void
    158 GetPackets()
    159 {
    160 	ticcmd_t *src, *dest;
    161 	int      realend, realstart, netconsole, netnode, start;
    162 
    163 	while (HGetPacket()) {
    164 		if (netbuffer->checksum & NCMD_SETUP)
    165 			continue;
    166 		netconsole = netbuffer->player & ~PL_DRONE;
    167 		netnode = doomcom->remotenode;
    168 		realstart = ExpandTics (netbuffer->starttic);
    169 		realend = (realstart+netbuffer->numtics);
    170 		if (netbuffer->checksum & NCMD_EXIT) {
    171 			if (!nodeingame[netnode])
    172 				continue;
    173 			nodeingame[netnode] = false;
    174 			playeringame[netconsole] = false;
    175 			strcpy(exitmsg, "Player 1 left the game");
    176 			exitmsg[7] += netconsole;
    177 			players[consoleplayer].message = exitmsg;
    178 			if (demorecording)
    179 				G_CheckDemoStatus();
    180 			continue;
    181 		}
    182 		if (netbuffer->checksum & NCMD_KILL)
    183 			I_Error("Killed by network driver");
    184 		nodeforplayer[netconsole] = netnode;
    185 		if (resendcount[netnode] <= 0 && (netbuffer->checksum & NCMD_RETRANSMIT)) {
    186 			resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
    187 			if (debugfile)
    188 				fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
    189 			resendcount[netnode] = RESENDCOUNT;
    190 		} else
    191 			--resendcount[netnode];
    192 		if (realend == nettics[netnode])
    193 			continue;
    194 		if (realend < nettics[netnode]) {
    195 			if (debugfile)
    196 				fprintf (debugfile, "out of order packet (%i + %i)\n", realstart, netbuffer->numtics);
    197 			continue;
    198 		}
    199 		if (realstart > nettics[netnode]) {
    200 			if (debugfile)
    201 				fprintf (debugfile, "missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
    202 			remoteresend[netnode] = true;
    203 			continue;
    204 		}
    205 
    206 		remoteresend[netnode] = false;
    207 		start = nettics[netnode] - realstart;
    208 		src = &netbuffer->cmds[start];
    209 		while (nettics[netnode] < realend) {
    210 			dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
    211 			++nettics[netnode];
    212 			*dest = *src;
    213 			++src;
    214 		}
    215 	}
    216 }
    217 
    218 int gametime;
    219 
    220 void
    221 NetUpdate()
    222 {
    223 	int nowtime, newtics, i, j, realstart, gameticdiv;
    224 
    225 	nowtime = I_GetTime()/ticdup;
    226 	newtics = nowtime - gametime;
    227 	gametime = nowtime;
    228 	if (newtics <= 0) 
    229 		goto listen;
    230 	if (skiptics <= newtics) {
    231 		newtics -= skiptics;
    232 		skiptics = 0;
    233 	} else {
    234 		skiptics -= newtics;
    235 		newtics = 0;
    236 	}
    237 	netbuffer->player = consoleplayer;
    238 	gameticdiv = gametic/ticdup;
    239 	for (i = 0; i < newtics; ++i) {
    240 		I_StartTic();
    241 		D_ProcessEvents();
    242 		if (maketic - gameticdiv >= BACKUPTICS/2-1)
    243 			break;
    244 		G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]);
    245 		++maketic;
    246 	}
    247 	if (singletics)
    248 		return;
    249 	for (i = 0; i < doomcom->numnodes; ++i)
    250 		if (nodeingame[i]) {
    251 			netbuffer->starttic = realstart = resendto[i];
    252 			netbuffer->numtics = maketic - realstart;
    253 			if (netbuffer->numtics > BACKUPTICS)
    254 				I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
    255 			resendto[i] = maketic - doomcom->extratics;
    256 			for (j = 0; j < netbuffer->numtics; ++j)
    257 				netbuffer->cmds[j] = localcmds[(realstart+j)%BACKUPTICS];
    258 			if (remoteresend[i]) {
    259 				netbuffer->retransmitfrom = nettics[i];
    260 				HSendPacket (i, NCMD_RETRANSMIT);
    261 			} else {
    262 				netbuffer->retransmitfrom = 0;
    263 				HSendPacket (i, 0);
    264 			}
    265 		}
    266 listen:
    267 	GetPackets();
    268 }
    269 
    270 void
    271 CheckAbort()
    272 {
    273 	event_t *ev;
    274 	int     stoptic;
    275 
    276 	stoptic = I_GetTime() + 2;
    277 	while (I_GetTime() < stoptic)
    278 		I_StartTic();
    279 	I_StartTic();
    280 	for (; eventtail != eventhead; eventtail = (eventtail+1)&(MAXEVENTS-1)) {
    281 		ev = &events[eventtail];
    282 		if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
    283 			I_Error("Network game synchronization aborted.");
    284 	}
    285 }
    286 
    287 void
    288 D_ArbitrateNetStart()
    289 {
    290 	int     i;
    291 	uchar   gotinfo[MAXNETNODES];
    292 
    293 	autostart = true;
    294 	memset(gotinfo, 0, sizeof(gotinfo));
    295 	if (doomcom->consoleplayer) {
    296 		printf ("listening for network start info...\n");
    297 		for (;;) {
    298 			CheckAbort();
    299 			if (!HGetPacket())
    300 				continue;
    301 			if (netbuffer->checksum & NCMD_SETUP) {
    302 				if (netbuffer->player != VERSION)
    303 					I_Error("Different DOOM versions cannot play a net game!");
    304 				startskill = netbuffer->retransmitfrom & 15;
    305 				deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
    306 				nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
    307 				respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
    308 				startmap = netbuffer->starttic & 0x3f;
    309 				startepisode = netbuffer->starttic >> 6;
    310 				return;
    311 			}
    312 		}
    313 	} else {
    314 		printf("sending network start info...\n");
    315 		do {
    316 			CheckAbort();
    317 			for (i = 0; i < doomcom->numnodes; ++i) {
    318 				netbuffer->retransmitfrom = startskill;
    319 				if (deathmatch)
    320 					netbuffer->retransmitfrom |= (deathmatch<<6);
    321 				if (nomonsters)
    322 					netbuffer->retransmitfrom |= 0x20;
    323 				if (respawnparm)
    324 					netbuffer->retransmitfrom |= 0x10;
    325 				netbuffer->starttic = startepisode * 64 + startmap;
    326 				netbuffer->player = VERSION;
    327 				netbuffer->numtics = 0;
    328 				HSendPacket(i, NCMD_SETUP);
    329 			}
    330 			for(i = 10; i && HGetPacket(); --i)
    331 				if((netbuffer->player&0x7f) < MAXNETNODES)
    332 					gotinfo[netbuffer->player&0x7f] = true;
    333 			for (i = 1; i < doomcom->numnodes; ++i)
    334 				if (!gotinfo[i])
    335 					break;
    336 		} while (i < doomcom->numnodes);
    337 	}
    338 }
    339 
    340 extern int viewangleoffset;
    341 
    342 void
    343 D_CheckNetGame()
    344 {
    345 	int i;
    346 
    347 	for (i = 0; i < MAXNETNODES; ++i) {
    348 		nodeingame[i] = false;
    349 			nettics[i] = 0;
    350 		remoteresend[i] = false;
    351 		resendto[i] = 0;
    352 	}
    353 	I_InitNetwork();
    354 	if (doomcom->id != DOOMCOM_ID)
    355 		I_Error ("Doomcom buffer invalid!");
    356 	netbuffer = &doomcom->data;
    357 	consoleplayer = displayplayer = doomcom->consoleplayer;
    358 	if (netgame)
    359 		D_ArbitrateNetStart();
    360 	printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
    361 	ticdup = doomcom->ticdup;
    362 	maxsend = BACKUPTICS/(2 * ticdup) - 1;
    363 	if (maxsend < 1)
    364 		maxsend = 1;
    365 	for (i = 0; i < doomcom->numplayers; ++i)
    366 		playeringame[i] = true;
    367 	for (i = 0; i < doomcom->numnodes; ++i)
    368 		nodeingame[i] = true;
    369 	printf("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
    370 }
    371 
    372 void
    373 D_QuitNetGame()
    374 {
    375 	int i, j;
    376 
    377 	if (debugfile)
    378 		fclose(debugfile);
    379 	if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
    380 		return;
    381 	netbuffer->player = consoleplayer;
    382 	netbuffer->numtics = 0;
    383 	for (i = 0; i < 4; ++i) {
    384 		for (j = 1; j < doomcom->numnodes; ++j)
    385 			if (nodeingame[j])
    386 				HSendPacket(j, NCMD_EXIT);
    387 		I_WaitVBL(1);
    388 	}
    389 }
    390 
    391 int	frametics[4];
    392 int	frameon;
    393 int	frameskip[4];
    394 int	oldnettics;
    395 extern	boolean	advancedemo;
    396 
    397 void
    398 TryRunTics()
    399 {
    400 	static int oldentertics;
    401 	ticcmd_t   *cmd;
    402 	int		   i, j, lowtic, entertic, realtics, availabletics, counts, numplaying, buf;
    403 
    404 	entertic = I_GetTime() / ticdup;
    405 	realtics = entertic - oldentertics;
    406 	oldentertics = entertic;
    407 	NetUpdate();
    408 	lowtic = INT_MAX;
    409 	numplaying = 0;
    410 	for (i = 0; i < doomcom->numnodes; ++i) {
    411 		if (nodeingame[i]) {
    412 			++numplaying;
    413 			if (nettics[i] < lowtic)
    414 				lowtic = nettics[i];
    415 		}
    416 	}
    417 	availabletics = lowtic - gametic/ticdup;
    418 	if (realtics < availabletics-1)
    419 		counts = realtics+1;
    420 	else if (realtics < availabletics)
    421 		counts = realtics;
    422 	else
    423 		counts = availabletics;
    424 	if (counts < 1)
    425 		counts = 1;
    426 	++frameon;
    427 	if (debugfile)
    428 		fprintf (debugfile, "=======real: %i  avail: %i  game: %i\n", realtics, availabletics,counts);
    429 	if (!demoplayback) {
    430 		for (i = 0; i < MAXPLAYERS; ++i)
    431 			if (playeringame[i])
    432 				break;
    433 		if (consoleplayer != i) {
    434 			if (nettics[0] <= nettics[nodeforplayer[i]])
    435 				--gametime;
    436 			frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
    437 			oldnettics = nettics[0];
    438 			if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
    439 				skiptics = 1;
    440 		}
    441 	}
    442 	while (lowtic < gametic/ticdup + counts) {
    443 		NetUpdate();
    444 		lowtic = INT_MAX;
    445 		for (i = 0; i < doomcom->numnodes; ++i)
    446 			if (nodeingame[i] && nettics[i] < lowtic)
    447 				lowtic = nettics[i];
    448 		if (lowtic < gametic/ticdup)
    449 			I_Error("TryRunTics: lowtic < gametic");
    450 		if (I_GetTime ()/ticdup - entertic >= 20) {
    451 			M_Ticker();
    452 			return;
    453 		}
    454 	}
    455 	while (--counts) {
    456 		for (i = 0; i < ticdup; ++i) {
    457 			if (gametic/ticdup > lowtic)
    458 				I_Error("gametic>lowtic");
    459 			if (advancedemo)
    460 				D_DoAdvanceDemo();
    461 			M_Ticker();
    462 			G_Ticker();
    463 			++gametic;
    464 			if (i != ticdup-1) {
    465 				buf = (gametic/ticdup) % BACKUPTICS;
    466 				for (j = 0; j < MAXPLAYERS; ++j) {
    467 					cmd = &netcmds[j][buf];
    468 					cmd->chatchar = 0;
    469 					if (cmd->buttons & BT_SPECIAL)
    470 						cmd->buttons = 0;
    471 				}
    472 			}
    473 		}
    474 		NetUpdate();
    475 	}
    476 }