Vanilla Netrek Server Development Archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[VANILLA-L:364] Vanilla Metaserver Solicitation Patches 2.7pl4+Solicit
Folks,
I have constructed a patch to both the metaserver and the Vanilla server that
provides for rapid and cheap UDP based update of the metaserver information.
This is a prototype for the Metaserver Redevelopment Project. It's a case of
20% of effort for the 80% benefit ... namely an increase in data timeliness
and a decrease in network bandwidth.
Vanilla Team : your action is to review the code in solicit.c and the change
to daemonII.c in the following patch. This is prototype code, not intended
for immediate use.
Metaserver Team : you have to review the whole thing, and preferrably apply the
patches to a test metaserver and later the production metaservers.
There are two patches here, and it's no use installing them unless a
metaserver that listens to the UDP packets is available.
Format of the .metaservers file used by server ...
% cat .metaservers
metaserver.stl.dec.com 3521 60 300 ogg.stl.dec.com B 2592 2593 available
%
Host name of metaserver,
Port number of metaserver (UDP listen port),
Minimum update time (seconds),
Maximum update time (seconds),
Server host name,
Server type (B=Bronco),
Server player port number,
Server observer port number
Comment word (no spaces ;-( )
Warning: this patch should be applied with -p to preserve pathname prefixes.
*** manifest.old Thu Dec 19 18:40:56 1996
--- manifest Thu Dec 19 18:41:15 1996
***************
*** 87,92 ****
--- 87,93 ----
Server/ntserv/slotmaint.c
Server/ntserv/smessage.c
Server/ntserv/socket.c
+ Server/ntserv/solicit.c
Server/ntserv/startrobot.c
Server/ntserv/openmem.c
Server/ntserv/strdup.c
*** patchlevel.h.old Thu Dec 19 18:40:45 1996
--- patchlevel.h Thu Dec 19 18:31:40 1996
***************
*** 26,31 ****
--- 26,32 ----
/*
* Only compile this to add the name of your patch before it's official */
char *patchname[10] = {
+ "Solicit",
NULL,
};
#endif
*** ./ntserv/Makefile.old Sun Dec 8 01:47:02 1996
--- ./ntserv/Makefile Sun Dec 8 16:20:06 1996
***************
*** 22,28 ****
D_OBJS = daemonII.o sintab.o sysdefaults.o data.o slotmaint.o \
util.o $(RANDOMO) getpath.o getship.o smessage.o queue.o \
! wander2.o openmem.o
# Src files
--- 22,28 ----
D_OBJS = daemonII.o sintab.o sysdefaults.o data.o slotmaint.o \
util.o $(RANDOMO) getpath.o getship.o smessage.o queue.o \
! wander2.o openmem.o solicit.o
# Src files
*** ./ntserv/daemonII.c.old Thu Dec 19 18:23:17 1996
--- ./ntserv/daemonII.c Thu Dec 19 18:46:32 1996
***************
*** 386,391 ****
--- 386,392 ----
if (fuse(QUEUEFUSE)){
queues_purge();
+ solicit(0);
}
if (status->gameup & GU_PAUSED){ /* Game is paused */
***************
*** 2703,2708 ****
--- 2704,2710 ----
/* Kill waiting players */
for (i=0; i<MAXQUEUE; i++) queues[i].q_flags=0;
save_planets();
+ solicit(0);
#if !(defined(SCO) || defined(linux))
sleep(2);
#endif
*** ./ntserv/solicit.c.old Thu Dec 19 18:27:59 1996
--- ./ntserv/solicit.c Fri Dec 13 21:31:38 1996
***************
*** 0 ****
--- 1,229 ----
+ #include <stdio.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+
+ #include "defs.h"
+ #include "struct.h"
+ #include "data.h"
+
+ /* bytes to reserve for outgoing packet to metaserver */
+ #define MAXMETABYTES 1024
+
+ /* maximum number of metaservers supported */
+ #define MAXMETASERVERS 3
+
+ /* 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 */
+ } metaservers[MAXMETASERVERS];
+
+ /* initialisation done flag */
+ static int initialised = 0;
+
+ /* attach to a metaserver, i.e. prepare the socket */
+ static int udp_attach(struct metaserver *m)
+ {
+ /* 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 */
+ m->address.sin_addr.s_addr = INADDR_ANY;
+ 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: bind");
+ 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;
+ }
+ }
+
+ return 1;
+ }
+
+ /* transmit a packet to the metaserver */
+ static int udp_tx(struct metaserver *m, char *buffer, int length)
+ {
+ /* send the packet */
+ 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 *here = packet;
+ time_t now = time(NULL);
+
+ /* 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];
+
+ /* if end of file reached, stop */
+ if (feof(file)) break;
+
+ /* scan the line */
+ fscanf(file, "%s %d %d %d %s %s %d %d %s/n", m->host, &m->port,
+ &m->minimum, &m->maximum, m->ours, m->type, &m->pport,
+ &m->oport, m->comment);
+
+ /* 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++;
+ }
+
+ /* update each metaserver */
+ for (i=0; i<MAXMETASERVERS; i++)
+ {
+ struct metaserver *m = &metaservers[i];
+ int j;
+
+ /* skip empty metaserver entries */
+ if (m->sock == -1) continue;
+
+ /* if we told metaserver recently, don't speak yet */
+ if (!force)
+ if ((now-m->sent) < m->minimum) continue;
+
+ /* count up the number of free slots and players */
+ for (j=0; j<MAXPLAYER; j++)
+ if (players[j].p_status == PFREE)
+ nfree++;
+ else
+ nplayers++;
+
+ /* if the free slots are zero, translate it to a queue length */
+ if (nfree == 0) nfree = -queues[QU_PICKUP].count;
+
+ /* 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",
+ /* version */ "a",
+ /* address */ m->ours,
+ /* type */ m->type,
+ /* port */ m->pport,
+ /* observe */ m->oport,
+ /* players */ nplayers,
+ /* free */ nfree,
+ /* t-mode */ status->tourn ? "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 || players[j].p_stats.st_tticks==0)
+ continue;
+ 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 */ players[j].p_name,
+ /* user */ players[j].p_login,
+ /* host */ players[j].p_monitor );
+ here += strlen(here);
+ }
+
+ /* 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)) continue;
+
+ /* send the packet */
+ if (udp_tx(m, packet, here-packet)) {
+ m->sent=time(NULL);
+ strcpy(m->prior, packet);
+ }
+ }
+ }
____ _ _ _
/ ___| _ _ | |_ | | | | ___ _ __ ___
| | | | | | | __| | |_| | / _ \ | '__| / _ \
| |___ | |_| | | |_ | _ | | __/ | | | __/
\____| \__,_| \__| |_| |_| \___| |_| \___|
diff -c metaserver.old/Makefile metaserver/Makefile
*** metaserver.old/Makefile Thu Dec 28 22:45:13 1995
--- metaserver/Makefile Mon Dec 9 17:59:57 1996
***************
*** 25,31 ****
all: $(TARGET)
$(TARGET): $(OFILES)
! cc $(CFLAGS) -o $(TARGET) $(OFILES) $(LIBS)
$(OFILES): meta.h
--- 26,32 ----
all: $(TARGET)
$(TARGET): $(OFILES)
! $(CC) $(CFLAGS) -o $(TARGET) $(OFILES) $(LIBS)
$(OFILES): meta.h
diff -c metaserver.old/disp_new.c metaserver/disp_new.c
*** metaserver.old/disp_new.c Thu Dec 28 00:56:12 1995
--- metaserver/disp_new.c Wed Dec 11 19:20:52 1996
***************
*** 112,117 ****
--- 112,121 ----
Uprintf(idx, "%3d %-17s %s\n", ago, "* Timed out",
flagbuf);
break;
+ case WD_FLOODING:
+ Uprintf(idx, "%3d %-17s %s\n", ago, "* Flooding",
+ flagbuf);
+ break;
default:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Not responding",
flagbuf);
***************
*** 233,238 ****
--- 237,245 ----
break;
case WD_TIMEOUT:
Uprintf(idx, "Timed out waiting for response.\n");
+ break;
+ case WD_FLOODING:
+ Uprintf(idx, "Flooding metaserver, temporarily delisted.\n");
break;
default:
log_msg("got weird sp->why_dead (%d) in display_verbose()",
diff -c metaserver.old/meta.h metaserver/meta.h
*** metaserver.old/meta.h Thu Dec 28 22:39:59 1995
--- metaserver/meta.h Wed Dec 11 19:21:18 1996
***************
*** 25,30 ****
--- 25,32 ----
#define MAX_CALLS 3 /* you get this many calls... */
#define FLOOD_TIME 10 /* ...over this many seconds. */
+ /* server flood controls */
+ #define MIN_UREAD_TIME 60
/*
* end of configurable stuff
***************
*** 82,88 ****
/* global structure declarations */
typedef enum { SS_WORKING, SS_QUEUE, SS_OPEN, SS_EMPTY, SS_NOCONN, SS_INIT }
SERVER_STATUS; /* note: this affects sorting order */
! typedef enum { WD_UNKNOWN, WD_CONN, WD_READ, WD_GARBAGE, WD_TIMEOUT } WHY_DEAD;
typedef enum { PS_PORTM, PS_PORT } PROBE_STATE;
typedef enum { CS_CLOSED, CS_CONNECTING, CS_CONNECTED } CONN_STATE;
typedef struct {
--- 84,91 ----
/* global structure declarations */
typedef enum { SS_WORKING, SS_QUEUE, SS_OPEN, SS_EMPTY, SS_NOCONN, SS_INIT }
SERVER_STATUS; /* note: this affects sorting order */
! typedef enum { WD_UNKNOWN, WD_CONN, WD_READ, WD_GARBAGE, WD_TIMEOUT,
! WD_FLOODING } WHY_DEAD;
typedef enum { PS_PORTM, PS_PORT } PROBE_STATE;
typedef enum { CS_CLOSED, CS_CONNECTING, CS_CONNECTED } CONN_STATE;
typedef struct {
diff -c metaserver.old/scan.c metaserver/scan.c
*** metaserver.old/scan.c Thu Dec 28 22:42:32 1995
--- metaserver/scan.c Thu Dec 19 18:16:15 1996
***************
*** 876,882 ****
--- 876,1100 ----
last_busy = busy; /* got one, update last_busy */
}
+ /* Maximum size of a UDP packet accepted */
+ /* ??? need to have this documented and agreed */
+ #define MAXMETABYTES 1024
+ /* Port on metaserver for incoming UDP */
+ #define USOCKPORT 3521
+
+ void
+ uread(int usock)
+ {
+ char packet[MAXMETABYTES];
+ struct hostent *hp;
+ struct sockaddr_in from;
+ unsigned int fromlen = sizeof(from);
+ int bytes;
+ SERVER *sp, srvbuf;
+ char *p;
+ int i;
+
+ /* read the UDP packet from the server */
+ bytes = recvfrom(usock, packet, MAXMETABYTES, 0, (struct sockaddr *)&from,
+ &fromlen );
+ if (bytes < 0) {
+ perror("uread: recvfrom");
+ return;
+ }
+
+ /* initialise our server structure */
+ srvbuf.status = SS_OPEN;
+ srvbuf.last_update = time(NULL);
+ srvbuf.next_update = srvbuf.last_update+3600;
+ srvbuf.addr = from.sin_addr.s_addr;
+
+ /* null terminate it so we can use strtok on it */
+ packet[bytes] = 0;
+ #ifdef UREADTRACE
+ log_msg("%s %s", inet_ntoa(srvbuf.addr), packet );
+ #endif
+
+ /* version */
+ p = strtok(packet, "\n");
+ if (p == NULL) return;
+ if (p[0] != 'a') {
+ log_msg("uread: bad version %02.2x from %s\n", p[0],
+ inet_ntoa(srvbuf.addr));
+ return;
+ }
+
+ /* address (DNS, as stated by server within packet) */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ strcpy(srvbuf.hostname, p);
+
+ /* address (IP, as received on socket) */
+ p = inet_ntoa ( from.sin_addr );
+ strcpy(srvbuf.ip_addr, p);
+
+ /* type */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ srvbuf.type[0] = p[0];
+
+ /* port */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ srvbuf.port = atoi(p);
+
+ /* observer port (ignored) */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+
+ /* number of players */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ srvbuf.player_count = atoi(p);
+ if (srvbuf.player_count == 0) srvbuf.status = SS_EMPTY;
+
+ /* number of free slots */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ if (atoi(p) > 0) srvbuf.max_players = srvbuf.player_count + atoi(p);
+ srvbuf.queue_size = -atoi(p);
+ if (srvbuf.queue_size < 0) srvbuf.queue_size = 0;
+ if (srvbuf.queue_size > 0) srvbuf.status = SS_QUEUE;
+
+ /* t-mode */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ srvbuf.display_flags = 0;
+ if (p[0] == 'y') {
+ srvbuf.display_flags |= DF_HAD_TMODE;
+ }
+
+ /* comment */
+ p = strtok(NULL, "\n");
+ if (p == NULL) goto truncated;
+ strncpy(srvbuf.comment, p, 40);
+
+ /* parse player list */
+ if (srvbuf.player_count > 0)
+ {
+ int i; /* player number */
+
+ for (i=0; i<MAX_PLAYER; i++) srvbuf.players[i].p_status = PFREE;
+
+ for(;;)
+ {
+ /* slot number */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+
+ /* determine offset in our player structure, give up if bad */
+ for (i=0; i<MAX_PLAYER; i++) if (shipnos[i] == p[0]) break;
+ if (i == MAX_PLAYER) break;
+
+ /* save the second map character */
+ srvbuf.players[i].p_mapchars[1] = p[0];
+
+ /* team letter */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+ srvbuf.players[i].p_mapchars[0] = p[0];
+ switch (p[0])
+ {
+ /* pathetic hack */
+ case 'I': srvbuf.players[i].p_team = 0; break;
+ case 'F': srvbuf.players[i].p_team = 1; break;
+ case 'R': srvbuf.players[i].p_team = 2; break;
+ case 'K': srvbuf.players[i].p_team = 4; break;
+ case 'O': srvbuf.players[i].p_team = 8; break;
+ default : srvbuf.players[i].p_team = 7; break;
+ }
+
+ /* ship class */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+ srvbuf.players[i].ship_type = atoi(p);
+ /* ??? note change from design, ship type number not string */
+
+ /* rank */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+ srvbuf.players[i].p_rank = atoi(p);
+ /* ??? note change from design, rank number not string */
+
+ /* name */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+ strncpy(srvbuf.players[i].p_name, p, 16);
+
+ /* login */
+ p = strtok(NULL, "@");
+ if (p == NULL) break;
+ strncpy(srvbuf.players[i].p_login, p, 16);
+
+ /* monitor */
+ p = strtok(NULL, "\n");
+ if (p == NULL) break;
+ strncpy(srvbuf.players[i].p_monitor, p, 16);
+
+ srvbuf.players[i].p_status = PALIVE;
+ }
+ }
+
+ /* now either update an existing server entry or create a new one */
+ for (i = 0, sp = servers; i < server_count; i++, sp++) {
+ if (!strcmp(srvbuf.hostname, sp->hostname) &&
+ !strcmp(srvbuf.ip_addr, sp->ip_addr) &&
+ (srvbuf.type[0] == sp->type[0]) &&
+ srvbuf.port == sp->port) {
+ /* we know about the server already */
+
+ /* check for flooding */
+ if (srvbuf.player_count != 0) {
+ if ((srvbuf.last_update - sp->last_update) <
+ MIN_UREAD_TIME) {
+
+ /* server flooding, delist it */
+ sp->status = SS_NOCONN;
+ sp->why_dead = WD_FLOODING;
+ sp->last_update = srvbuf.last_update;
+ return;
+ }
+ }
+ /* note above: player count zero is the only condition that
+ server will violate our time limit, and we hardly expect any
+ server operators to _want_ to use player count zero flooding */
+
+ /* server is being nice, accept the update and return */
+ bcopy(&srvbuf, sp, sizeof(SERVER));
+ log_msg("usock: updated by %s (%s)\n", srvbuf.hostname,
+ srvbuf.ip_addr );
+ return;
+ }
+ }
+
+ if (servers == NULL) {
+ servers = (SERVER *) malloc(sizeof(SERVER));
+ } else {
+ servers = (SERVER *) realloc(servers,
+ sizeof(SERVER) * (server_count+1));
+ }
+ if (servers == NULL) {
+ fprintf(stderr, "ERROR: uread: malloc/realloc failure (servers)\n");
+ exit(1);
+ }
+
+ bcopy(&srvbuf, &servers[server_count], sizeof(SERVER));
+ server_count++;
+
+ log_msg("usock: new server %s (%s)\n", srvbuf.hostname, srvbuf.ip_addr );
+ return;
+
+ truncated:
+ log_msg("uread: truncated packet of %d bytes from %s\n", bytes,
+ inet_ntoa(srvbuf.addr));
+ return;
+ }
+
/*
* Basic strategy: do blocking I/O until we get everything we want. Wake up
* once every 60 seconds to check our status and look for more work to do.
***************
*** 892,897 ****
--- 1110,1116 ----
int on = 1;
fd_set readfds, writefds;
int cc, lsock, *listen_sock = NULL;
+ int usock;
struct itimerval udt;
#ifdef DEBUG2
***************
*** 904,909 ****
--- 1123,1144 ----
FD_ZERO(&readfd_set);
FD_ZERO(&writefd_set);
+ /* create UDP socket for servers to send us updates */
+ if ((usock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("ERROR: unable to create UDP socket for server reports");
+ log_msg("exit: unable to create UDP socket");
+ exit(1);
+ }
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(USOCKPORT);
+ if (bind(usock, &addr, sizeof(addr)) < 0) {
+ perror("ERROR: unable to bind to UDP socket");
+ log_msg("exit: unable to bind to UDP socket");
+ exit(1);
+ }
+ FD_SET(usock, &readfd_set);
+
/* Prepare sockets to accept calls */
if ((listen_sock = (int *) malloc(port_count * sizeof(int))) == NULL) {
fprintf(stderr, "ERROR: malloc failure (main/listen)\n");
***************
*** 966,971 ****
--- 1201,1212 ----
continue;
}
if (verbose) fseek(stdout, (off_t) 0, 2);
+
+ /* activity on servers' UDP socket, read it */
+ if (FD_ISSET(usock, &readfds)) {
+ uread(usock);
+ FD_CLR(usock, &readfds);
+ }
/* cc is #of interesting file descriptors */
for (i = 0; i < maxfd && cc; i++) {
--
James Cameron (cameron@stl.dec.com)
Digital Equipment Corporation (Australia) Pty. Ltd. A.C.N. 000 446 800