You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bomberclone/src/network.c

807 lines
23 KiB

/* $Id: network.c,v 1.28 2003/06/01 20:17:03 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);
if (bman.firewall)
i = NETF_firewall;
else
i = 0;
send_playerid (&bman.players[0].net.addr, bman.playername, NULL, NULL, -1, -1, i);
}
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, bman.players[pl_nr].net.flags);
}
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, bman.players[pl_nr].net.flags);
}
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_netplayer (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_netplayer (bman.players[p].state) && p != bman.p_nr && NET_CANSEND(p))
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_netplayer (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) && NET_CANSEND(p))
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 && bman.players[p].bombs[b].state != BS_trigger)
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_netplayer (bman.players[pl].state) && pl != bman.p_nr && NET_CANSEND(pl))
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_netplayer (bman.players[pl].state) && pl != bman.p_nr && NET_CANSEND(pl))
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);
bman.updatestatusbar = 1; // force an update
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_netplayer (bman.players[i].state)) {
send_quit (&bman.players[i].net.addr, host, port);
j++;
}
if (bman.notifygamemaster)
gamesrv_sendmode (bman.maxplayer, j);
bman.updatestatusbar=1;
}
}
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_netplayer (bman.players[i].state) && i != bman.p_nr && NET_CANSEND(i))
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_netplayer (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, bman.players[i].net.flags);
};
void
net_send_chat (char *text, signed char notigamesrv)
{
int i;
for (i = 0; i < MAX_PLAYERS; i++)
if (PS_IS_netplayer (bman.players[i].state) && i != bman.p_nr && NET_CANSEND(i))
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_netplayer (bman.players[i].state) && i != bman.p_nr && NET_CANSEND(i))
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;
bman.players[p].special.type = SP_nothing;
bman.updatestatusbar=1;
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;
};
/* send special use elements into the network,
to make sure nothing bad happens with explosions
we send the ex_nr number too */
void
net_game_send_special (int pl_nr, int ex_nr)
{
int pl;
d_printf ("Send Special Data %d\n", pl_nr);
if (pl_nr < 0 || pl_nr >= MAX_PLAYERS)
return;
for (pl = 0; pl < MAX_PLAYERS; pl++)
if (PS_IS_netplayer (bman.players[pl].state) && pl != pl_nr && NET_CANSEND(pl))
send_special (&bman.players[pl].net.addr, pl_nr, bman.players[pl_nr].special.type, ex_nr);
};