void D_CheckNetGame(void) { packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL); if (server) { lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n"); do { while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) { packet_set(packet, PKT_GO, 0); *(uint8_t*)(packet+1) = consoleplayer; I_SendPacket(packet, sizeof(packet_header_t)+1); I_uSleep(100000); } } while (packet->type != PKT_GO); } Z_Free(packet); }
void D_CheckNetGame(void) { packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL); if (server) { lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n"); #ifdef USE_ANDROID jni_info_msg("Waiting for server to signal game start!", TT_LONG_DELAY | TT_COLOR_RED); #endif do { while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) { packet_set(packet, PKT_GO, 0); *(byte*)(packet+1) = consoleplayer; I_SendPacket(packet, sizeof(packet_header_t)+1); I_uSleep(100000); } } while (packet->type != PKT_GO); } Z_Free(packet); }
void D_InitNetGame (void) { int i; int numplayers = 1; i = M_CheckParm("-net"); if (i && i < myargc-1) i++; if (!(netgame = server = !!i)) { playeringame[consoleplayer = 0] = TRUE; // e6y // for play, recording or playback using "single-player coop" mode. // Equivalent to using prboom_server with -N 1 netgame = M_CheckParm("-solo-net") || M_CheckParm("-net1"); } else { // Get game info from server packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL); struct setup_packet_s *sinfo = (void*)(packet+1); struct { packet_header_t head; short pn; } PACKEDATTR initpacket; I_InitNetwork(); udp_socket = I_Socket(0); I_ConnectToServer(myargv[i]); do { do { // Send init packet initpacket.pn = doom_htons(wanted_player_number); packet_set(&initpacket.head, PKT_INIT, 0); I_SendPacket(&initpacket.head, sizeof(initpacket)); I_WaitForPacket(5000); } while (!I_GetPacket(packet, 1000)); if (packet->type == PKT_DOWN) I_Error("Server aborted the game"); } while (packet->type != PKT_SETUP); // Get info from the setup packet consoleplayer = sinfo->yourplayer; compatibility_level = sinfo->complevel; G_Compatibility(); startskill = sinfo->skill; deathmatch = sinfo->deathmatch; startmap = sinfo->level; startepisode = sinfo->episode; ticdup = sinfo->ticdup; xtratics = sinfo->extratic; G_ReadOptions(sinfo->game_options); lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n", consoleplayer+1, numplayers = sinfo->players, sinfo->numwads); { char *p = sinfo->wadnames; int i = sinfo->numwads; while (i--) { D_AddFile(p, source_net); p += strlen(p) + 1; } } Z_Free(packet); } localcmds = netcmds[displayplayer = consoleplayer]; for (i=0; i<numplayers; i++) playeringame[i] = TRUE; for (; i<MAXPLAYERS; i++) playeringame[i] = FALSE; if (!playeringame[consoleplayer]) I_Error("D_InitNetGame: consoleplayer not in game"); }
void NetUpdate(void) { static int lastmadetic; if (isExtraDDisplay) return; if (server) { // Receive network packets size_t recvlen; packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL); while ((recvlen = I_GetPacket(packet, 10000))) { switch(packet->type) { case PKT_TICS: { uint8_t *p = (void*)(packet+1); int tics = *p++; unsigned long ptic = doom_ntohl(packet->tic); if (ptic > (unsigned)remotetic) { // Missed some packet_set(packet, PKT_RETRANS, remotetic); *(uint8_t*)(packet+1) = consoleplayer; I_SendPacket(packet, sizeof(*packet)+1); } else { if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things remotetic = ptic; while (tics--) { int players = *p++; while (players--) { int n = *p++; RawToTic(&netcmds[n][remotetic%BACKUPTICS], p); p += sizeof(ticcmd_t); } remotetic++; } } } break; case PKT_RETRANS: // Resend request remotesend = doom_ntohl(packet->tic); break; case PKT_DOWN: // Server downed { int j; for (j=0; j<MAXPLAYERS; j++) if (j != consoleplayer) playeringame[j] = FALSE; server = FALSE; doom_printf("Server is down\nAll other players are no longer in the game\n"); } break; case PKT_EXTRA: // Misc stuff case PKT_QUIT: // Player quit // Queue packet to be processed when its tic time is reached queuedpacket = Z_Realloc(queuedpacket, ++numqueuedpackets * sizeof *queuedpacket, PU_STATIC, NULL); queuedpacket[numqueuedpackets-1] = Z_Malloc(recvlen, PU_STATIC, NULL); memcpy(queuedpacket[numqueuedpackets-1], packet, recvlen); break; case PKT_BACKOFF: /* cph 2003-09-18 - * The server sends this when we have got ahead of the other clients. We should * stall the input side on this client, to allow other clients to catch up. */ lastmadetic++; break; default: // Other packet, unrecognised or redundant break; } } Z_Free(packet); } { // Build new ticcmds int newtics = I_GetTime() - lastmadetic; newtics = (newtics > 0 ? newtics : 0); lastmadetic += newtics; if (ffmap) newtics++; while (newtics--) { I_StartTic(); if (maketic - gametic > BACKUPTICS/2) break; G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); maketic++; } if (server && maketic > remotesend) { // Send the tics to the server int sendtics; remotesend -= xtratics; if (remotesend < 0) remotesend = 0; sendtics = maketic - remotesend; { size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t); packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL); packet_set(packet, PKT_TICC, maketic - sendtics); *(uint8_t*)(packet+1) = sendtics; *(((uint8_t*)(packet+1))+1) = consoleplayer; { void *tic = ((uint8_t*)(packet+1)) +2; while (sendtics--) { TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]); tic = (uint8_t *)tic + sizeof(ticcmd_t); } } I_SendPacket(packet, pkt_size); Z_Free(packet); } } } }
boolean D_NetGetWad(const char* name) { #if defined(HAVE_WAIT_H) size_t psize = sizeof(packet_header_t) + strlen(name) + 500; packet_header_t *packet; boolean done = FALSE; if (!server || strchr(name, '/')) return FALSE; // If it contains path info, reject do { // Send WAD request to remote packet = Z_Malloc(psize, PU_STATIC, NULL); packet_set(packet, PKT_WAD, 0); *(uint8_t*)(packet+1) = consoleplayer; strcpy(1+(uint8_t*)(packet+1), name); I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2); I_uSleep(10000); } while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD)); Z_Free(packet); if (!strcasecmp((void*)(packet+1), name)) { pid_t pid; int rv; uint8_t *p = (uint8_t*)(packet+1) + strlen(name) + 1; /* Automatic wad file retrieval using wget (supports http and ftp, using URLs) * Unix systems have all these commands handy, this kind of thing is easy * Any windo$e port will have some awkward work replacing these. */ /* cph - caution here. This is data from an untrusted source. * Don't pass it via a shell. */ if ((pid = fork()) == -1) perror("fork"); else if (!pid) { /* Child chains to wget, does the download */ execlp("wget", "wget", p, NULL); } /* This is the parent, i.e. main LxDoom process */ wait(&rv); if (!(done = !access(name, R_OK))) { if (!strcmp(p+strlen(p)-4, ".zip")) { p = strrchr(p, '/')+1; if ((pid = fork()) == -1) perror("fork"); else if (!pid) { /* Child executes decompressor */ execlp("unzip", "unzip", p, name, NULL); } /* Parent waits for the file */ wait(&rv); done = !!access(name, R_OK); } /* Add more decompression protocols here as desired */ } Z_Free(buffer); } return done; #else /* HAVE_WAIT_H */ return FALSE; #endif }
void D_InitNetGame (void) { int i; int numplayers = 1; i = M_CheckParm("-net"); if (i && i < myargc-1) i++; server = netgame; if (!netgame) { playeringame[consoleplayer = 0] = true; } else { StartWifi(); // Get game info from server packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL); struct setup_packet_s *sinfo = (void*)(packet+1); struct { packet_header_t head; short pn; } PACKEDATTR initpacket; iprintf("I_InitNetwork()\n"); I_InitNetwork(); udp_socket = I_Socket(0); //I_ConnectToServer(myargv[i]); iprintf("I_ConnectToServer()\n"); if (I_ConnectToServer(server_address[0]) != 0) iprintf("FAILURE!\n"); iprintf("Connected?\n"); do { iprintf("Send Init Packet\n"); do { // Send init packet initpacket.pn = doom_htons(wanted_player_number); packet_set(&initpacket.head, PKT_INIT, 0); I_SendPacket(&initpacket.head, sizeof(initpacket)); iprintf("Wait for packet\n"); I_WaitForPacket(5000); iprintf("Done\n"); } while (!I_GetPacket(packet, 1000)); iprintf("Got it!\n"); if (packet->type == PKT_DOWN) I_Error("Server aborted the game"); } while (packet->type != PKT_SETUP); iprintf("Out of loop!\n"); // Once we have been accepted by the server, we should tell it when we leave atexit(D_QuitNetGame); // Get info from the setup packet consoleplayer = sinfo->yourplayer; compatibility_level = sinfo->complevel; G_Compatibility(); startskill = sinfo->skill; deathmatch = sinfo->deathmatch; startmap = sinfo->level; startepisode = sinfo->episode; ticdup = sinfo->ticdup; xtratics = sinfo->extratic; G_ReadOptions(sinfo->game_options); lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n", consoleplayer+1, numplayers = sinfo->players, sinfo->numwads); { char *p = sinfo->wadnames; int i = sinfo->numwads; while (i--) { D_AddFile(p, source_net); p += strlen(p) + 1; } } Z_Free(packet); } localcmds = netcmds[displayplayer = consoleplayer]; for (i=0; i<numplayers; i++) playeringame[i] = true; for (; i<MAXPLAYERS; i++) playeringame[i] = false; if (!playeringame[consoleplayer]) I_Error("D_InitNetGame: consoleplayer not in game"); }
int main(int argc, char** argv) { int localport = 5030, numplayers = 2, xtratics = 0, ticdup = 1; int exectics = 0; // gametics completed struct setup_packet_s setupinfo = { 2, 0, 1, 1, 1, 0, 3}; char**wadname = NULL; char**wadget = NULL; int numwads = 0; { int opt; byte *gameopt = setupinfo.game_options; memcpy(gameopt, &def_game_options, sizeof (setupinfo.game_options)); while ((opt = getopt(argc, argv, "p:e:l:adrfns:c:N:x:t:vw:")) != EOF) switch (opt) { case 't': if (optarg) ticdup = atoi(optarg); break; case 'x': if (optarg) xtratics = atoi(optarg); break; case 'p': if (optarg) localport = atoi(optarg); break; case 'e': if (optarg) setupinfo.episode = atoi(optarg); break; case 'l': if (optarg) setupinfo.level = atoi(optarg); break; case 'a': setupinfo.deathmatch = 2; break; case 'd': setupinfo.deathmatch = 1; break; case 'r': setupinfo.game_options[6] = 1; break; case 'f': setupinfo.game_options[7] = 1; break; case 'n': setupinfo.game_options[8] = 1; break; case 's': if (optarg) setupinfo.skill = atoi(optarg); break; case 'N': if (optarg) setupinfo.players = numplayers = atoi(optarg); break; case 'v': verbose++; break; case 'w': if (optarg) { char *p; wadname = realloc(wadname, ++numwads * sizeof *wadname); wadget = realloc(wadget , numwads * sizeof *wadget ); wadname[numwads-1] = strdup(optarg); if ((p = strchr(wadname[numwads-1], ','))) { *p++ = 0; wadget[numwads-1] = p; } else wadget[numwads-1] = NULL; } break; } } setupinfo.ticdup = ticdup; setupinfo.extratic = xtratics; *(int*)(&setupinfo.game_options[10]) = time(NULL); // random number seed I_InitSockets(localport); printf("Listening on port %d, waiting for %d players\n", localport, numplayers); { // no players initially int i; for (i=0; i<MAXPLAYERS; i++) playerjoingame[i] = INT_MAX; playerleftgame[i] = 0; // Print wads for (i=0; i<numwads; i++) printf("Wad %s (%s)\n", wadname[i], wadget[i]); } // Exit and signal handling atexit(doexit); // heh signal(SIGTERM, sig_handler); signal(SIGINT , sig_handler); signal(SIGQUIT, sig_handler); signal(SIGKILL, sig_handler); signal(SIGHUP , sig_handler); { int remoteticfrom[MAXPLAYERS] = { 0, 0, 0, 0 }; int remoteticto[MAXPLAYERS] = { 0, 0, 0, 0 }; int curplayers = 0; boolean ingame = false; ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; while (1) { { packet_header_t *packet = malloc(10000); size_t len; usleep(10000); while ((len = I_GetPacket(packet, 10000))) { if (verbose>2) printf("Received packet:"); switch (packet->type) { case PKT_INIT: printf("INIT\n"); if (!ingame) { { int n; struct setup_packet_s *sinfo = (void*)(packet+1); const char *rname = (void*)((short*)(packet+1)+1); // Add player to the game for (n=0; n<MAXPLAYERS; n++) if (playerjoingame[n] == INT_MAX) break; if (n == MAXPLAYERS) break; // Full game playerjoingame[n] = 0; remoteaddr[n] = sentfrom; remoteaddr[n].sin_port = *(short*)(packet+1); if (!memchr(rname,0,1000)) rname = "Invalid"; printf("%s(%s:%u) joined\n", rname, inet_ntoa(remoteaddr[n].sin_addr), ntohs(remoteaddr[n].sin_port)); { int i; size_t extrabytes = 0; // Send setup packet packet->type = PKT_SETUP; packet->tic = 0; memcpy(sinfo, &setupinfo, sizeof setupinfo); sinfo->yourplayer = n; sinfo->numwads = numwads; for (i=0; i<numwads; i++) { strcpy(sinfo->wadnames + extrabytes, wadname[i]); extrabytes += strlen(wadname[i]) + 1; } I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, remoteaddr+n); usleep(10000); I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, remoteaddr+n); } } } break; case PKT_GO: if (!ingame) { int from = *(byte*)(packet+1); if (playerleftgame[from] == INT_MAX) break; playerleftgame[from] = INT_MAX; if (++curplayers == numplayers) { ingame=true; printf("All players joined, beginning game.\n"); packet->type = PKT_GO; packet->tic = 0; BroadcastPacket(packet, sizeof *packet); usleep(10000); BroadcastPacket(packet, sizeof *packet); usleep(100000); } } break; case PKT_TICC: { byte tics = *(byte*)(packet+1); int from = *(((byte*)(packet+1))+1); if (verbose>2) printf("tics %d - %d from %d\n", packet->tic, packet->tic + tics - 1, from); if (packet->tic > remoteticfrom[from]) { // Missed tics, so request a resend packet->tic = remoteticfrom[from]; packet->type = PKT_RETRANS; I_SendPacketTo(packet, sizeof *packet, remoteaddr+from); } else { ticcmd_t *newtic = (void*)(((byte*)(packet+1))+2); if (packet->tic + tics < remoteticfrom[from]) break; // Won't help remoteticfrom[from] = packet->tic; while (tics--) netcmds[from][remoteticfrom[from]++%BACKUPTICS] = *newtic++; } } break; case PKT_RETRANS: { int from = *(byte*)(packet+1); if (verbose>2) printf("%d requests resend from %d\n", from, packet->tic); remoteticto[from] = packet->tic; } break; case PKT_QUIT: { int from = *(byte*)(packet+1); if (verbose>2) printf("%d quits at %d\n", from, packet->tic); if (playerleftgame[from] == INT_MAX) { // In the game playerleftgame[from] = packet->tic; if (ingame && !--curplayers) exit(0); // All players have exited } } // Fall through and broadcast it case PKT_EXTRA: BroadcastPacket(packet, len); if (packet->type == PKT_EXTRA) { if (verbose>2) printf("misc from %d\n", *(((byte*)(packet+1))+1)); } break; case PKT_WAD: { int i; int from = *(byte*)(packet+1); char *name = 1 + (char*)(packet+1); size_t size = sizeof(packet_header_t); packet_header_t *reply; if (verbose) printf("Request for %s ", name); for (i=0; i<numwads; i++) if (!strcasecmp(name, wadname[i])) break; if ((i==numwads) || !wadget[i]) { if (verbose) printf("n/a\n"); *(char*)(packet+1) = 0; I_SendPacketTo(packet, size+1, remoteaddr + from); } else { size += strlen(wadname[i]) + strlen(wadget[i]) + 2; reply = malloc(size); reply->type = PKT_WAD; reply->tic = 0; strcpy((char*)(reply+1), wadname[i]); strcpy((char*)(reply+1) + strlen(wadname[i]) + 1, wadget[i]); printf("sending %s\n", wadget[i]); I_SendPacketTo(reply, size, remoteaddr + from); free(reply); } } break; default: printf("Unrecognised packet type %d\n", packet->type); break; } } free(packet); } if (ingame) { // Run some tics int lowtic = INT_MAX; int i; for (i=0; i<MAXPLAYERS; i++) if (playeringame(i)) if (remoteticfrom[i]<lowtic) lowtic = remoteticfrom[i]; if (verbose>1) printf("%d new tics can be run\n", lowtic - exectics); if (lowtic > exectics) exectics = lowtic; // count exec'ed tics // Now send all tics up to lowtic for (i=0; i<MAXPLAYERS; i++) if (playeringame(i)) { int tics; if (lowtic <= remoteticto[i]) continue; remoteticto[i] -= xtratics; tics = lowtic - remoteticto[i]; { packet_header_t *packet = malloc(sizeof(packet_header_t) + 1 + tics * (1 + numplayers * (1 + sizeof(ticcmd_t)))); byte *p = (void*)(packet+1); packet->type = PKT_TICS; packet->tic = remoteticto[i] - xtratics; *p++ = tics; if (verbose>1) printf("sending %d tics to %d\n", tics, i); while (tics--) { int j, playersthistic = 0; byte *q = p++; for (j=0; j<MAXPLAYERS; j++) if ((playerjoingame[j] < remoteticto[i]) && (playerleftgame[j] > remoteticto[i])) { *p++ = j; memcpy(p, &netcmds[j][remoteticto[i]%BACKUPTICS], sizeof(ticcmd_t)); p += sizeof(ticcmd_t); playersthistic++; } *q = playersthistic; remoteticto[i]++; } I_SendPacketTo(packet, p - ((byte*)packet), remoteaddr+i); free(packet); } } } { // Statistics reporting static int counter = 0; static int lastrecvdbytes, lastsentbytes; int fh; if (exectics && !(counter++%100)) { char buf[1000]; sprintf(buf, "lxdoom-game-server-stats.%u", getpid()); fh = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0666); sprintf(buf, "Players %d, tic %d\n\tSent\tReceived\n" "Now\t%d\t%d\nPer tic\t%d\t%d\n", curplayers, exectics, sentbytes - lastsentbytes, recvdbytes - lastrecvdbytes, sentbytes/exectics, recvdbytes/exectics); write(fh, buf, strlen(buf)); close(fh); lastrecvdbytes = recvdbytes; lastsentbytes = sentbytes; } } } } }