Hi James, As discussed I implemented and tested (server-side only) the addition of the metaserver listing for INL server feature. I followed your suggestion and used this as my .metaservers file: metaserver.us.netrek.org 3521 60 120 home.nl.netrek.org I 4566 4000 open metaserver.us.netrek.org 3521 60 120 away.nl.netrek.org I 4577 5000 open metaserver2.us.netrek.org 3521 60 120 home.nl.netrek.org I 4566 4000 open metaserver2.us.netrek.org 3521 60 120 away.nl.netrek.org I 4577 5000 open The server now gets properly listed on the metaserver: Server Host Port Ago Status Flags --------------------------------------- -------- ---- ----------------- ------- -h away.nl.netrek.org -p 4577 43 Nobody playing I -h home.nl.netrek.org -p 4566 0 OPEN: 1 player I Since I dislike having all INL servers show up on the metaserver list even when there are no players on it (which is 99.9% of the time), I made it so that only when there are actually players on it, the metaserver is informed. When the last player logs out it will send a final players=0 update and that's it. That means that after 60 minutes (this I tested) the entry will be automatically removed from the metaserver. So how does the server get listed in the first place then you may wonder? Well, I hope that the old clue players know how to use -h host -p port command line options. They will probably be the ones to call a clue game, so they will enter first too. And that triggers the metaserver listing. Client changes required! I see NetrekXP already shows entries of type "I" (=INL). Had not expected that. Anyway. the change required is stripping off "home." and "away." from the start of the hostname and only *displaying* that on screen. Should be trivial. I will do that later for NetrekXP. Attached you find 2 files: solicit.c and solicit.h. In the latter I had to increase MAXMETASERVERS to 4, so that my 4 entries would all work (2 metaservers, each has a home/away). There is a small 64bit bugfix in there too. Someone did a nice pointer copy and thought only of 32bit ;-) James: there's still a lot of extraneous fprintf() statements in. If you apply the patch, you can safely remove them.. Greetx, Erik James Cameron wrote: > Erik, > > I've had a look at the metaserver code just now, at scan.c and > disp_udp.c, and it would be quite simple to adjust that to account for > INL servers. I made the UDP protocol extensible. > > But you mentioned port 3521. Does the NetrekXP client use the UDP or > TCP connection? If TCP, then I don't think I'd like to change the > format. I'd really like Stas to use UDP if he can. ;-) > > To hack it now without changing any code, in the backwards compatible > way, the .metaservers file for the INL server should contain one line > for each port, using a DNS alias. > > For example; > > metaserver.us.netrek.org 3521 60 120 pickup.nl.netrek.org B 2592 2593 open > metaserver.us.netrek.org 3521 60 120 home.nl.netrek.org B 4566 4000 open > metaserver.us.netrek.org 3521 60 120 away.nl.netrek.org B 4577 5000 open > metaserver.us.netrek.org 3521 60 120 home-observers.nl.netrek.org B 4000 > ... > > We should add a server type for an INL server, and include INL servers > that have solicited in the response to the client. I'd want to make a > new server side protocol version to include all four ports. I'd expect > the client developers to accept four ports and figure a way to display > it! > > Erik wrote: > >> b) Currently only the player port is listed! > > > Yes, that's insufficient. > > We didn't design in a protocol version number for the client UDP query, > but there's room; only the first character is checked now and it must be > a question mark. We could add a version number after that, and have the > later version cause more ports to be listed. > > I'm happy to change COW. > > >>3) Problem is I have not yet found the point where to put the >> call to solicit(). How can the server check if it is an INL >> server? > > > (status->gameup & GU_INROBOT) > > The flag is set by the INL robot. > > >> Which process owns that metaservers[] table? > > > Daemon. > > >> Should the check be done in deamon (which has the solicit() call), >> or in netrekd when a player enters? > > > Daemon. It won't be running until a player enters. > > >>Any feedback is appreciated. I am really convinced this is a >>necessary feature to promote clue netrek to midbies who simply >>happen to not be a computer wizard ;-) > > > I agree. > -------------- next part -------------- #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/time.h> #include "defs.h" #include "struct.h" #include "data.h" #include "solicit.h" /* our copy of .metaservers file */ static struct metaserver metaservers[MAXMETASERVERS]; /* initialisation done flag */ static int initialised = 0; /* remove non-printable chars in a string. Lifted from tools/players.c */ /* with a minor addition: error checking on the strdup */ static char *name_fix(char *name) { char *new = strdup(name); /* xx, never freed */ register char *r = new; if(!new) return new; /* don't play with null ptr */ while(*name){ *r++ = (*name <= 32)?'_':*name; name++; } *r = 0; return new; } /* attach to a metaserver, i.e. prepare the socket */ static int udp_attach(struct metaserver *m) { char *our_server = m->ours; /* create the socket structure */ m->sock = socket(AF_INET, SOCK_DGRAM, 0); if (m->sock < 0) { perror("solicit: udp_attach: socket"); return 0; } /* bind the local socket */ /* bind to interface attatched to this.host.name */ /* first check if perhaps it is a fake INL server address */ if( ( m->type[0] == 'I' || m->type[0] == 'i' ) && ( strncasecmp( m->ours, "home.", 5 ) == 0 || strncasecmp( m->ours, "away.", 5 ) == 0 ) ) { our_server += 5; } /* numeric first */ if( (m->address.sin_addr.s_addr = inet_addr( our_server )) == -1 ) { struct hostent *hp; /* then name */ if ((hp = gethostbyname( our_server )) == NULL) { /* bad hostname or unable to get ip address */ fprintf(stderr, "Bad this.host.name field in .metaservers.\n"); return 0; } else { // m->address.sin_addr.s_addr = *(long *) hp->h_addr; NOT 64BIT SAFE memcpy( &(m->address.sin_addr.s_addr), hp->h_addr, 4); } } m->address.sin_family = AF_INET; m->address.sin_port = 0; if (bind(m->sock,(struct sockaddr *)&m->address, sizeof(m->address)) < 0) { perror("solicit: udp_attach: unable to bind to desired interface (this.host.name field)"); return 0; } /* build the destination address */ m->address.sin_family = AF_INET; m->address.sin_port = htons(m->port); /* attempt numeric translation first */ if ((m->address.sin_addr.s_addr = inet_addr(m->host)) == -1) { struct hostent *hp; /* then translation by name */ if ((hp = gethostbyname(m->host)) == NULL) { /* if it didn't work, return failure and warning */ fprintf(stderr, "solicit: udp_attach: host %s not known\n", m->host); return 0; } else { // m->address.sin_addr.s_addr = *(long *) hp->h_addr; NOT 64BIT SAFE memcpy( &(m->address.sin_addr.s_addr), hp->h_addr, 4); } } return 1; } /* transmit a packet to the metaserver */ static int udp_tx(struct metaserver *m, char *buffer, int length) { int i; /* send the packet */ fprintf(stderr, "solicit: udp_tx: sendto (size:%d)\n", length ); if (sendto(m->sock, buffer, length, 0, (struct sockaddr *)&m->address, sizeof(m->address)) < 0) { perror("solicit: udp_tx: sendto"); return 0; } return 1; } void solicit(int force) { int i, nplayers=0, nfree=0; char packet[MAXMETABYTES]; /* static char prior[MAXMETABYTES];*/ char *fixed_name, *fixed_login; /* name/login stripped of unprintables */ char *name, *login; /* name and login guaranteed not blank */ char unknown[] = "unknown"; /* unknown player/login string */ char *here = packet; time_t now = time(NULL); int gamefull = 0; /* is the game full? */ int isrsa = 0; /* is this server RSA? */ /* perform first time initialisation */ if (initialised == 0) { FILE *file; /* clear metaserver socket list */ for (i=0; i<MAXMETASERVERS; i++) metaservers[i].sock = -1; /* open the metaserver list file */ file = fopen(".metaservers", "r"); /* ??? LIBDIR prefix? */ if (file == NULL) { initialised++; return; } /* read the metaserver list file */ for (i=0; i<MAXMETASERVERS; i++) { struct metaserver *m = &metaservers[i]; char buffer[256]; /* where to hold the .metaservers line */ char *line; /* return from fgets() */ char *token; /* current line token */ /* read a line */ line = fgets(buffer, 256, file); /* if end of file reached, stop */ if (line == NULL) break; if (feof(file)) break; /* parse each field, ignore the line if insufficient fields found */ token = strtok(line, " "); /* meta host name */ if (token == NULL) continue; strncpy(m->host, token, 32); token = strtok(NULL, " "); /* meta port */ if (token == NULL) continue; m->port = atoi(token); token = strtok(NULL, " "); /* min solicit time */ if (token == NULL) continue; m->minimum = atoi(token); token = strtok(NULL, " "); /* max solicit time */ if (token == NULL) continue; m->maximum = atoi(token); token = strtok(NULL, " "); /* our host name */ if (token == NULL) continue; strncpy(m->ours, token, 32); token = strtok(NULL, " "); /* server type */ if (token == NULL) continue; strncpy(m->type, token, 2); token = strtok(NULL, " "); /* player port */ if (token == NULL) continue; m->pport = atoi(token); token = strtok(NULL, " "); /* observer port */ if (token == NULL) continue; m->oport = atoi(token); token = strtok(NULL, "\n"); /* comment text */ if (token == NULL) continue; strncpy(m->comment, token, 32); /* force minimum and maximum delays (see note on #define) */ if (m->minimum < META_MINIMUM_DELAY) m->minimum = META_MINIMUM_DELAY; if (m->maximum > META_MAXIMUM_DELAY) m->maximum = META_MAXIMUM_DELAY; /* attach to the metaserver (DNS lookup is only delay) */ udp_attach(m); /* place metaserver addresses in /etc/hosts to speed this */ /* use numeric metaserver address to speed this */ /* initialise the other parts of the structure */ m->sent = 0; strcpy(m->prior, ""); } initialised++; fclose(file); } printf("solicit call!\n\n" ); /* update each metaserver */ for (i=0; i<MAXMETASERVERS; i++) { struct metaserver *m = &metaservers[i]; int j; fprintf(stderr, "solicit[%d](ours:'%s' type='%s' pport=%d oport=%d meta='%s' mport=%d)\n", i, m->ours, m->type, m->pport, m->oport, m->host, m->port ); /* skip empty metaserver entries */ if (m->sock == -1) { fprintf(stderr, " skip empty metaserver entries\n" ); continue; } /* only process entries with the correct server type */ if( status->gameup & GU_INROBOT && m->type[0] != 'i' && m->type[0] != 'I' ) { fprintf(stderr, " skip metaserver entries with non-INL type during INL mode\n" ); continue; } else if( !(status->gameup & GU_INROBOT) && ( m->type[0] == 'i' || m->type[0] == 'I' ) ) { fprintf(stderr, " skip metaserver entries with INL type during non-INL mode\n" ); continue; } /* if we told metaserver recently, don't speak yet */ if (!force) if ((now-m->sent) < m->minimum) continue; /* don't remake the packet unless necessary */ /* for INL the always recreate because we have multiple ports */ if ( status->gameup & GU_INROBOT ) { here = packet; nplayers=0; nfree=0; gamefull=0; isrsa=0; } if ( here == packet ) { if (status->gameup & GU_NEWBIE) { for (j = 0; j < queues[QU_NEWBIE_PLR].high_slot; j++) { if (players[j].p_status == PFREE) nfree++; else nplayers++; } } else if (status->gameup & GU_PRET) { for (j = 0; j < queues[QU_PRET_PLR].high_slot; j++) { if (players[j].p_status == PFREE) nfree++; else nplayers++; } } else if (status->gameup & GU_INROBOT && strncasecmp( m->ours, "home.", 5 ) == 0 ) { for (j = queues[QU_HOME].low_slot; j < queues[QU_HOME].high_slot; j++) { if (players[j].p_status == PFREE) nfree++; else nplayers++; } } else if (status->gameup & GU_INROBOT && strncasecmp( m->ours, "away.", 5 ) == 0 ) { for (j = queues[QU_AWAY].low_slot; j < queues[QU_AWAY].high_slot; j++) { if (players[j].p_status == PFREE) nfree++; else nplayers++; } } else { for (j = 0; j < queues[QU_PICKUP].high_slot; j++) { if (players[j].p_status == PFREE) nfree++; else nplayers++; } } fprintf(stderr, "before: nfree=%d nplayers=%d gamefull=%d\n", nfree, nplayers, gamefull ); /* Special case: do *not* report anything for INL servers if nplayers=0, except one last time when players are leaving. Actually i just want to delist the server. is there a better way? ehb */ if( nplayers == 0 && status->gameup & GU_INROBOT && ( strcmp(packet, m->prior) == 0 || strcmp( m->prior, "") == 0 ) ) { fprintf(stderr, " skip metaserver entries during INL mode if zero players\n" ); continue; } /* if the free slots are zero, translate it to a queue length */ /* and report that the game is full */ if (nfree == 0) { if (status->gameup & GU_NEWBIE) nfree = -queues[QU_NEWBIE_PLR].count; else if (status->gameup & GU_PRET) nfree = -queues[QU_PRET_PLR].count; else if (status->gameup & GU_INROBOT && strncasecmp( m->ours, "home.", 5 ) == 0 ) nfree = -queues[QU_HOME].count; else if (status->gameup & GU_INROBOT && strncasecmp( m->ours, "away.", 5 ) == 0 ) nfree = -queues[QU_AWAY].count; else nfree = -queues[QU_PICKUP].count; gamefull++; } fprintf(stderr, " nfree=%d nplayers=%d gamefull=%d\n", nfree, nplayers, gamefull ); #ifdef RSA isrsa++; /* this is an RSA server */ #endif /* build start of the packet, the server information */ sprintf(here, "%s\n%s\n%s\n%d\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n", /* version */ "b", /* address */ m->ours, /* type */ m->type, /* port */ m->pport, /* observe */ m->oport, /* players */ nplayers, /* free */ nfree, /* t-mode */ status->tourn ? "y" : "n", /* RSA */ isrsa ? "y" : "n", /* full */ gamefull ? "y" : "n", /* comment */ m->comment ); here += strlen(here); /* now append per-player information to the packet */ for (j=0; j<MAXPLAYER; j++) { /* ignore free slots */ if (players[j].p_status == PFREE || #ifdef LTD_STATS ltd_ticks(&(players[j]), LTD_TOTAL) == 0) #else players[j].p_stats.st_tticks == 0) #endif continue; fixed_name = name_fix(players[j].p_name); /*get rid of non-printables*/ fixed_login = name_fix(players[j].p_login); /* make sure name_fix() doesn't return NULL */ name = ( fixed_name != NULL ) ? fixed_name : players[j].p_name; login = ( fixed_login != NULL ) ? fixed_login : players[j].p_login; /* if string is empty, report "unknown" */ name = ( *(name) == 0 ) ? unknown : name; login = ( *(login) == 0 ) ? unknown : login; sprintf(here, "%c\n%c\n%d\n%d\n%s\n%s@%s\n", /* number */ players[j].p_mapchars[1], /* team */ players[j].p_mapchars[0], /* class */ players[j].p_ship.s_type, /* ??? note change from design, ship type number not string */ /* rank */ players[j].p_stats.st_rank, /* ??? note change from design, rank number not string */ /* name */ name, /* user */ login, /* host */ players[j].p_monitor ); here += strlen(here); free(fixed_name); /*because name_fix malloc()s a string */ free(fixed_login); } } fprintf( stderr, " packet created for sending:\n%s\n", packet ); /* if we have exceeded the maximum time, force an update */ if ((now-m->sent) > m->maximum) force=1; /* if we are not forcing an update, and nothing has changed, drop */ if (!force) if (!strcmp(packet, m->prior)) { fprintf( stderr, " No change in packet since last time. dont send.\n" ); continue; } /* send the packet */ if (udp_tx(m, packet, here-packet)) { m->sent=time(NULL); strcpy(m->prior, packet); } } } -------------- next part -------------- /* bytes to reserve for outgoing packet to metaserver */ #define MAXMETABYTES 2048 /* maximum number of metaservers supported */ #define MAXMETASERVERS 4 /* minimum allowable delay between updates to metaserver */ #define META_MINIMUM_DELAY 60 /* Note: changing this may cause the metaserver to delist your server */ /* maximum delay between updates to metaserver */ #define META_MAXIMUM_DELAY 900 /* ship classes (wish these were in data.c/data.h) */ /* static char *ships[] = {"SC", "DD", "CA", "BB", "AS", "SB", "GA"}; */ /* structure of information about a single metaserver */ struct metaserver { /* data items derived from metaservers file */ char host[32]; /* address of metaserver (DNS) */ int port; /* port of metaserver */ int minimum; /* minimum update time */ int maximum; /* maximum update time */ char ours[32]; /* DNS address of server */ char type[2]; /* server type code (B/P/C/H/?) */ int pport; /* server main player port (e.g. 2592) */ int oport; /* server observer player port */ char comment[32]; /* comment string */ /* our own data about the communication with the metaserver */ int sock; /* our socket number */ struct sockaddr_in address; /* address of metaserver */ time_t sent; /* date time metaserver last updated */ char prior[MAXMETABYTES]; /* prior packet sent */ }; void solicit(int force); -------------- next part -------------- _______________________________________________ vanilla-devel mailing list vanilla-devel at us.netrek.org https://mailman.real-time.com/mailman/listinfo/vanilla-devel