1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
18 // Revision 1.1 2000/02/29 18:21:04 stegerg
19 // Doom port based on ADoomPPC. Read README.AROS!
23 // DOOM Network game communication and protocol,
24 // all OS independend parts.
26 //-----------------------------------------------------------------------------
29 static const char rcsid
[] = "$Id$";
36 #include <proto/exec.h>
51 #define NCMD_EXIT 0x80000000
52 #define NCMD_RETRANSMIT 0x40000000
53 #define NCMD_SETUP 0x20000000
54 #define NCMD_KILL 0x10000000 // kill game
55 #define NCMD_CHECKSUM 0x0fffffff
59 doomdata_t
* netbuffer
; // points inside doomcom
65 // gametic is the tic about to (or currently being) run
66 // maketic is the tick that hasn't had control made for it yet
67 // nettics[] has the maketics for all players
69 // a gametic cannot be run until nettics[] > gametic for all players
71 #define RESENDCOUNT 10
72 #define PL_DRONE 0x80 // bit flag in doomdata->player
74 ticcmd_t localcmds
[BACKUPTICS
];
76 ticcmd_t netcmds
[MAXPLAYERS
][BACKUPTICS
];
77 int nettics
[MAXNETNODES
];
78 boolean nodeingame
[MAXNETNODES
]; // set false as nodes leave game
79 boolean remoteresend
[MAXNETNODES
]; // set when local needs tics
80 int resendto
[MAXNETNODES
]; // set when remote needs tics
81 int resendcount
[MAXNETNODES
];
83 int nodeforplayer
[MAXPLAYERS
];
89 int maxsend
; // BACKUPTICS/(2*ticdup)-1
92 void D_ProcessEvents (void);
93 void G_BuildTiccmd (ticcmd_t
*cmd
);
94 void D_DoAdvanceDemo (void);
96 boolean reboundpacket
;
97 doomdata_t reboundstore
;
99 static int use_pcchecksum
= 0;
105 int NetbufferSize (void)
107 return (int)&(((doomdata_t
*)0)->cmds
[netbuffer
->numtics
]);
113 unsigned NetbufferChecksum (void)
120 #ifdef __BIG_ENDIAN__
129 return 0; // byte order problems
132 #ifdef __BIG_ENDIAN__
133 c
+= SWAPLONG(*(unsigned *)&netbuffer
->retransmitfrom
);
134 for (i
= 0; i
< netbuffer
->numtics
; i
++) {
135 t
= &netbuffer
->cmds
[i
];
136 c
+= ((i
<< 1) + 2) * (((unsigned char)t
->forwardmove
) +
137 (((unsigned char)t
->sidemove
) << 8) +
138 (((unsigned short)t
->angleturn
) << 16));
139 c
+= ((i
<< 1) + 3) * (((unsigned short)t
->consistancy
) +
140 (((unsigned char)t
->chatchar
) << 16) +
141 (((unsigned char)t
->buttons
) << 24));
143 return c
& NCMD_CHECKSUM
;
146 l
= (NetbufferSize () - (int)&(((doomdata_t
*)0)->retransmitfrom
))/4;
147 for (i
=0 ; i
<l
; i
++)
148 c
+= ((unsigned *)&netbuffer
->retransmitfrom
)[i
] * (i
+1);
150 return c
& NCMD_CHECKSUM
;
157 int ExpandTics (int low
)
161 delta
= low
- (maketic
&0xff);
163 if (delta
>= -64 && delta
<= 64)
164 return (maketic
&~0xff) + low
;
166 return (maketic
&~0xff) - 256 + low
;
168 return (maketic
&~0xff) + 256 + low
;
170 I_Error ("ExpandTics: strange value %i at maketic %i",low
,maketic
);
184 netbuffer
->checksum
= NetbufferChecksum () | flags
;
188 reboundstore
= *netbuffer
;
189 reboundpacket
= true;
197 I_Error ("Tried to transmit to another node");
199 doomcom
->command
= CMD_SEND
;
200 doomcom
->remotenode
= node
;
201 doomcom
->datalength
= NetbufferSize ();
207 if (netbuffer
->checksum
& NCMD_RETRANSMIT
)
208 realretrans
= ExpandTics (netbuffer
->retransmitfrom
);
212 fprintf (debugfile
,"send (%i + %i, R %i) [%i] ",
213 ExpandTics(netbuffer
->starttic
),
214 netbuffer
->numtics
, realretrans
, doomcom
->datalength
);
216 for (i
=0 ; i
<doomcom
->datalength
; i
++)
217 fprintf (debugfile
,"%i ",((byte
*)netbuffer
)[i
]);
219 fprintf (debugfile
,"\n");
227 // Returns false if no packet is waiting
229 boolean
HGetPacket (void)
233 *netbuffer
= reboundstore
;
234 doomcom
->remotenode
= 0;
235 reboundpacket
= false;
245 doomcom
->command
= CMD_GET
;
248 if (doomcom
->remotenode
== -1)
251 if (doomcom
->datalength
!= NetbufferSize ())
254 fprintf (debugfile
,"bad packet length %i\n",doomcom
->datalength
);
258 if (NetbufferChecksum () != (netbuffer
->checksum
&NCMD_CHECKSUM
) )
263 fprintf (debugfile
,"bad packet checksum\n");
264 fprintf (stderr
, "Net Packet Checksum error, %08x != %08x\n",
266 netbuffer
->checksum
&NCMD_CHECKSUM
);
267 fprintf (stderr
, "%08x",
268 ((int *)&netbuffer
->retransmitfrom
)[0]);
269 for (i
= 0; i
< netbuffer
->numtics
; i
++)
270 fprintf (stderr
, " %08x %08x",
271 ((int *)&netbuffer
->cmds
[0])[2*i
],
272 ((int *)&netbuffer
->cmds
[0])[2*i
+1]);
273 fprintf (stderr
, "\n");
282 if (netbuffer
->checksum
& NCMD_SETUP
)
283 fprintf (debugfile
,"setup packet\n");
286 if (netbuffer
->checksum
& NCMD_RETRANSMIT
)
287 realretrans
= ExpandTics (netbuffer
->retransmitfrom
);
291 fprintf (debugfile
,"get %i = (%i + %i, R %i)[%i] ",
293 ExpandTics(netbuffer
->starttic
),
294 netbuffer
->numtics
, realretrans
, doomcom
->datalength
);
296 for (i
=0 ; i
<doomcom
->datalength
; i
++)
297 fprintf (debugfile
,"%i ",((byte
*)netbuffer
)[i
]);
298 fprintf (debugfile
,"\n");
310 void GetPackets (void)
314 ticcmd_t
*src
, *dest
;
318 while ( HGetPacket() )
320 if (netbuffer
->checksum
& NCMD_SETUP
)
321 continue; // extra setup packet
323 netconsole
= netbuffer
->player
& ~PL_DRONE
;
324 netnode
= doomcom
->remotenode
;
326 // to save bytes, only the low byte of tic numbers are sent
327 // Figure out what the rest of the bytes are
328 realstart
= ExpandTics (netbuffer
->starttic
);
329 realend
= (realstart
+netbuffer
->numtics
);
331 // check for exiting the game
332 if (netbuffer
->checksum
& NCMD_EXIT
)
334 if (!nodeingame
[netnode
])
336 nodeingame
[netnode
] = false;
337 playeringame
[netconsole
] = false;
338 strcpy (exitmsg
, "Player 1 left the game");
339 exitmsg
[7] += netconsole
;
340 players
[consoleplayer
].message
= exitmsg
;
342 G_CheckDemoStatus ();
346 // check for a remote game kill
347 if (netbuffer
->checksum
& NCMD_KILL
)
348 I_Error ("Killed by network driver");
350 nodeforplayer
[netconsole
] = netnode
;
352 // check for retransmit request
353 if ( resendcount
[netnode
] <= 0
354 && (netbuffer
->checksum
& NCMD_RETRANSMIT
) )
356 resendto
[netnode
] = ExpandTics(netbuffer
->retransmitfrom
);
358 fprintf (debugfile
,"retransmit from %i\n", resendto
[netnode
]);
359 resendcount
[netnode
] = RESENDCOUNT
;
362 resendcount
[netnode
]--;
364 // check for out of order / duplicated packet
365 if (realend
== nettics
[netnode
])
368 if (realend
< nettics
[netnode
])
372 "out of order packet (%i + %i)\n" ,
373 realstart
,netbuffer
->numtics
);
377 // check for a missed packet
378 if (realstart
> nettics
[netnode
])
380 // stop processing until the other system resends the missed tics
383 "missed tics from %i (%i - %i)\n",
384 netnode
, realstart
, nettics
[netnode
]);
385 remoteresend
[netnode
] = true;
389 // update command store from the packet
393 remoteresend
[netnode
] = false;
395 start
= nettics
[netnode
] - realstart
;
396 src
= &netbuffer
->cmds
[start
];
398 while (nettics
[netnode
] < realend
)
400 dest
= &netcmds
[netconsole
][nettics
[netnode
]%BACKUPTICS
];
412 // Builds ticcmds for console player,
413 // sends out a packet
417 void NetUpdate (void)
426 nowtime
= I_GetTime ()/ticdup
;
427 newtics
= nowtime
- gametime
;
430 if (newtics
<= 0) // nothing new to update
433 if (skiptics
<= newtics
)
445 netbuffer
->player
= consoleplayer
;
447 // build new ticcmds for console player
448 gameticdiv
= gametic
/ticdup
;
449 for (i
=0 ; i
<newtics
; i
++)
453 if (maketic
- gameticdiv
>= BACKUPTICS
/2-1)
454 break; // can't hold any more
456 //printf ("mk:%i ",maketic);
457 G_BuildTiccmd (&localcmds
[maketic
%BACKUPTICS
]);
463 return; // singletic update is syncronous
465 // send the packet to the other nodes
466 for (i
=0 ; i
<doomcom
->numnodes
; i
++)
469 netbuffer
->starttic
= realstart
= resendto
[i
];
470 netbuffer
->numtics
= maketic
- realstart
;
471 if (netbuffer
->numtics
> BACKUPTICS
)
472 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
474 resendto
[i
] = maketic
- doomcom
->extratics
;
476 for (j
=0 ; j
< netbuffer
->numtics
; j
++)
478 localcmds
[(realstart
+j
)%BACKUPTICS
];
482 netbuffer
->retransmitfrom
= nettics
[i
];
483 HSendPacket (i
, NCMD_RETRANSMIT
);
487 netbuffer
->retransmitfrom
= 0;
492 // listen for other packets
502 void CheckAbort (void)
507 stoptic
= I_GetTime () + 2;
508 while (I_GetTime() < stoptic
)
512 for ( ; eventtail
!= eventhead
513 ; eventtail
++, eventtail
= (eventtail
)&(MAXEVENTS
-1) )
515 ev
= &events
[eventtail
];
516 if (ev
->type
== ev_keydown
&& ev
->data1
== KEY_ESCAPE
)
517 I_Error ("Network game synchronization aborted.");
526 // D_ArbitrateNetStart
528 void D_ArbitrateNetStart (void)
531 boolean gotinfo
[MAXNETNODES
];
534 memset (gotinfo
,0,sizeof(gotinfo
));
536 if (doomcom
->consoleplayer
)
538 // listen for setup info from key player
539 printf ("listening for network start info...\n");
543 SetTaskPri(FindTask(NULL
), -1);
544 SetTaskPri(FindTask(NULL
), 0);
549 if (netbuffer
->checksum
& NCMD_SETUP
)
551 if (netbuffer
->player
!= VERSION
)
552 I_Error ("Different DOOM versions cannot play a net game!");
553 startskill
= netbuffer
->retransmitfrom
& 15;
554 deathmatch
= (netbuffer
->retransmitfrom
& 0xc0) >> 6;
555 nomonsters
= (netbuffer
->retransmitfrom
& 0x20) > 0;
556 respawnparm
= (netbuffer
->retransmitfrom
& 0x10) > 0;
557 startmap
= netbuffer
->starttic
& 0x3f;
558 startepisode
= netbuffer
->starttic
>> 6;
565 // key player, send the setup info
566 printf ("sending network start info...\n");
571 //printf("network start info loop: sending packets \n");
572 for (i
=0 ; i
<doomcom
->numnodes
; i
++)
574 netbuffer
->retransmitfrom
= startskill
;
576 netbuffer
->retransmitfrom
|= (deathmatch
<<6);
578 netbuffer
->retransmitfrom
|= 0x20;
580 netbuffer
->retransmitfrom
|= 0x10;
581 netbuffer
->starttic
= startepisode
* 64 + startmap
;
582 netbuffer
->player
= VERSION
;
583 netbuffer
->numtics
= 0;
584 HSendPacket (i
, NCMD_SETUP
);
587 //printf("network start info loop: getting packets \n");
590 for(i
= 10 ; i
&& HGetPacket(); --i
)
592 if((netbuffer
->player
&0x7f) < MAXNETNODES
)
594 //printf("Got Info from Player %d\n",netbuffer->player&0x7f);
595 gotinfo
[netbuffer
->player
&0x7f] = true;
597 //printf("Bad Info from Player %d\n",netbuffer->player&0x7f);
601 while (HGetPacket ())
603 gotinfo
[netbuffer
->player
&0x7f] = true;
607 for (i
=1 ; i
<doomcom
->numnodes
; i
++)
612 SetTaskPri(FindTask(NULL
), -1);
613 SetTaskPri(FindTask(NULL
), 0);
615 } while (i
< doomcom
->numnodes
);
617 //printf("network start info okay for all nodes\n");
624 // Works out player numbers among the net participants
626 extern int viewangleoffset
;
628 void D_CheckNetGame (void)
632 use_pcchecksum
= M_CheckParm ("-pcchecksum");
634 for (i
=0 ; i
<MAXNETNODES
; i
++)
636 nodeingame
[i
] = false;
638 remoteresend
[i
] = false; // set when local needs tics
639 resendto
[i
] = 0; // which tic to start sending
642 // I_InitNetwork sets doomcom and netgame
644 if (doomcom
->id
!= DOOMCOM_ID
)
645 I_Error ("Doomcom buffer invalid!");
647 netbuffer
= &doomcom
->data
;
648 consoleplayer
= displayplayer
= doomcom
->consoleplayer
;
650 D_ArbitrateNetStart ();
652 printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
653 startskill
, deathmatch
, startmap
, startepisode
);
655 // read values out of doomcom
656 ticdup
= doomcom
->ticdup
;
657 maxsend
= BACKUPTICS
/(2*ticdup
)-1;
661 for (i
=0 ; i
<doomcom
->numplayers
; i
++)
662 playeringame
[i
] = true;
663 for (i
=0 ; i
<doomcom
->numnodes
; i
++)
664 nodeingame
[i
] = true;
666 printf ("player %i of %i (%i nodes)\n",
667 consoleplayer
+1, doomcom
->numplayers
, doomcom
->numnodes
);
674 // Called before quitting to leave a net game
675 // without hanging the other players
677 void D_QuitNetGame (void)
684 if (!netgame
|| !usergame
|| consoleplayer
== -1 || demoplayback
)
687 // send a bunch of packets for security
688 netbuffer
->player
= consoleplayer
;
689 netbuffer
->numtics
= 0;
690 for (i
=0 ; i
<4 ; i
++)
692 for (j
=1 ; j
<doomcom
->numnodes
; j
++)
694 HSendPacket (j
, NCMD_EXIT
);
709 extern boolean advancedemo
;
711 void TryRunTics (void)
716 static int oldentertics
;
723 entertic
= I_GetTime ()/ticdup
;
724 realtics
= entertic
- oldentertics
;
725 oldentertics
= entertic
;
727 // get available tics
732 for (i
=0 ; i
<doomcom
->numnodes
; i
++)
737 if (nettics
[i
] < lowtic
)
741 availabletics
= lowtic
- gametic
/ticdup
;
743 // decide how many tics to run
744 if (realtics
< availabletics
-1)
746 else if (realtics
< availabletics
)
749 counts
= availabletics
;
758 "=======real: %i avail: %i game: %i\n",
759 realtics
, availabletics
,counts
);
763 // ideally nettics[0] should be 1 - 3 tics above lowtic
764 // if we are consistantly slower, speed up time
765 for (i
=0 ; i
<MAXPLAYERS
; i
++)
768 if (consoleplayer
== i
)
770 // the key player does not adapt
774 if (nettics
[0] <= nettics
[nodeforplayer
[i
]])
779 frameskip
[frameon
&3] = (oldnettics
> nettics
[nodeforplayer
[i
]]);
780 oldnettics
= nettics
[0];
781 if (frameskip
[0] && frameskip
[1] && frameskip
[2] && frameskip
[3])
789 // wait for new tics if needed
790 while (lowtic
< gametic
/ticdup
+ counts
)
795 for (i
=0 ; i
<doomcom
->numnodes
; i
++)
796 if (nodeingame
[i
] && nettics
[i
] < lowtic
)
799 if (lowtic
< gametic
/ticdup
)
800 I_Error ("TryRunTics: lowtic < gametic");
802 // don't stay in here forever -- give the menu a chance to work
803 if (I_GetTime ()/ticdup
- entertic
>= 20)
810 // run the count * ticdup dics
813 for (i
=0 ; i
<ticdup
; i
++)
815 if (gametic
/ticdup
> lowtic
)
816 I_Error ("gametic>lowtic");
823 // modify command for duplicated tics
830 buf
= (gametic
/ticdup
)%BACKUPTICS
;
831 for (j
=0 ; j
<MAXPLAYERS
; j
++)
833 cmd
= &netcmds
[j
][buf
];
835 if (cmd
->buttons
& BT_SPECIAL
)
840 NetUpdate (); // check for new console commands