d_client.c 15 KB

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