Readme for Metaserver client-side drop-in code:

1: What it does
        This little bit of code will connect your mud via a one-
way udp connection to a metaserver that lists all registered muds,
their addresses, uptime, and number of users.  People can then
connect to it with any client they want, type "list" to see the list,
then connect to a mud of their choosing.  It opens up a connection to
port 5000, so if you have a firewall set up, you might need to adjust
it accordingly.

2: How it does it
        setup_report() handles getting the necessary information in
the necessary formats and places, then opens a persistent (from the
mud's point of view, UDP isn't a connection oriented protocol) socket
to the metaserver.  This uses gethostbyname() so there might be a delay
in this step if you have a slow nameserver.  (I'm working on a way
around this).  As of version 1.10, you now have the option to have it be
either persistent or not.  If you choose not to use the persistent connection
and you experience small lags each time your mud updates, then I'd recommend
switching to the persistent connection.  Untouched, the code is defaulting to
using the non-persistent method, and sendto().  This actually grew out of the
fix for Solaris, which would complain about sendto() when passed valid sockaddr
info on an already connected socket.

        report_status() called from heartbeat() builds a string containing
your uptime, name, users, and port, then sends it off to the server.

        The server parses this and either adds or updates the mud's
information.  Crashed or sleeping (no players) muds are expired from the
list after 15 minutes of inactivity.  See below for a way to keep your mud
listed while it's sleeping.

3: Howto
Copy client.c to your src directory, then edit it.
find the line "/* #define MUDNAME "BOGUS_MUDNAME" */", remove the comments,
and change BOGUS_MUDNAME to the name of your mud.  If you don't remove the
comments, it'll fail to compile.  If you don't change the name, your mud won't
be listed.

If you have some dynamic dns for your ip from anywhere, fine the line:
#define MUDDNS "BOGUS.DNS.NET", and replase the BOGUS.DNS.NET part with
your dns.

Open Makefile.
add "client.o" to the end of the OBJFILES definition. If needed, add the
client.o: dependency at the end.  See the FAQ that came with the source for how
to add dependencies. (It's in section 3.12).

(Although I really hate that crap at the end of the Makefile and like it
in it's own file.  Lots easier to regenerate on the fly.)

Open comm.c  -  The patch below does this step for you. If you're applying it,
don't do this step here.  At the top of the file with the other function
prototypes add:
 void setup_report(void);
 void report_status(void);

find the function init_world() and somewhere in it, add:
 log("Setting up metaserver reporting socket.");
 setup_report();

find the function heartbeat():
Somewhere in one of the if(pulse % ...) blocks that are longer than one
minute but less than 15  (In stock pl21 PULSE_AUTOSAVE is 1 minute,
PULSE_USAGE is 5.  Check your function and defines for your specifics.)
add the line:
 report_status();
(You might need to add {}'s around the if-block, otherwise it'll execute
every time heartbeat() is called.  I have the bandwidth for it but it would hammer
your server pretty hard.)


4: Listing your mud while no-one's on.
The problem at this point is that you need players logged in for this to
run and update.  If there's no users, your server silently sleeps in a
select() on the mother_desc.  While it's there, nothing is run in heartbeat().
This patch below simply adds a timeout to that select.  When the
timeout expires, the mud updates itself and goes back to sleeping.  To
change the update interval, you change the value in _tv.tv_sec.

(This here is against stock bpl21.  For other versions it should be very
similar, but if you have a problem contact me and I'll see what I can do.)
Just copy/paste this block below to a file like "report.patch", save that to
your src/ directory, and type "patch <report.patch".  It'll update comm.c.
If you get any rejects or have any problems, the file "comm.c.orig" will contain
the prepatched version of it.  If you decide you don't like it or it causes some
kind of problem, type "patch -R <report.patch" and it'll be removed.  Please
report the problems to me.  (I'm not going to spend lots of time making diff's
against lots of versions... if you leave out the comments it only takes a couple
minutes to hand-patch it anyway).

[--- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE ---]

--- comm.c	Sun Oct  6 10:36:08 2002
+++ comm.c.new	Sun Oct  6 10:46:11 2002
@@ -162,6 +162,8 @@ void free_messages(void);
 void Board_clear_all(void);
 void free_social_messages(void);
 void Free_Invalid_List(void);
+void setup_report(void);
+void report_status(void);
 
 #ifdef __CXREF__
 #undef FD_ZERO
@@ -611,15 +613,37 @@ void game_loop(socket_t mother_desc)
 
     /* Sleep if we don't have any connections */
     if (descriptor_list == NULL) {
+      int _retval = 0;
+      struct timeval _tv;
+
       log("No connections.  Going to sleep.");
+loop: /*
+       * Re-entry point after timer expires. _tv is undefined at that point so re-initialize it.
+       * As it is, this gives you a 60 second pause between reports.  You can change this to any
+       * number up to 15 minutes.  5 or 10 minutes (300 or 600 seconds) should be sufficient.
+       */
+      _tv.tv_sec = 60;
+      _tv.tv_usec = 0;
+      
       FD_ZERO(&input_set);
       FD_SET(mother_desc, &input_set);
-      if (select(mother_desc + 1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0) {
+      /*
+       * Have select expire after _tv.tv_sec seconds.  When it does, report & hop back up to reinitialize _tv.
+       */
+      if ((_retval = select(mother_desc + 1, &input_set, (fd_set *) 0, (fd_set *) 0, &_tv)) < 0) {
 	if (errno == EINTR)
 	  log("Waking up to process signal.");
 	else
 	  perror("SYSERR: Select coma");
-      } else
+      } else if(_retval == 0) { /* _retval = 0, timer expired */
+          report_status();
+          /*
+	   * At this point, _tv is undefined, go back and re-initialize it and go back to sleep.
+	   * I use goto rather than continue so the log doesn't get hammered logging "No connections.  Going to sleep"
+	   * every time we report.
+	   */
+	  goto loop; 
+      } else 
 	log("New connection.  Waking up.");
       gettimeofday(&last_time, (struct timezone *) 0);
     }


[---- END PATCH ----]

5: Miscellaneous
This is developed on a (loosely based on RedHat) linux system.  If you have any
problems on other arch's or os's that you can't figure out, let me know and I'll
see what I can do.  If you come up with a fix for any of them, please let me
know so it can be included.

This IS a work in progress.  Please report any bugs, problems,
bugfixes, solutions, suggestions, improvements to:
mike@velgarian.sytes.net

5.1: Known working systems.
Linux
Cygwin
Solaris

Let me know if it works on any different systems so I can add it to the list.  If it
doesn't work on yours, and you get it fixed, send me either a diff or the new file and
I'll work some #ifdef's in.

