1
2 /*
3 * d_net.c
4 * This version has the fixed ticdup code
5 */
6
7 #ifdef HAVE_USLEEP
8 #include <unistd.h>
9 #endif
10 #include "doomdef.h"
11
12 #define NCMD_EXIT 0x80000000
13 #define NCMD_RETRANSMIT 0x40000000
14 #define NCMD_SETUP 0x20000000
15 #define NCMD_KILL 0x10000000 /* kill game */
16 #define NCMD_CHECKSUM 0x0fffffff
17
18
19 doomcom_t *doomcom;
20 doomdata_t *netbuffer; /* points inside doomcom */
21
22
23 /*
24 ==============================================================================
25
26 NETWORKING
27
28 gametic is the tic about to (or currently being) run
29 maketic is the tick that hasn't had control made for it yet
30 nettics[] has the maketics for all players
31
32 a gametic cannot be run until nettics[] > gametic for all players
33
34 ==============================================================================
35 */
36
37 #define RESENDCOUNT 10
38 #define PL_DRONE 0x80 /* bit flag in doomdata->player */
39
40 ticcmd_t localcmds[BACKUPTICS];
41
42 ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
43 int nettics[MAXNETNODES];
44 boolean nodeingame[MAXNETNODES]; /* set false as nodes leave game */
45 boolean remoteresend[MAXNETNODES]; /* set when local needs tics */
46 int resendto[MAXNETNODES]; /* set when remote needs tics */
47 int resendcount[MAXNETNODES];
48
49 int nodeforplayer[MAXPLAYERS];
50
51 int maketic;
52 int lastnettic, skiptics;
53 int ticdup;
54 int maxsend; /* BACKUPTICS/(2*ticdup)-1 */
55
56 void D_ProcessEvents (void);
57 void G_BuildTiccmd (ticcmd_t *cmd);
58 void D_DoAdvanceDemo (void);
59
60 boolean reboundpacket;
61 doomdata_t reboundstore;
62
63
NetbufferSize(void)64 unsigned long NetbufferSize (void)
65 {
66 return (unsigned long)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
67 }
68
NetbufferChecksum(void)69 unsigned NetbufferChecksum (void)
70 {
71 unsigned int c;
72 int i,l;
73
74 c = 0x1234567;
75
76 #if defined(NeXT)
77 return 0; /* byte order problems */
78 #endif
79
80 l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
81 for (i=0 ; i<l ; i++)
82 c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
83
84 return c & NCMD_CHECKSUM;
85 }
86
ExpandTics(int low)87 int ExpandTics (int low)
88 {
89 int delta;
90
91 delta = low - (maketic&0xff);
92
93 if (delta >= -64 && delta <= 64)
94 return (maketic&~0xff) + low;
95 if (delta > 64)
96 return (maketic&~0xff) - 256 + low;
97 if (delta < -64)
98 return (maketic&~0xff) + 256 + low;
99
100 I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
101 return 0;
102 }
103
104
105 /* ============================================================================ */
106
107
108 /*
109 ==============
110 =
111 = HSendPacket
112 =
113 ==============
114 */
115
HSendPacket(int node,int flags)116 void HSendPacket (int node, int flags)
117 {
118 netbuffer->checksum = NetbufferChecksum () | flags;
119
120 /* if (!node) */
121 if (node == doomcom->consoleplayer)
122 {
123 reboundstore = *netbuffer;
124 reboundpacket = true;
125 return;
126 }
127
128 if (demoplayback)
129 return;
130
131 if (!netgame)
132 I_Error ("Tried to transmit to another node");
133
134 doomcom->command = CMD_SEND;
135 doomcom->remotenode = node;
136 doomcom->datalength = NetbufferSize ();
137
138 if (debugfile)
139 {
140 int i;
141 int realretrans;
142 if (netbuffer->checksum & NCMD_RETRANSMIT)
143 realretrans = ExpandTics (netbuffer->retransmitfrom);
144 else
145 realretrans = -1;
146 fprintf (debugfile,"send (%i + %i, R %i) [%i] "
147 ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
148 for (i=0 ; i<doomcom->datalength ; i++)
149 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
150 fprintf (debugfile,"\n");
151 }
152
153 I_NetCmd ();
154 }
155
156 /*
157 ==============
158 =
159 = HGetPacket
160 =
161 = Returns false if no packet is waiting
162 =
163 ==============
164 */
165
HGetPacket(void)166 boolean HGetPacket (void)
167 {
168 if (reboundpacket)
169 {
170 *netbuffer = reboundstore;
171 /* doomcom->remotenode = 0; */
172 doomcom->remotenode = doomcom->consoleplayer;
173 reboundpacket = false;
174 return true;
175 }
176
177 if (!netgame)
178 return false;
179 if (demoplayback)
180 return false;
181
182 doomcom->command = CMD_GET;
183 I_NetCmd ();
184 if (doomcom->remotenode == -1)
185 return false;
186
187 if (doomcom->datalength != NetbufferSize ())
188 {
189 if (debugfile)
190 fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
191 return false;
192 }
193
194 if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
195 {
196 if (debugfile)
197 fprintf (debugfile,"bad packet checksum\n");
198 return false;
199 }
200
201 if (debugfile)
202 {
203 int realretrans;
204 int i;
205
206 if (netbuffer->checksum & NCMD_SETUP)
207 fprintf (debugfile,"setup packet\n");
208 else
209 {
210 if (netbuffer->checksum & NCMD_RETRANSMIT)
211 realretrans = ExpandTics (netbuffer->retransmitfrom);
212 else
213 realretrans = -1;
214 fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
215 ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
216 for (i=0 ; i<doomcom->datalength ; i++)
217 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
218 fprintf (debugfile,"\n");
219 }
220 }
221 return true;
222 }
223
224
225 /*
226 ===================
227 =
228 = GetPackets
229 =
230 ===================
231 */
232
233 char exitmsg[80];
234
GetPackets(void)235 void GetPackets (void)
236 {
237 int netconsole;
238 int netnode;
239 ticcmd_t *src, *dest;
240 int realend;
241 int realstart;
242
243 while (HGetPacket ())
244 {
245 if (netbuffer->checksum & NCMD_SETUP)
246 continue; /* extra setup packet */
247
248 netconsole = netbuffer->player & ~PL_DRONE;
249 netnode = doomcom->remotenode;
250 /*
251 * to save bytes, only the low byte of tic numbers are sent
252 * Figure out what the rest of the bytes are
253 */
254 realstart = ExpandTics (netbuffer->starttic);
255 realend = (realstart+netbuffer->numtics);
256
257 /*
258 * check for exiting the game
259 */
260 if (netbuffer->checksum & NCMD_EXIT)
261 {
262 if (!nodeingame[netnode])
263 continue;
264 nodeingame[netnode] = false;
265 playeringame[netconsole] = false;
266 strcpy (exitmsg, "PLAYER 1 LEFT THE GAME");
267 exitmsg[7] += netconsole;
268 players[consoleplayer].message = exitmsg;
269 players[consoleplayer].messageTics = MESSAGETICS;
270
271 /*
272 * if (demorecording)
273 * G_CheckDemoStatus ();
274 */
275 continue;
276 }
277
278 /*
279 * check for a remote game kill
280 */
281 if (netbuffer->checksum & NCMD_KILL)
282 I_Error ("Killed by network driver");
283
284 nodeforplayer[netconsole] = netnode;
285
286 /*
287 * check for retransmit request
288 */
289 if ( resendcount[netnode] <= 0
290 && (netbuffer->checksum & NCMD_RETRANSMIT) )
291 {
292 resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
293 if (debugfile)
294 fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
295 resendcount[netnode] = RESENDCOUNT;
296 }
297 else
298 resendcount[netnode]--;
299
300 /*
301 * check for out of order / duplicated packet
302 */
303 if (realend == nettics[netnode])
304 continue;
305
306 if (realend < nettics[netnode])
307 {
308 if (debugfile)
309 fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
310 continue;
311 }
312
313 /*
314 * check for a missed packet
315 */
316 if (realstart > nettics[netnode])
317 {
318 /* stop processing until the other system resends the missed tics */
319 if (debugfile)
320 fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
321 remoteresend[netnode] = true;
322 continue;
323 }
324
325 /*
326 * update command store from the packet
327 */
328 {
329 int start;
330
331 remoteresend[netnode] = false;
332
333 start = nettics[netnode] - realstart;
334 src = &netbuffer->cmds[start];
335
336 while (nettics[netnode] < realend)
337 {
338 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
339 nettics[netnode]++;
340 *dest = *src;
341 src++;
342 }
343 }
344 }
345 }
346
347 /*
348 =============
349 =
350 = NetUpdate
351 =
352 = Builds ticcmds for console player
353 = sends out a packet
354 =============
355 */
356
357 int gametime;
358
NetUpdate(void)359 void NetUpdate (void)
360 {
361 int nowtime;
362 int newtics;
363 int i,j;
364 int realstart;
365 int gameticdiv;
366
367 /*
368 * check time
369 */
370 nowtime = I_GetTime ()/ticdup;
371 newtics = nowtime - gametime;
372 gametime = nowtime;
373
374 if (newtics <= 0) /* nothing new to update */
375 goto listen;
376
377 if (skiptics <= newtics)
378 {
379 newtics -= skiptics;
380 skiptics = 0;
381 }
382 else
383 {
384 skiptics -= newtics;
385 newtics = 0;
386 }
387
388
389 netbuffer->player = consoleplayer;
390
391 /*
392 * build new ticcmds for console player
393 */
394 gameticdiv = gametic/ticdup;
395 for (i=0 ; i<newtics ; i++)
396 {
397 I_StartTic ();
398 D_ProcessEvents ();
399 if (maketic - gameticdiv >= BACKUPTICS/2-1)
400 break; /* can't hold any more */
401 /* printf ("mk:%i ",maketic); */
402 G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
403 maketic++;
404 }
405
406
407 if (singletics)
408 return; /* singletic update is syncronous */
409
410 /*
411 * send the packet to the other nodes
412 */
413 for (i=0 ; i<doomcom->numnodes ; i++)
414 if (nodeingame[i])
415 {
416 netbuffer->starttic = realstart = resendto[i];
417 netbuffer->numtics = maketic - realstart;
418 if (netbuffer->numtics > BACKUPTICS)
419 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
420
421 resendto[i] = maketic - doomcom->extratics;
422
423 for (j=0 ; j< netbuffer->numtics ; j++)
424 netbuffer->cmds[j] =
425 localcmds[(realstart+j)%BACKUPTICS];
426
427 if (remoteresend[i])
428 {
429 netbuffer->retransmitfrom = nettics[i];
430 HSendPacket (i, NCMD_RETRANSMIT);
431 }
432 else
433 {
434 netbuffer->retransmitfrom = 0;
435 HSendPacket (i, 0);
436 }
437 }
438
439 /*
440 * listen for other packets
441 */
442 listen:
443
444 GetPackets ();
445 }
446
447
448 /*
449 =====================
450 =
451 = CheckAbort
452 =
453 =====================
454 */
455
CheckAbort(void)456 void CheckAbort (void)
457 {
458 event_t *ev;
459 int stoptic;
460
461 stoptic = I_GetTime () + 2;
462 while (I_GetTime() < stoptic)
463 I_StartTic ();
464
465 I_StartTic ();
466 for ( ; eventtail != eventhead
467 ; eventtail = (++eventtail)&(MAXEVENTS-1) )
468 {
469 ev = &events[eventtail];
470 if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
471 I_Error ("Network game synchronization aborted.");
472 }
473 }
474
475 /*
476 =====================
477 =
478 = D_ArbitrateNetStart
479 =
480 =====================
481 */
482
D_ArbitrateNetStart(void)483 void D_ArbitrateNetStart (void)
484 {
485 int i;
486 boolean gotinfo[MAXNETNODES];
487
488 autostart = true;
489 memset (gotinfo,0,sizeof(gotinfo));
490
491 if (doomcom->consoleplayer)
492 { /* listen for setup info from key player */
493 printf ("listening for network start info...\n");
494 while (1)
495 {
496 CheckAbort ();
497 if (!HGetPacket ())
498 continue;
499 if (netbuffer->checksum & NCMD_SETUP)
500 {
501 if (netbuffer->player != VERSION)
502 I_Error ("Different Heretic versions cannot play a net game!");
503 startskill = netbuffer->retransmitfrom & 15;
504 deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
505 nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
506 respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
507 /* startmap = netbuffer->starttic & 0x3f; */
508 /* startepisode = netbuffer->starttic >> 6; */
509 startmap = netbuffer->starttic&15;
510 startepisode = netbuffer->starttic>>4;
511 return;
512 }
513 }
514 }
515 else
516 { /* key player, send the setup info */
517 printf ("sending network start info...\n");
518 do
519 {
520 CheckAbort ();
521 for (i=0 ; i<doomcom->numnodes ; i++)
522 {
523 netbuffer->retransmitfrom = startskill;
524 if (deathmatch)
525 netbuffer->retransmitfrom |= (deathmatch<<6);
526 if (nomonsters)
527 netbuffer->retransmitfrom |= 0x20;
528 if (respawnparm)
529 netbuffer->retransmitfrom |= 0x10;
530 /* netbuffer->starttic = startepisode * 64 + startmap; */
531 netbuffer->starttic = (startepisode<<4)+startmap;
532 netbuffer->player = VERSION;
533 netbuffer->numtics = 0;
534 HSendPacket (i, NCMD_SETUP);
535 }
536
537 #if 1
538 for(i = 10 ; i && HGetPacket(); --i)
539 {
540 if((netbuffer->player&0x7f) < MAXNETNODES)
541 gotinfo[netbuffer->player&0x7f] = true;
542 }
543 #else
544 while (HGetPacket ())
545 {
546 gotinfo[netbuffer->player&0x7f] = true;
547 }
548 #endif
549
550 for (i=1 ; i<doomcom->numnodes ; i++)
551 if (!gotinfo[i])
552 break;
553 } while (i < doomcom->numnodes);
554 }
555 }
556
557 /*
558 ===================
559 =
560 = D_CheckNetGame
561 =
562 = Works out player numbers among the net participants
563 ===================
564 */
565
566 extern int viewangleoffset;
567
D_CheckNetGame(void)568 void D_CheckNetGame (void)
569 {
570 int i;
571
572 for (i=0 ; i<MAXNETNODES ; i++)
573 {
574 nodeingame[i] = false;
575 nettics[i] = 0;
576 remoteresend[i] = false; /* set when local needs tics */
577 resendto[i] = 0; /* which tic to start sending */
578 }
579
580 /* I_InitNetwork sets doomcom and netgame */
581 I_InitNetwork ();
582 if (doomcom->id != DOOMCOM_ID)
583 I_Error ("Doomcom buffer invalid!");
584 netbuffer = &doomcom->data;
585 consoleplayer = displayplayer = doomcom->consoleplayer;
586 if (netgame)
587 D_ArbitrateNetStart ();
588 printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
589
590 /* read values out of doomcom */
591 ticdup = doomcom->ticdup;
592 maxsend = BACKUPTICS/(2*ticdup)-1;
593 if (maxsend<1)
594 maxsend = 1;
595
596 for (i=0 ; i<doomcom->numplayers ; i++)
597 playeringame[i] = true;
598 for (i=0 ; i<doomcom->numnodes ; i++)
599 nodeingame[i] = true;
600
601 printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
602
603 }
604
605 /*
606 ==================
607 =
608 = D_QuitNetGame
609 =
610 = Called before quitting to leave a net game without hanging the
611 = other players
612 =
613 ==================
614 */
615
D_QuitNetGame(void)616 void D_QuitNetGame (void)
617 {
618 int i, j;
619
620 if (debugfile)
621 fclose (debugfile);
622
623 if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
624 return;
625
626 /* send a bunch of packets for security */
627 netbuffer->player = consoleplayer;
628 netbuffer->numtics = 0;
629 for (i=0 ; i<4 ; i++)
630 {
631 for (j=0 ; j<doomcom->numnodes ; j++)
632 if (nodeingame[j])
633 HSendPacket (j, NCMD_EXIT);
634 I_WaitVBL (1);
635 }
636 }
637
638
639
640 /*
641 ===============
642 =
643 = TryRunTics
644 =
645 ===============
646 */
647
648 int frametics[4], frameon;
649 int frameskip[4];
650 int oldnettics;
651 extern boolean advancedemo;
652
TryRunTics(void)653 void TryRunTics (void)
654 {
655 int i;
656 int lowtic;
657 int entertic;
658 static int oldentertics;
659 int realtics, availabletics;
660 int counts;
661 int numplaying;
662
663 /*
664 * get real tics
665 */
666 entertic = I_GetTime ()/ticdup;
667 realtics = entertic - oldentertics;
668 oldentertics = entertic;
669
670 /*
671 * get available tics
672 */
673 NetUpdate ();
674
675 lowtic = MAXINT;
676 numplaying = 0;
677 for (i=0 ; i<doomcom->numnodes ; i++)
678 if (nodeingame[i])
679 {
680 numplaying++;
681 if (nettics[i] < lowtic)
682 lowtic = nettics[i];
683 }
684 availabletics = lowtic - gametic/ticdup;
685
686
687 /*
688 * decide how many tics to run
689 */
690 if (realtics < availabletics-1)
691 counts = realtics+1;
692 else if (realtics < availabletics)
693 counts = realtics;
694 else
695 counts = availabletics;
696 if (counts < 1)
697 counts = 1;
698
699 frameon++;
700
701 if (debugfile)
702 fprintf (debugfile,"=======real: %i avail: %i game: %i\n",realtics, availabletics,counts);
703
704 if (!demoplayback)
705 {
706 /* =============================================================================
707 *
708 * ideally nettics[0] should be 1 - 3 tics above lowtic
709 * if we are consistantly slower, speed up time
710 *
711 */
712
713 for (i=0 ; i<MAXPLAYERS ; i++)
714 if (playeringame[i])
715 break;
716 if (consoleplayer == i)
717 { /* the key player does not adapt */
718 }
719 else
720 {
721 if (nettics[0] <= nettics[nodeforplayer[i]])
722 {
723 gametime--;
724 /* printf ("-"); */
725 }
726 frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
727 oldnettics = nettics[0];
728 if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
729 {
730 skiptics = 1;
731 /* printf ("+"); */
732 }
733 }
734 }
735
736 /* =============================================================================
737 * demoplayback
738 *
739 * wait for new tics if needed
740 *
741 */
742 while (lowtic < gametic/ticdup + counts)
743 {
744 #ifdef HAVE_USLEEP
745 /* Busywaiting is evil! We sleep 1/8 tic for each iteration. */
746 usleep((1000000/TICRATE)/8);
747 #endif
748
749 NetUpdate ();
750 lowtic = MAXINT;
751
752 for (i=0 ; i<doomcom->numnodes ; i++)
753 if (nodeingame[i] && nettics[i] < lowtic)
754 lowtic = nettics[i];
755
756 if (lowtic < gametic/ticdup)
757 I_Error ("TryRunTics: lowtic < gametic");
758
759 /* don't stay in here forever -- give the menu a chance to work */
760 if (I_GetTime ()/ticdup - entertic >= 20)
761 {
762 MN_Ticker ();
763 return;
764 }
765 }
766
767 /*
768 * run the count * ticdup dics
769 */
770 while (counts--)
771 {
772 for (i=0 ; i<ticdup ; i++)
773 {
774 if (gametic/ticdup > lowtic)
775 I_Error ("gametic>lowtic");
776 if (advancedemo)
777 D_DoAdvanceDemo ();
778 MN_Ticker ();
779 G_Ticker ();
780 gametic++;
781 /*
782 * modify command for duplicated tics
783 */
784 if (i != ticdup-1)
785 {
786 ticcmd_t *cmd;
787 int buf;
788 int j;
789
790 buf = (gametic/ticdup)%BACKUPTICS;
791 for (j=0 ; j<MAXPLAYERS ; j++)
792 {
793 cmd = &netcmds[j][buf];
794 cmd->chatchar = 0;
795 if (cmd->buttons & BT_SPECIAL)
796 cmd->buttons = 0;
797 }
798 }
799 }
800 NetUpdate (); /* check for new console commands */
801 }
802 }
803
804
805