aboutsummaryrefslogtreecommitdiff
path: root/src/d_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/d_client.c')
-rw-r--r--src/d_client.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/src/d_client.c b/src/d_client.c
new file mode 100644
index 0000000..dd09720
--- /dev/null
+++ b/src/d_client.c
@@ -0,0 +1,539 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Network client. Passes information to/from server, staying
31 * synchronised.
32 * Contains the main wait loop, waiting for network input or
33 * time before doing the next tic.
34 * Rewritten for LxDoom, but based around bits of the old code.
35 *
36 *-----------------------------------------------------------------------------
37 */
38
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#endif
42#include <sys/types.h>
43#ifdef HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46#ifdef HAVE_SYS_WAIT_H
47#include <sys/wait.h>
48#endif
49
50#ifdef USE_SDL_NET
51 #include "SDL.h"
52#endif
53
54#include "doomtype.h"
55#include "doomstat.h"
56#include "d_net.h"
57#include "z_zone.h"
58
59#include "d_main.h"
60#include "g_game.h"
61#include "m_menu.h"
62#include "p_checksum.h"
63
64#include "protocol.h"
65#include "i_network.h"
66#include "i_system.h"
67#include "i_main.h"
68#include "i_video.h"
69#include "m_argv.h"
70#include "r_fps.h"
71#include "lprintf.h"
72
73static boolean server;
74static int remotetic; // Tic expected from the remote
75static int remotesend; // Tic expected by the remote
76ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
77static ticcmd_t* localcmds;
78static unsigned numqueuedpackets;
79static packet_header_t** queuedpacket;
80int maketic;
81int ticdup = 1;
82static int xtratics = 0;
83int wanted_player_number;
84
85static boolean isExtraDDisplay = false;
86
87static void D_QuitNetGame (void);
88
89#ifndef HAVE_NET
90doomcom_t* doomcom;
91#endif
92
93#ifdef HAVE_NET
94void D_InitNetGame (void)
95{
96 int i;
97 int numplayers = 1;
98
99 i = M_CheckParm("-net");
100 if (i && i < myargc-1) i++;
101
102 if (!(netgame = server = !!i)) {
103 playeringame[consoleplayer = 0] = true;
104 // e6y
105 // for play, recording or playback using "single-player coop" mode.
106 // Equivalent to using prboom_server with -N 1
107 netgame = M_CheckParm("-solo-net");
108 } else {
109 // Get game info from server
110 packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL);
111 struct setup_packet_s *sinfo = (void*)(packet+1);
112 struct { packet_header_t head; short pn; } PACKEDATTR initpacket;
113
114 I_InitNetwork();
115 udp_socket = I_Socket(0);
116 I_ConnectToServer(myargv[i]);
117
118 do
119 {
120 do {
121 // Send init packet
122 initpacket.pn = doom_htons(wanted_player_number);
123 packet_set(&initpacket.head, PKT_INIT, 0);
124 I_SendPacket(&initpacket.head, sizeof(initpacket));
125 I_WaitForPacket(5000);
126 } while (!I_GetPacket(packet, 1000));
127 if (packet->type == PKT_DOWN) I_Error("Server aborted the game");
128 } while (packet->type != PKT_SETUP);
129
130 // Once we have been accepted by the server, we should tell it when we leave
131 atexit(D_QuitNetGame);
132
133 // Get info from the setup packet
134 consoleplayer = sinfo->yourplayer;
135 compatibility_level = sinfo->complevel;
136 G_Compatibility();
137 startskill = sinfo->skill;
138 deathmatch = sinfo->deathmatch;
139 startmap = sinfo->level;
140 startepisode = sinfo->episode;
141 ticdup = sinfo->ticdup;
142 xtratics = sinfo->extratic;
143 G_ReadOptions(sinfo->game_options);
144
145 lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n",
146 consoleplayer+1, numplayers = sinfo->players, sinfo->numwads);
147 {
148 char *p = sinfo->wadnames;
149 int i = sinfo->numwads;
150
151 while (i--) {
152 D_AddFile(p, source_net);
153 p += strlen(p) + 1;
154 }
155 }
156 Z_Free(packet);
157 }
158 localcmds = netcmds[displayplayer = consoleplayer];
159 for (i=0; i<numplayers; i++)
160 playeringame[i] = true;
161 for (; i<MAXPLAYERS; i++)
162 playeringame[i] = false;
163 if (!playeringame[consoleplayer]) I_Error("D_InitNetGame: consoleplayer not in game");
164}
165#else
166void D_InitNetGame (void)
167{
168 int i;
169
170 doomcom = Z_Malloc(sizeof *doomcom, PU_STATIC, NULL);
171 doomcom->consoleplayer = 0;
172 doomcom->numnodes = 0; doomcom->numplayers = 1;
173 localcmds = netcmds[consoleplayer];
174 netgame = (M_CheckParm("-solo-net") != 0);
175
176 for (i=0; i<doomcom->numplayers; i++)
177 playeringame[i] = true;
178 for (; i<MAXPLAYERS; i++)
179 playeringame[i] = false;
180
181 consoleplayer = displayplayer = doomcom->consoleplayer;
182}
183#endif // HAVE_NET
184
185#ifdef HAVE_NET
186void D_CheckNetGame(void)
187{
188 packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL);
189
190 if (server) {
191 lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n");
192 do {
193 while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) {
194 packet_set(packet, PKT_GO, 0);
195 *(byte*)(packet+1) = consoleplayer;
196 I_SendPacket(packet, sizeof(packet_header_t)+1);
197 I_uSleep(100000);
198 }
199 } while (packet->type != PKT_GO);
200 }
201 Z_Free(packet);
202}
203
204boolean D_NetGetWad(const char* name)
205{
206#if defined(HAVE_WAIT_H)
207 size_t psize = sizeof(packet_header_t) + strlen(name) + 500;
208 packet_header_t *packet;
209 boolean done = false;
210
211 if (!server || strchr(name, '/')) return false; // If it contains path info, reject
212
213 do {
214 // Send WAD request to remote
215 packet = Z_Malloc(psize, PU_STATIC, NULL);
216 packet_set(packet, PKT_WAD, 0);
217 *(byte*)(packet+1) = consoleplayer;
218 strcpy(1+(byte*)(packet+1), name);
219 I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2);
220
221 I_uSleep(10000);
222 } while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD));
223 Z_Free(packet);
224
225 if (!strcasecmp((void*)(packet+1), name)) {
226 pid_t pid;
227 int rv;
228 byte *p = (byte*)(packet+1) + strlen(name) + 1;
229
230 /* Automatic wad file retrieval using wget (supports http and ftp, using URLs)
231 * Unix systems have all these commands handy, this kind of thing is easy
232 * Any windo$e port will have some awkward work replacing these.
233 */
234 /* cph - caution here. This is data from an untrusted source.
235 * Don't pass it via a shell. */
236 if ((pid = fork()) == -1)
237 perror("fork");
238 else if (!pid) {
239 /* Child chains to wget, does the download */
240 execlp("wget", "wget", p, NULL);
241 }
242 /* This is the parent, i.e. main LxDoom process */
243 wait(&rv);
244 if (!(done = !access(name, R_OK))) {
245 if (!strcmp(p+strlen(p)-4, ".zip")) {
246 p = strrchr(p, '/')+1;
247 if ((pid = fork()) == -1)
248 perror("fork");
249 else if (!pid) {
250 /* Child executes decompressor */
251 execlp("unzip", "unzip", p, name, NULL);
252 }
253 /* Parent waits for the file */
254 wait(&rv);
255 done = !!access(name, R_OK);
256 }
257 /* Add more decompression protocols here as desired */
258 }
259 Z_Free(buffer);
260 }
261 return done;
262#else /* HAVE_WAIT_H */
263 return false;
264#endif
265}
266
267void NetUpdate(void)
268{
269 static int lastmadetic;
270 if (isExtraDDisplay)
271 return;
272 if (server) { // Receive network packets
273 size_t recvlen;
274 packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL);
275 while ((recvlen = I_GetPacket(packet, 10000))) {
276 switch(packet->type) {
277 case PKT_TICS:
278 {
279 byte *p = (void*)(packet+1);
280 int tics = *p++;
281 unsigned long ptic = doom_ntohl(packet->tic);
282 if (ptic > (unsigned)remotetic) { // Missed some
283 packet_set(packet, PKT_RETRANS, remotetic);
284 *(byte*)(packet+1) = consoleplayer;
285 I_SendPacket(packet, sizeof(*packet)+1);
286 } else {
287 if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things
288 remotetic = ptic;
289 while (tics--) {
290 int players = *p++;
291 while (players--) {
292 int n = *p++;
293 RawToTic(&netcmds[n][remotetic%BACKUPTICS], p);
294 p += sizeof(ticcmd_t);
295 }
296 remotetic++;
297 }
298 }
299 }
300 break;
301 case PKT_RETRANS: // Resend request
302 remotesend = doom_ntohl(packet->tic);
303 break;
304 case PKT_DOWN: // Server downed
305 {
306 int j;
307 for (j=0; j<MAXPLAYERS; j++)
308 if (j != consoleplayer) playeringame[j] = false;
309 server = false;
310 doom_printf("Server is down\nAll other players are no longer in the game\n");
311 }
312 break;
313 case PKT_EXTRA: // Misc stuff
314 case PKT_QUIT: // Player quit
315 // Queue packet to be processed when its tic time is reached
316 queuedpacket = Z_Realloc(queuedpacket, ++numqueuedpackets * sizeof *queuedpacket,
317 PU_STATIC, NULL);
318 queuedpacket[numqueuedpackets-1] = Z_Malloc(recvlen, PU_STATIC, NULL);
319 memcpy(queuedpacket[numqueuedpackets-1], packet, recvlen);
320 break;
321 case PKT_BACKOFF:
322 /* cph 2003-09-18 -
323 * The server sends this when we have got ahead of the other clients. We should
324 * stall the input side on this client, to allow other clients to catch up.
325 */
326 lastmadetic++;
327 break;
328 default: // Other packet, unrecognised or redundant
329 break;
330 }
331 }
332 Z_Free(packet);
333 }
334 { // Build new ticcmds
335 int newtics = I_GetTime() - lastmadetic;
336 newtics = (newtics > 0 ? newtics : 0);
337 lastmadetic += newtics;
338 if (ffmap) newtics++;
339 while (newtics--) {
340 I_StartTic();
341 if (maketic - gametic > BACKUPTICS/2) break;
342 G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]);
343 maketic++;
344 }
345 if (server && maketic > remotesend) { // Send the tics to the server
346 int sendtics;
347 remotesend -= xtratics;
348 if (remotesend < 0) remotesend = 0;
349 sendtics = maketic - remotesend;
350 {
351 size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t);
352 packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL);
353
354 packet_set(packet, PKT_TICC, maketic - sendtics);
355 *(byte*)(packet+1) = sendtics;
356 *(((byte*)(packet+1))+1) = consoleplayer;
357 {
358 void *tic = ((byte*)(packet+1)) +2;
359 while (sendtics--) {
360 TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]);
361 tic = (byte *)tic + sizeof(ticcmd_t);
362 }
363 }
364 I_SendPacket(packet, pkt_size);
365 Z_Free(packet);
366 }
367 }
368 }
369}
370#else
371
372void D_BuildNewTiccmds(void)
373{
374 static int lastmadetic;
375 int newtics = I_GetTime() - lastmadetic;
376 lastmadetic += newtics;
377 while (newtics--)
378 {
379 I_StartTic();
380 if (maketic - gametic > BACKUPTICS/2) break;
381 G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]);
382 maketic++;
383 }
384}
385#endif
386
387#ifdef HAVE_NET
388/* cph - data passed to this must be in the Doom (little-) endian */
389void D_NetSendMisc(netmisctype_t type, size_t len, void* data)
390{
391 if (server) {
392 size_t size = sizeof(packet_header_t) + 3*sizeof(int) + len;
393 packet_header_t *packet = Z_Malloc(size, PU_STATIC, NULL);
394 int *p = (void*)(packet+1);
395
396 packet_set(packet, PKT_EXTRA, gametic);
397 *p++ = LONG(type); *p++ = LONG(consoleplayer); *p++ = LONG(len);
398 memcpy(p, data, len);
399 I_SendPacket(packet, size);
400
401 Z_Free(packet);
402 }
403}
404
405static void CheckQueuedPackets(void)
406{
407 int i;
408 for (i=0; (unsigned)i<numqueuedpackets; i++)
409 if (doom_ntohl(queuedpacket[i]->tic) <= gametic)
410 switch (queuedpacket[i]->type) {
411 case PKT_QUIT: // Player quit the game
412 {
413 int pn = *(byte*)(queuedpacket[i]+1);
414 playeringame[pn] = false;
415 doom_printf("Player %d left the game\n", pn);
416 }
417 break;
418 case PKT_EXTRA:
419 {
420 int *p = (int*)(queuedpacket[i]+1);
421 size_t len = LONG(*(p+2));
422 switch (LONG(*p)) {
423 case nm_plcolour:
424 G_ChangedPlayerColour(LONG(*(p+1)), LONG(*(p+3)));
425 break;
426 case nm_savegamename:
427 if (len < SAVEDESCLEN) {
428 memcpy(savedescription, p+3, len);
429 // Force terminating 0 in case
430 savedescription[len] = 0;
431 }
432 break;
433 }
434 }
435 break;
436 default: // Should not be queued
437 break;
438 }
439
440 { // Requeue remaining packets
441 int newnum = 0;
442 packet_header_t **newqueue = NULL;
443
444 for (i=0; (unsigned)i<numqueuedpackets; i++)
445 if (doom_ntohl(queuedpacket[i]->tic) > gametic) {
446 newqueue = Z_Realloc(newqueue, ++newnum * sizeof *newqueue,
447 PU_STATIC, NULL);
448 newqueue[newnum-1] = queuedpacket[i];
449 } else Z_Free(queuedpacket[i]);
450
451 Z_Free(queuedpacket);
452 numqueuedpackets = newnum; queuedpacket = newqueue;
453 }
454}
455#endif // HAVE_NET
456
457void TryRunTics (void)
458{
459 int runtics;
460 int entertime = I_GetTime();
461
462 // Wait for tics to run
463 while (1) {
464#ifdef HAVE_NET
465 NetUpdate();
466#else
467 D_BuildNewTiccmds();
468#endif
469 runtics = (server ? remotetic : maketic) - gametic;
470 if (!runtics) {
471 if (!movement_smooth) {
472#ifdef HAVE_NET
473 if (server)
474 I_WaitForPacket(ms_to_next_tick);
475 else
476#endif
477 I_uSleep(ms_to_next_tick*1000);
478 }
479 if (I_GetTime() - entertime > 10) {
480#ifdef HAVE_NET
481 if (server) {
482 char buf[sizeof(packet_header_t)+1];
483 remotesend--;
484 packet_set((packet_header_t *)buf, PKT_RETRANS, remotetic);
485 buf[sizeof(buf)-1] = consoleplayer;
486 I_SendPacket((packet_header_t *)buf, sizeof buf);
487 }
488#endif
489 M_Ticker(); return;
490 }
491 //if ((displaytime) < (tic_vars.next-SDL_GetTicks()))
492 {
493 WasRenderedInTryRunTics = true;
494 if (V_GetMode() == VID_MODEGL ?
495 movement_smooth :
496 movement_smooth && gamestate==wipegamestate)
497 {
498 isExtraDDisplay = true;
499 D_Display();
500 isExtraDDisplay = false;
501 }
502 }
503 } else break;
504 }
505
506 while (runtics--) {
507#ifdef HAVE_NET
508 if (server) CheckQueuedPackets();
509#endif
510 if (advancedemo)
511 D_DoAdvanceDemo ();
512 M_Ticker ();
513 I_GetTime_SaveMS();
514 G_Ticker ();
515 P_Checksum(gametic);
516 gametic++;
517#ifdef HAVE_NET
518 NetUpdate(); // Keep sending our tics to avoid stalling remote nodes
519#endif
520 }
521}
522
523#ifdef HAVE_NET
524static void D_QuitNetGame (void)
525{
526 byte buf[1 + sizeof(packet_header_t)];
527 packet_header_t *packet = (void*)buf;
528 int i;
529
530 if (!server) return;
531 buf[sizeof(packet_header_t)] = consoleplayer;
532 packet_set(packet, PKT_QUIT, gametic);
533
534 for (i=0; i<4; i++) {
535 I_SendPacket(packet, 1 + sizeof(packet_header_t));
536 I_uSleep(10000);
537 }
538}
539#endif