/* $Id: network.c,v 1.14 2003/05/06 17:47:13 stpohle Exp $ */ /* network routines. */ #include "bomberclone.h" #include "network.h" #include "gamesrv.h" #include "packets.h" #include "gfx.h" int network_server_port (char *server, char *host, int hostlen, char *port, int portlen) { char *pos, *pos2; if (host == NULL) return -1; pos2 = pos = strchr (server, ':'); if (pos != NULL) while (pos2 != NULL) { pos = pos2; pos2 = strchr (pos + 1, ':'); } if (pos != NULL) { // : für Portangabe gefunden if (pos - server < hostlen) { strncpy (host, server, pos - server); host[pos - server] = 0; if (pos[1] == 0) sprintf (port, "11000"); else strcpy (port, pos + 1); } else { return -1; } } else { // Portangabe wurde nicht gefunden und wird auf 0 gesetzt strncpy (host, server, hostlen); sprintf (port, "11000"); } return 0; }; /* try to work better with the network packet option */ void net_dyn_pkgoption () { int p; _net_pkgopt *npkg; for (p = 0; p < MAX_PLAYERS; p++) if (PS_IS_netplayer (bman.players[p].state)) { npkg = &bman.players[p].net.pkgopt; if (npkg->to_2sec > DYN_PKG_MAX_MISSING) { if (npkg->send_set < 10) npkg->send_set++; npkg->to_2sec = 0; npkg->to_timestamp = timestamp; } if ((timestamp - npkg->to_timestamp > 2000) && npkg->to_2sec <= DYN_PKG_MIN_MISSING) { if (npkg->send_set > PKG_SENDSETOPT) npkg->send_set--; npkg->to_2sec = 0; npkg->to_timestamp = timestamp; } } }; /* setup everything for the network loop */ int network_init () { int i; /* we need it for the windows winsock */ #ifdef _WIN32 WSADATA wsaData; if (WSAStartup (MAKEWORD (1, 1), &wsaData) != 0) { d_printf ("WSAStartup failed.\n"); exit (1); } #endif bman.sock = -1; bman.p_nr = -1; timestamp = SDL_GetTicks (); // reset playernumber and number of connected players for (i = 0; i < MAX_PLAYERS; i++) { bman.players[i].net.addr.host[0] = 0; bman.players[i].net.addr.port[0] = 0; bman.players[i].name[0] = 0; bman.players[i].gfx_nr = -1; bman.players[i].net.timestamp = timestamp; bman.players[i].points = 0; bman.players[i].wins = 0; bman.players[i].net.pingreq = 20; bman.players[i].net.pingack = 22; bman.players[i].state = 0; } bman.lastwinner = -1; bman.players_nr_s = 1; // start the udp server bman.sock = udp_server (bman.port, bman.net_ai_family); if (bman.sock < 0) { #ifdef _WIN32 WSACleanup (); #endif return -1; } // we have got out socket.. so now allocate the memory for the resend_cache resend_cache.data = (char *) malloc (PKG_RESENDCACHE_SIZE); resend_cache.fill = 0; // if we are the server set up my data if (GT_MP_PTPM) { strncpy (bman.players[0].name, bman.playername, LEN_PLAYERNAME); bman.p_nr = 0; bman.players[0].state = PSF_used; if (bman.notifygamemaster) gamesrv_sendmode (bman.maxplayer, 1); } // if client send Data to the server if (GT_MP_PTPS) { network_server_port (bman.servername, bman.players[0].net.addr.host, LEN_SERVERNAME, bman.players[0].net.addr.port, LEN_PORT); d_printf ("Connect To: %s[:%s]\n", bman.players[0].net.addr.host, bman.players[0].net.addr.port); dns_filladdr (bman.players[0].net.addr.host, LEN_SERVERNAME, bman.players[0].net.addr.port, LEN_PORT, bman.net_ai_family, &bman.players[0].net.addr.sAddr); bman.players[0].net.addr.port[0] = bman.players[0].net.addr.host[0] = 0; dns_filladdr (bman.players[0].net.addr.host, LEN_SERVERNAME, bman.players[0].net.addr.port, LEN_PORT, bman.net_ai_family, &bman.players[0].net.addr.sAddr); send_playerid (&bman.players[0].net.addr, bman.playername, NULL, NULL, -1, -1); } return 0; }; /* shutdown the network part */ void network_shutdown () { int i; if (GT_MP_PTPM) { for (i = 1; i < MAX_PLAYERS; i++) if (bman.players[i].net.addr.host[0] != 0) send_quit (&bman.players[i].net.addr, NULL, NULL); if (bman.notifygamemaster) gamesrv_quit (); } else if (bman.players[0].net.addr.host[0] != 0) send_quit (&bman.players[0].net.addr, NULL, NULL); udp_close (bman.sock); if (resend_cache.data != NULL) free (resend_cache.data); resend_cache.data = NULL; resend_cache.fill = -1; bman.p_nr = -1; bman.sock = -1; #ifdef _WIN32 WSACleanup (); #endif }; int net_check_timeout (int pl_nr) { int timeout = UDP_TIMEOUT; if ((bman.players[pl_nr].state & (PSF_net + PSF_used)) == (PSF_used + PSF_net) && timestamp - bman.players[pl_nr].net.timestamp > timeout && bman.players[pl_nr].net.pingreq != bman.players[pl_nr].net.pingack) { d_printf ("net_check_timeout pl_nr=%d, ack=%d, req=%d, timediff=%d\n", pl_nr, bman.players[pl_nr].net.pingack, bman.players[pl_nr].net.pingreq, timestamp - bman.players[pl_nr].net.timestamp); bman.players[pl_nr].net.timestamp = timestamp; bman.players[pl_nr].net.pingack = bman.players[pl_nr].net.pingreq; send_ping (&bman.players[pl_nr].net.addr, bman.players[pl_nr].net.pingack + 100, PKG_pingreq); } if ((bman.players[pl_nr].state & (PSF_net + PSF_used)) == (PSF_used + PSF_net) && timestamp - bman.players[pl_nr].net.timestamp > timeout && bman.players[pl_nr].net.pingreq == bman.players[pl_nr].net.pingack) { d_printf ("net_check_timeout pl_nr=%d, ack=%d, req=%d, timediff=%d\n", pl_nr, bman.players[pl_nr].net.pingack, bman.players[pl_nr].net.pingreq, timestamp - bman.players[pl_nr].net.timestamp); return 1; } return 0; }; /* Read data from the network and work with it */ int network_loop () { char data[MAX_UDPDATA]; struct pkg *packet = (struct pkg *) data; int inlen, i; _net_addr addr; if (bman.state != GS_running && bman.state != GS_ready) timestamp = SDL_GetTicks (); /* as long as we get any new data, work with them */ inlen = udp_get (bman.sock, data, MAX_UDPDATA, &addr.sAddr, bman.net_ai_family); addr.port[0] = addr.host[0] = 0; if (inlen > 0) dns_filladdr (addr.host, LEN_SERVERNAME, addr.port, LEN_PORT, bman.net_ai_family, &addr.sAddr); while (inlen > 0) { do_pkg (packet, &addr); // printf ("Network : inlen (%d) typ (%d) Size (%d)\n", inlen, packet->typ, pkglen); inlen = udp_get (bman.sock, data, MAX_UDPDATA, &addr.sAddr, bman.net_ai_family); addr.port[0] = addr.host[0] = 0; if (inlen > 0) dns_filladdr (addr.host, LEN_SERVERNAME, addr.port, LEN_PORT, bman.net_ai_family, &addr.sAddr); } /* check here for old connections who aren't answering */ if (bman.state == GS_wait || bman.state == GS_ready || bman.state == GS_running) { if (GT_MP_PTPS) { if (net_check_timeout (0)) { d_printf ("Server Timed Out\n"); bman.state = GS_startup; } } else if (GT_MP_PTPM) { for (i = 1; i < MAX_PLAYERS; i++) if (net_check_timeout (i)) { d_printf ("Player %d Timed Out\n", i); net_delplayer (i); } } } /* resend_cache.... */ rscache_loop (); /* dynamic calibration of the network traffic option */ net_dyn_pkgoption (); return 0; }; /* this is needed to draw the whole uppdate of everything */ void draw_netupdatestate () { char text[255]; int y = 0, i; SDL_Rect src, dest; for (i = 0; i < MAX_PLAYERS; i++) if (PS_IS_used (bman.players[i].state)) { y += 48; redraw_logo (0, y, gfx.res.x, y + 48); if (bman.players[i].gfx_nr != -1) { dest.w = src.w = bman.players[i].gfx->smal_size.x; dest.h = src.h = bman.players[i].gfx->smal_size.y; src.x = bman.players[i].gfx->smal_size.x * down; src.y = 0; dest.x = 50; dest.y = y; SDL_BlitSurface (bman.players[i].gfx->smal_image, &src, gfx.screen, &dest); gfx_AddUpdateRect (dest.x, dest.y, gfx.res.x - dest.x, dest.h); } if (bman.players[i].net.net_istep == 0) sprintf (text, "%s - State : READY", bman.players[i].name); else sprintf (text, "%s - State : DOWNLOAD", bman.players[i].name); draw_text (70, y, text, 1); text[0] = 0; if (bman.players[i].net.net_istep == 2) sprintf (text, "Getting Field Data %d of %d.", bman.players[i].net.net_status, bman.fieldsize.x); if (bman.players[i].net.net_istep == 1) sprintf (text, "Getting Player Data %d of %d.", bman.players[i].net.net_status, MAX_PLAYERS); draw_text (70, y + 16, text, 1); } return; } /* used to update settings at startup */ void net_change_playerid (int pl_nr, unsigned char senddata) { int i; if (GT_MP_PTPM) { /* Send to all connected clients the update */ for (i = 1; i < MAX_PLAYERS; i++) if (bman.players[i].net.addr.host[0] != 0) send_playerid (&bman.players[i].net.addr, bman.players[pl_nr].name, bman.players[pl_nr].net.addr.host, bman.players[pl_nr].net.addr.port, pl_nr, bman.players[pl_nr].gfx_nr); } else { /* Send only to the Server the update and only if in_nr == bman.in_nr */ if (pl_nr == bman.p_nr && senddata) send_playerid (&bman.players[0].net.addr, bman.players[pl_nr].name, bman.players[pl_nr].net.addr.host, bman.players[pl_nr].net.addr.port, pl_nr, bman.players[pl_nr].gfx_nr); } player_set_gfx (&bman.players[pl_nr], bman.players[pl_nr].gfx_nr); }; /* sets up everything for the network game.. on servers the game field will be created and the clients will wait for the game data within the network loop */ void net_new_gamedata () { int done = 0, keypressed = 0, x, y, p, i, net_istep; // network init step SDL_Event event; Uint8 *keys; Uint32 downtimestamp = 0; draw_logo (); if (GT_MP_PTPM) draw_text (100, 0, "Waiting for the Clients", 1); else draw_text (100, 0, "Downloading Data", 1); SDL_Flip (gfx.screen); /* prepare everything for the loop */ for (x = 0; x < MAX_PLAYERS; x++) { bman.players[x].net.timestamp = 0; bman.players[x].net.net_status = -1; bman.players[x].net.net_istep = 2; } y = -1; if (GT_MP_PTPM) net_istep = 0; else net_istep = 2; while (!done && bman.state == GS_update) { /* the network thing */ network_loop (); /* if PTPM check if all players are ready */ if (GT_MP_PTPM) { for (p = 1, i = 1; p < MAX_PLAYERS; p++) if (PS_IS_playing (bman.players[p].state) && bman.players[p].net.net_istep != 0) i = 0; if (i == 1) { /* all players are ready */ done = 1; bman.state = GS_ready; } } /* if PTPS get all data */ if (GT_MP_PTPS) { if (net_istep == 2) { if ((y < bman.fieldsize.y - 1 && y == bman.players[bman.p_nr].net.net_status) || y == -1) { /* send field data req */ y++; downtimestamp = timestamp; send_getfield (&bman.players[0].net.addr, y); } else if (y < bman.fieldsize.y && y != bman.players[bman.p_nr].net.net_status && y >= 0 && timestamp - downtimestamp > DOWNLOAD_TIMEOUT) { /* we have got no field data */ y--; } else if (y == bman.fieldsize.y - 1 && bman.players[bman.p_nr].net.net_status == y) { /* we have got all field data */ y = -1; bman.players[bman.p_nr].net.net_istep = --net_istep; bman.players[bman.p_nr].net.net_status = -1; } } if (net_istep == 1) { if ((y < MAX_PLAYERS - 1 && y == bman.players[bman.p_nr].net.net_status) || y == -1) { /* send player date req */ y++; downtimestamp = timestamp; send_getplayerdata (&bman.players[0].net.addr, y); } if (y < MAX_PLAYERS && y != bman.players[bman.p_nr].net.net_status && y >= 0 && timestamp - downtimestamp > DOWNLOAD_TIMEOUT) { /* we have got no player data */ y--; } if (y == MAX_PLAYERS - 1 && bman.players[bman.p_nr].net.net_status == y) { /* we have got all playerdata */ y = -1; bman.players[bman.p_nr].net.net_istep = --net_istep; bman.players[bman.p_nr].net.net_status = -1; downtimestamp = timestamp; send_playerstatus (&bman.players[0].net.addr, bman.p_nr, 0, 0); } } if (net_istep == 0 && bman.players[bman.p_nr].net.net_status == -1 && timestamp - downtimestamp > DOWNLOAD_TIMEOUT) { /* server did not send informations back */ downtimestamp = timestamp; send_playerstatus (&bman.players[0].net.addr, bman.p_nr, 0, 0); } } /* do the grafik work */ draw_netupdatestate (); SDL_Flip (gfx.screen); if (SDL_PollEvent (&event) != 0) switch (event.type) { case (SDL_QUIT): bman.state = GS_quit; bman.p_nr = -1; done = 1; } keys = SDL_GetKeyState (NULL); if (keys[SDLK_ESCAPE] && event.type == SDL_KEYDOWN) { done = 1; bman.p_nr = -1; keypressed = 1; bman.state = GS_startup; } if (event.type == SDL_KEYUP) keypressed = 0; timestamp = SDL_GetTicks (); // needed for time sync. SDL_Delay (1); // we don't need here anything better /* player is only watching so just go after we have got everything go to show the field */ if (GT_MP_PTPS && bman.state == GS_update && net_istep == 0 && bman.players[bman.p_nr].gfx_nr == -1) { done = 1; bman.state = GS_running; } } }; void net_game_send_player (int p_nr) { int p; if (GT_MP_PTPM) { for (p = 0; p < MAX_PLAYERS; p++) if (PS_IS_used (bman.players[p].state) && p != bman.p_nr) send_playerdata (&bman.players[p].net.addr, p_nr, &bman.players[p_nr]); } else if (p_nr == bman.p_nr) { for (p = 0; p < MAX_PLAYERS; p++) if (PS_IS_used (bman.players[p].state) && p != bman.p_nr) send_playerdata (&bman.players[p].net.addr, p_nr, &bman.players[p_nr]); } }; void net_game_send_playermove (int p_nr, int mustsend) { int p; _player *pl; for (p = 0; p < MAX_PLAYERS; p++) if (PS_IS_used (bman.players[p].state) && p != bman.p_nr) { pl = &bman.players[p_nr]; pl->net.pkgopt.send_to--; if (pl->net.pkgopt.send_to <= 0 || mustsend) send_playermove (&bman.players[p].net.addr, p_nr, pl); /* network packet send control */ if (pl->net.pkgopt.send_to <= 0 || pl->net.pkgopt.send_to > pl->net.pkgopt.send_set) pl->net.pkgopt.send_to = pl->net.pkgopt.send_set; } }; void net_game_send_bomb (int p, int b) { int pl; /* check if we are slave and send something else as dropping a bomb */ if (GT_MP_PTPS && bman.players[p].bombs[b].state != BS_ticking) return; d_printf ("Send BombData %d, %d\n", p, b); if (p < 0 || p >= MAX_PLAYERS || b < 0 || b >= MAX_BOMBS) return; for (pl = 0; pl < MAX_PLAYERS; pl++) if (PS_IS_used (bman.players[pl].state) && pl != bman.p_nr) send_bombdata (&bman.players[pl].net.addr, p, b, &bman.players[p].bombs[b]); }; void net_game_send_field (int x, int y) { int pl; d_printf ("Send FieldData %d, %d\n", x, y); if (x < 0 || x >= MAX_FIELDSIZE_X || y < 0 || y >= MAX_FIELDSIZE_Y) return; for (pl = 0; pl < MAX_PLAYERS; pl++) if (PS_IS_used (bman.players[pl].state) && pl != bman.p_nr) send_field (&bman.players[pl].net.addr, x, y, &bman.field[x][y]); }; void net_delplayer (int pl_nr) { char host[LEN_SERVERNAME]; char port[LEN_PORT]; int i, j = 1; d_printf ("net_delplayer (%d)\n", pl_nr); if (pl_nr == bman.p_nr) { /* we're not wanted */ network_shutdown (); bman.state = GS_startup; } else { strncpy (host, bman.players[pl_nr].net.addr.host, LEN_SERVERNAME); strncpy (port, bman.players[pl_nr].net.addr.port, LEN_PORT); bman.players[pl_nr].state &= (0xFF - (PSF_used + PSF_alife)); // Delete the used flag bman.players[pl_nr].net.net_istep = 0; // needed for disconnect during the update bman.players_nr_s--; bman.players[pl_nr].gfx_nr = -1; if (GT_MP_PTPM && (GS_WAITRUNNING || bman.state == GS_update)) { for (i = 1; i < MAX_PLAYERS; i++) if (PS_IS_used (bman.players[i].state)) { send_quit (&bman.players[i].net.addr, host, port); j++; } if (bman.notifygamemaster) gamesrv_sendmode (bman.maxplayer, j); } } if (GT_MP_PTPS && pl_nr == 0) /* masterserver quit */ bman.state = GS_startup; if (GT_MP_PTPM && bman.notifygamemaster) gamesrv_sendmode (bman.maxplayer, bman.players_nr_s); }; void net_game_fillsockaddr () { /* Update all sockaddr before the game starts */ int i; for (i = 0; i < MAX_PLAYERS; i++) if (bman.players[i].net.addr.host[0] != 0 && bman.players[i].net.addr.host[0] != 0) dns_filladdr (bman.players[i].net.addr.host, LEN_SERVERNAME, bman.players[i].net.addr.port, LEN_PORT, bman.net_ai_family, &bman.players[i].net.addr.sAddr); }; void net_send_servermode () { int i; for (i = 0; i < MAX_PLAYERS; i++) if (PS_IS_used (bman.players[i].state) && i != bman.p_nr) send_servermode (&bman.players[i].net.addr, i); if (GT_MP_PTPM && bman.notifygamemaster) /* send notification the the gamemaster */ gamesrv_sendmode (bman.maxplayer, bman.players_nr_s); }; /* sends to everyone an up to date playerlist*/ void net_send_players () { int i, j; for (j = 0; j < MAX_PLAYERS; j++) if (PS_IS_used (bman.players[j].state) && j != bman.p_nr) for (i = 0; i < MAX_PLAYERS; i++) send_playerid (&bman.players[j].net.addr, bman.players[i].name, bman.players[i].net.addr.host, bman.players[i].net.addr.port, i, bman.players[i].gfx_nr); }; void net_send_chat (char *text, signed char notigamesrv) { int i; for (i = 0; i < MAX_PLAYERS; i++) if (PS_IS_used (bman.players[i].state) && i != bman.p_nr) send_chat (&bman.players[i].net.addr, text); if (GT_MP_PTPM && bman.notifygamemaster && notigamesrv == 1) /* send notification the the gamemaster */ gamesrv_sendchat (text); }; void net_game_send_ill (int p_nr) { int i; d_printf ("net_game_send_ill (%d)\n", p_nr); for (i = 0; i < MAX_PLAYERS; i++) if (PS_IS_used (bman.players[i].state) && i != bman.p_nr) send_ill (&bman.players[i].net.addr, p_nr, &bman.players[p_nr]); }; /* this routine will set up some things for the network game after this the data should be transfered to the other clients. */ void net_new_game () { int p, i; // reset playerposition for (i = 0; i < MAX_PLAYERS; i++) bman.players[i].pos.y = bman.players[i].pos.x = -1; bman.players_nr = 0; bman.players_nr_s = 0; for (p = 0; p < MAX_PLAYERS; p++) { bman.players[p].frame = 0; bman.players[p].frameto = 0; if (PS_IS_used (bman.players[p].state)) { bman.players_nr_s++; if (bman.players[p].gfx_nr == -1) { bman.players[p].gfx = NULL; bman.players[p].state &= (0xff - (PSF_alife + PSF_playing)); } else { bman.players[p].state |= PSF_alife + PSF_playing; bman.players[p].gfx = &gfx.players[bman.players[p].gfx_nr]; } } else bman.players[p].state = 0; bman.players[p].bombs_n = START_BOMBS; bman.players[p].range = START_RANGE; bman.players[p].speed = START_SPEED; for (i = 0; i < PI_max; i++) /* all types of illnes turn them off */ bman.players[p].ill[i].to = 0; bman.players[p].frame = 0; bman.players[p].frameto = 0; bman.players[p].d = 0; // reset bombs for (i = 0; i < MAX_BOMBS; i++) { bman.players[p].bombs[i].state = BS_off; bman.players[p].bombs[i].ex_nr = -1; } } bman.players[bman.p_nr].state &= (0xFF - PSF_net); // we are the local player bman.last_ex_nr = 1; };