Offline Room String Database

This patch places all room titles and descriptions in a separate file that
is accessed every time they are needed.  This differs from the other patch
that unloads room descriptions when they aren't needed.  This patch saved
about 600 KB on a stock CircleMUD.  Profiling this code showed that the
new functions take little time compared to some of the other functions. 
This patch is based on stock CircleMUD bpl14. 

The SIZE field is the important one, in KB.

With this patch:
USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
greerga  13818 35.7  4.1  3352  2636   4 S   13:47   0:02 bin/circle

Stock CircleMUD:
USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
greerga  13864 47.2  5.1  3988  3276   4 S   14:04   0:01 bin/circle

All of the resulting strings are packed into the database as tightly as
possible, even without a terminating NUL.  The stock CircleMUD one ends up
being 453,201 bytes large. The extra 150 KB savings I surmise to be from
the saved malloc() overhead and ~15 KB saved from removing the 'name' and
'description' pointers. 

NOTE: You do not have to remove this text in order to apply this patch.

This will be extremely incompatible with any existing OLC system but would
not be hard to modify the OLC to use this. 

George Greer
greerga@circlemud.org
7/4/98

diff -uprN stk/Makefile wldswp/Makefile
--- stk/Makefile	Fri Jul  3 20:50:22 1998
+++ wldswp/Makefile	Sat Jul  4 13:05:49 1998
@@ -29,14 +29,14 @@ OBJFILES = comm.o act.comm.o act.informa
 	castle.o class.o config.o constants.o db.o fight.o graph.o handler.o \
 	house.o interpreter.o limits.o magic.o mail.o mobact.o modify.o \
 	objsave.o olc.o random.o shop.o spec_assign.o spec_procs.o \
-	spell_parser.o spells.o utils.o weather.o
+	spell_parser.o spells.o utils.o weather.o world.o
 
 CXREF_FILES = act.comm.c act.informative.c act.item.c act.movement.c \
 	act.offensive.c act.other.c act.social.c act.wizard.c ban.c boards.c \
 	castle.c class.c comm.c config.c constants.c db.c fight.c graph.c \
 	handler.c house.c interpreter.c limits.c magic.c mail.c mobact.c \
 	modify.c objsave.c olc.c random.c shop.c spec_assign.c spec_procs.c \
-	spell_parser.c spells.c utils.c weather.c
+	spell_parser.c spells.c utils.c weather.c world.c
 
 default: all
 
diff -uprN stk/act.informative.c wldswp/act.informative.c
--- stk/act.informative.c	Thu Jun 25 01:16:56 1998
+++ wldswp/act.informative.c	Fri Jul  3 23:25:46 1998
@@ -376,13 +376,13 @@ ACMD(do_exits)
       if (GET_LEVEL(ch) >= LVL_IMMORT)
 	sprintf(buf2, "%-5s - [%5d] %s\r\n", dirs[door],
 		GET_ROOM_VNUM(EXIT(ch, door)->to_room),
-		world[EXIT(ch, door)->to_room].name);
+		get_room_name(EXIT(ch, door)->to_room, buf1));
       else {
 	sprintf(buf2, "%-5s - ", dirs[door]);
 	if (IS_DARK(EXIT(ch, door)->to_room) && !CAN_SEE_IN_DARK(ch))
 	  strcat(buf2, "Too dark to tell\r\n");
 	else {
-	  strcat(buf2, world[EXIT(ch, door)->to_room].name);
+	  strcat(buf2, get_room_name(EXIT(ch, door)->to_room, buf1));
 	  strcat(buf2, "\r\n");
 	}
       }
@@ -414,17 +414,17 @@ void look_at_room(struct char_data * ch,
   if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_ROOMFLAGS)) {
     sprintbit(ROOM_FLAGS(ch->in_room), room_bits, buf);
     sprintf(buf2, "[%5d] %s [ %s]", GET_ROOM_VNUM(IN_ROOM(ch)),
-	    world[ch->in_room].name, buf);
+	    get_room_name(ch->in_room, buf1), buf);
     send_to_char(buf2, ch);
   } else
-    send_to_char(world[ch->in_room].name, ch);
+    send_to_char(get_room_name(ch->in_room, buf2), ch);
 
   send_to_char(CCNRM(ch, C_NRM), ch);
   send_to_char("\r\n", ch);
 
   if ((!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_BRIEF)) || ignore_brief ||
       ROOM_FLAGGED(ch->in_room, ROOM_DEATH))
-    send_to_char(world[ch->in_room].description, ch);
+    send_to_char(get_room_desc(ch->in_room, buf2), ch);
 
   /* autoexits */
   if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOEXIT))
@@ -1311,7 +1311,8 @@ void perform_mortal_where(struct char_da
 	continue;
       if (world[ch->in_room].zone != world[i->in_room].zone)
 	continue;
-      sprintf(buf, "%-20s - %s\r\n", GET_NAME(i), world[i->in_room].name);
+      sprintf(buf, "%-20s - %s\r\n", GET_NAME(i),
+		get_room_name(i->in_room, buf1));
       send_to_char(buf, ch);
     }
   } else {			/* print only FIRST char, not all. */
@@ -1322,7 +1323,8 @@ void perform_mortal_where(struct char_da
 	continue;
       if (!isname(arg, i->player.name))
 	continue;
-      sprintf(buf, "%-25s - %s\r\n", GET_NAME(i), world[i->in_room].name);
+      sprintf(buf, "%-25s - %s\r\n", GET_NAME(i),
+		get_room_name(i->in_room, buf1));
       send_to_char(buf, ch);
       return;
     }
@@ -1341,7 +1343,7 @@ void print_object_location(int num, stru
 
   if (obj->in_room > NOWHERE) {
     sprintf(buf + strlen(buf), "[%5d] %s\r\n",
-	    GET_ROOM_VNUM(IN_ROOM(obj)), world[obj->in_room].name);
+	    GET_ROOM_VNUM(IN_ROOM(obj)), get_room_name(obj->in_room, buf1));
     send_to_char(buf, ch);
   } else if (obj->carried_by) {
     sprintf(buf + strlen(buf), "carried by %s\r\n",
@@ -1381,10 +1383,12 @@ void perform_immort_where(struct char_da
 	  if (d->original)
 	    sprintf(buf, "%-20s - [%5d] %s (in %s)\r\n",
 		    GET_NAME(i), GET_ROOM_VNUM(IN_ROOM(d->character)),
-		 world[d->character->in_room].name, GET_NAME(d->character));
+		    get_room_name(d->character->in_room, buf1),
+		    GET_NAME(d->character));
 	  else
 	    sprintf(buf, "%-20s - [%5d] %s\r\n", GET_NAME(i),
-		    GET_ROOM_VNUM(IN_ROOM(i)), world[i->in_room].name);
+		    GET_ROOM_VNUM(IN_ROOM(i)),
+		    get_room_name(i->in_room, buf1));
 	  send_to_char(buf, ch);
 	}
       }
@@ -1393,7 +1397,7 @@ void perform_immort_where(struct char_da
       if (CAN_SEE(ch, i) && i->in_room != NOWHERE && isname(arg, i->player.name)) {
 	found = 1;
 	sprintf(buf, "M%3d. %-25s - [%5d] %s\r\n", ++num, GET_NAME(i),
-		GET_ROOM_VNUM(IN_ROOM(i)), world[IN_ROOM(i)].name);
+		GET_ROOM_VNUM(IN_ROOM(i)), get_room_name(IN_ROOM(i), buf1));
 	send_to_char(buf, ch);
       }
     for (num = 0, k = object_list; k; k = k->next)
diff -uprN stk/act.wizard.c wldswp/act.wizard.c
--- stk/act.wizard.c	Thu Jun 25 00:59:20 1998
+++ wldswp/act.wizard.c	Sat Jul  4 13:43:15 1998
@@ -370,8 +370,8 @@ void do_stat_room(struct char_data * ch)
   struct obj_data *j = 0;
   struct char_data *k = 0;
 
-  sprintf(buf, "Room name: %s%s%s\r\n", CCCYN(ch, C_NRM), rm->name,
-	  CCNRM(ch, C_NRM));
+  sprintf(buf, "Room name: %s%s%s\r\n", CCCYN(ch, C_NRM),
+	get_room_name(ch->in_room, buf2), CCNRM(ch, C_NRM));
   send_to_char(buf, ch);
 
   sprinttype(rm->sector_type, sector_types, buf2);
@@ -386,8 +386,8 @@ void do_stat_room(struct char_data * ch)
   send_to_char(buf, ch);
 
   send_to_char("Description:\r\n", ch);
-  if (rm->description)
-    send_to_char(rm->description, ch);
+  if (get_room_desc(ch->in_room, buf))
+    send_to_char(buf, ch);
   else
     send_to_char("  None.\r\n", ch);
 
@@ -2038,7 +2038,7 @@ ACMD(do_show)
       for (j = 0; j < NUM_OF_DIRS; j++)
 	if (world[i].dir_option[j] && world[i].dir_option[j]->to_room == 0)
 	  sprintf(buf + strlen(buf), "%2d: [%5d] %s\r\n", ++k, GET_ROOM_VNUM(i),
-		  world[i].name);
+		  get_room_name(i, buf1));
     page_string(ch->desc, buf, TRUE);
     break;
   case 6:
@@ -2046,7 +2046,7 @@ ACMD(do_show)
     for (i = 0, j = 0; i <= top_of_world; i++)
       if (ROOM_FLAGGED(i, ROOM_DEATH))
 	sprintf(buf + strlen(buf), "%2d: [%5d] %s\r\n", ++j,
-		GET_ROOM_VNUM(i), world[i].name);
+		GET_ROOM_VNUM(i), get_room_name(i, buf1));
     page_string(ch->desc, buf, TRUE);
     break;
   case 7:
@@ -2054,7 +2054,7 @@ ACMD(do_show)
     for (i = 0, j = 0; i < top_of_world; i++)
     if (ROOM_FLAGGED(i, ROOM_GODROOM))
       sprintf(buf + strlen(buf), "%2d: [%5d] %s\r\n",
-		++j, GET_ROOM_VNUM(i), world[i].name);
+		++j, GET_ROOM_VNUM(i), get_room_name(i, buf1));
     page_string(ch->desc, buf, TRUE);
     break;
   case 8:
diff -uprN stk/comm.c wldswp/comm.c
--- stk/comm.c	Fri Jul  3 15:07:39 1998
+++ wldswp/comm.c	Fri Jul  3 23:50:27 1998
@@ -55,6 +55,7 @@
 #include "handler.h"
 #include "db.h"
 #include "house.h"
+#include "world.h"
 
 #ifdef HAVE_ARPA_TELNET_H
 #include <arpa/telnet.h>
@@ -312,6 +313,8 @@ void init_game(int port)
   log("Opening mother connection.");
   mother_desc = init_socket(port);
 
+  world_init();
+
   boot_db();
 
 #ifdef CIRCLE_UNIX
@@ -332,6 +335,7 @@ void init_game(int port)
 
   CLOSE_SOCKET(mother_desc);
   fclose(player_fl);
+  world_end();
 
   if (circle_reboot) {
     log("Rebooting.");
diff -uprN stk/db.c wldswp/db.c
--- stk/db.c	Thu Jun 25 00:44:38 1998
+++ wldswp/db.c	Sat Jul  4 13:43:32 1998
@@ -731,6 +731,7 @@ void parse_room(FILE * fl, int virtual_n
   int t[10], i;
   char line[256], flags[128];
   struct extra_descr_data *new_descr;
+  char *writeme;
 
   sprintf(buf2, "room #%d", virtual_nr);
 
@@ -745,8 +746,11 @@ void parse_room(FILE * fl, int virtual_n
     }
   world[room_nr].zone = zone;
   world[room_nr].number = virtual_nr;
-  world[room_nr].name = fread_string(fl, buf2);
-  world[room_nr].description = fread_string(fl, buf2);
+  /* Write the room title and description. */
+  db_write(writeme = fread_string(fl, buf2));
+  free(writeme);
+  db_write(writeme = fread_string(fl, buf2));
+  free(writeme);
 
   if (!get_line(fl, line)) {
     log("SYSERR: Expecting roomflags/sector type of room #%d but file ended!",
diff -uprN stk/db.h wldswp/db.h
--- stk/db.h	Sat Jun 13 13:51:20 1998
+++ wldswp/db.h	Fri Jul  3 23:53:23 1998
@@ -112,6 +112,9 @@ void	clear_char(struct char_data *ch);
 void	reset_char(struct char_data *ch);
 void	free_char(struct char_data *ch);
 
+int	db_write(const char *);
+const char *get_room_desc(room_rnum rnum, char *description);
+const char *get_room_name(room_rnum rnum, char *title);
 struct obj_data *create_obj(void);
 void	clear_object(struct obj_data *obj);
 void	free_obj(struct obj_data *obj);
diff -uprN stk/fight.c wldswp/fight.c
--- stk/fight.c	Wed Jun 24 19:03:31 1998
+++ wldswp/fight.c	Fri Jul  3 23:28:44 1998
@@ -183,11 +183,11 @@ void check_killer(struct char_data * ch,
   if (!PLR_FLAGGED(vict, PLR_KILLER) && !PLR_FLAGGED(vict, PLR_THIEF)
       && !PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(ch) && !IS_NPC(vict) &&
       (ch != vict)) {
-    char buf[256];
+    char buf[256], grm[1024];
 
     SET_BIT(PLR_FLAGS(ch), PLR_KILLER);
     sprintf(buf, "PC Killer bit set on %s for initiating attack on %s at %s.",
-	    GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name);
+	    GET_NAME(ch), GET_NAME(vict), get_room_name(vict->in_room, grm));
     mudlog(buf, BRF, LVL_IMMORT, TRUE);
     send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);
   }
@@ -782,8 +782,9 @@ int damage(struct char_data * ch, struct
     }
 
     if (!IS_NPC(victim)) {
+      char grm[1024];
       sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch),
-	      world[victim->in_room].name);
+	      get_room_name(victim->in_room, grm));
       mudlog(buf2, BRF, LVL_IMMORT, TRUE);
       if (MOB_FLAGGED(ch, MOB_MEMORY))
 	forget(ch, victim);
diff -uprN stk/olc.c wldswp/olc.c
--- stk/olc.c	Tue Jun 16 19:13:05 1998
+++ wldswp/olc.c	Sat Jul  4 13:46:28 1998
@@ -222,7 +222,7 @@ void olc_interpreter(void *targ, int mod
   case OLC_NAME:
     switch (mode) {
     case OLC_ROOM:
-      olc_string(&(olc_room->name), MAX_ROOM_NAME, arg);
+/*      olc_string(&(olc_room->name), MAX_ROOM_NAME, arg); */
       break;
     case OLC_MOB:
       olc_string(&olc_mob->player.short_descr, MAX_MOB_NAME, arg);
@@ -239,7 +239,7 @@ void olc_interpreter(void *targ, int mod
   case OLC_DESC:
     switch (mode) {
     case OLC_ROOM:
-      olc_string(&olc_room->description, MAX_ROOM_DESC, arg);
+/*      olc_string(&olc_room->description, MAX_ROOM_DESC, arg); */
       break;
     case OLC_MOB:
       olc_string(&olc_mob->player.long_descr, MAX_MOB_DESC, arg);
diff -uprN stk/shop.c wldswp/shop.c
--- stk/shop.c	Mon Jun 22 23:30:18 1998
+++ wldswp/shop.c	Fri Jul  3 23:29:30 1998
@@ -1209,7 +1209,7 @@ void list_detailed_shop(struct char_data
     if (index)
       strcat(buf, ", ");
     if ((temp = real_room(SHOP_ROOM(shop_nr, index))) != NOWHERE)
-      sprintf(buf1, "%s (#%d)", world[temp].name, GET_ROOM_VNUM(temp));
+      sprintf(buf1, "%s (#%d)", get_room_name(temp, buf2), GET_ROOM_VNUM(temp));
     else
       sprintf(buf1, "<UNKNOWN> (#%d)", SHOP_ROOM(shop_nr, index));
     handle_detailed_list(buf, buf1, ch);
diff -uprN stk/spells.c wldswp/spells.c
--- stk/spells.c	Thu Jun 18 15:11:21 1998
+++ wldswp/spells.c	Fri Jul  3 23:34:32 1998
@@ -134,7 +134,7 @@ ASPELL(spell_summon)
       sprintf(buf, "%s just tried to summon you to: %s.\r\n"
 	      "%s failed because you have summon protection on.\r\n"
 	      "Type NOSUMMON to allow other players to summon you.\r\n",
-	      GET_NAME(ch), world[ch->in_room].name,
+	      GET_NAME(ch), get_room_name(ch->in_room, buf2),
 	      (ch->player.sex == SEX_MALE) ? "He" : "She");
       send_to_char(buf, victim);
 
@@ -143,7 +143,7 @@ ASPELL(spell_summon)
       send_to_char(buf, ch);
 
       sprintf(buf, "%s failed summoning %s to %s.",
-	      GET_NAME(ch), GET_NAME(victim), world[ch->in_room].name);
+	      GET_NAME(ch), GET_NAME(victim), get_room_name(ch->in_room, buf2));
       mudlog(buf, BRF, LVL_IMMORT, TRUE);
       return;
     }
@@ -191,7 +191,7 @@ ASPELL(spell_locate_object)
 	      i->short_description, PERS(i->carried_by, ch));
     else if (i->in_room != NOWHERE)
       sprintf(buf, "%s is in %s.\r\n", i->short_description,
-	      world[i->in_room].name);
+	      get_room_name(i->in_room, buf1));
     else if (i->in_obj)
       sprintf(buf, "%s is in %s.\r\n", i->short_description,
 	      i->in_obj->short_description);
diff -uprN stk/structs.h wldswp/structs.h
--- stk/structs.h	Tue Jun 16 16:20:56 1998
+++ wldswp/structs.h	Sat Jul  4 13:40:48 1998
@@ -610,8 +610,10 @@ struct room_data {
    room_vnum number;		/* Rooms number	(vnum)		      */
    sh_int zone;                 /* Room zone (for resetting)          */
    int	sector_type;            /* sector type (move/hide)            */
+#if 0
    char	*name;                  /* Rooms name 'You are ...'           */
    char	*description;           /* Shown when entered                 */
+#endif
    struct extra_descr_data *ex_description; /* for examine/look       */
    struct room_direction_data *dir_option[NUM_OF_DIRS]; /* Directions */
    int room_flags;		/* DEATH,DARK ... etc                 */
diff -uprN stk/utils.c wldswp/utils.c
--- stk/utils.c	Sun Jun 21 00:08:44 1998
+++ wldswp/utils.c	Fri Jul  3 23:31:07 1998
@@ -125,10 +125,10 @@ int strn_cmp(const char *arg1, const cha
 /* log a death trap hit */
 void log_death_trap(struct char_data * ch)
 {
-  char buf[150];
+  char buf[150], grm[1024];
 
   sprintf(buf, "%s hit death trap #%d (%s)", GET_NAME(ch),
-	  GET_ROOM_VNUM(IN_ROOM(ch)), world[ch->in_room].name);
+	  GET_ROOM_VNUM(IN_ROOM(ch)), get_room_name(ch->in_room, grm));
   mudlog(buf, BRF, LVL_IMMORT, TRUE);
 }
 
diff -uprN stk/world.c wldswp/world.c
--- stk/world.c	Wed Dec 31 19:00:00 1969
+++ wldswp/world.c	Sat Jul  4 13:38:53 1998
@@ -0,0 +1,126 @@
+#include "conf.h"
+#include "sysdep.h"
+#include "structs.h"
+#include "utils.h"
+#include "db.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+
+extern int top_of_world;
+extern struct room_data *world;
+
+int db_write(const char *);
+int write_entry(const char *, FILE *);
+int read_entry(char *, FILE *);
+
+/*
+ * Idea of file format:
+ *
+ * struct {
+ *   int offset_to_next;
+ *   char title[variable_len];
+ *   int offset_to_next;
+ *   char description[variable_len];
+ * };
+ */
+
+FILE *open_world(const char *flags)
+{
+  FILE *fl;
+
+  fl = fopen("world.bin", flags);
+  if (fl == NULL)
+    perror("world.bin");
+  return fl;
+}
+
+int find_room(room_rnum rnum, FILE *fl)
+{
+  int jump;
+
+  if (rnum < 0 || rnum > top_of_world)
+    return FALSE;
+
+  while (rnum--) {
+    /* Read length of title. */
+    fread(&jump, sizeof(unsigned int), 1, fl);
+    /* Jump past title to room description. */
+    fseek(fl, jump, SEEK_CUR);
+
+    /* Read the length of this room description. */
+    fread(&jump, sizeof(unsigned int), 1, fl);
+    /* Jump offset to next room. */
+    fseek(fl, jump, SEEK_CUR);
+  }
+  return TRUE;
+}
+
+const char *get_room_name(room_rnum rnum, char *title)
+{
+  FILE *fl = open_world("r");
+  if (!find_room(rnum, fl))
+    return NULL;
+  read_entry(title, fl);
+  fclose(fl);
+  return title;
+}
+
+const char *get_room_desc(room_rnum rnum, char *description)
+{
+  FILE *fl = open_world("r");  
+  if (!find_room(rnum, fl))
+    return NULL;
+  /* Just drop the title. */
+  read_entry(NULL, fl);
+  read_entry(description, fl);
+  fclose(fl);
+  return description;
+}
+
+int write_entry(const char *str, FILE *fl)
+{
+  unsigned int len = 0;
+
+  if (str == NULL)
+    fwrite(&len, sizeof(unsigned int), 1, fl);
+  else { 
+    len = strlen(str);
+    fwrite(&len, sizeof(unsigned int), 1, fl);
+    fwrite(str, sizeof(char), len, fl);
+  }
+  return (len + sizeof(unsigned int));
+}
+
+int read_entry(char *write, FILE *fl)
+{
+  unsigned int len;
+  fread(&len, sizeof(unsigned int), 1, fl);
+  if (len) {
+    if (write) {
+      fread(write, sizeof(char), len, fl);
+      write[len] = '\0';
+    } else
+      fseek(fl, len, SEEK_CUR);
+  } else
+    *write = '\0';
+
+  return (len + sizeof(unsigned int));
+}
+
+int db_write(const char *str)
+{
+  FILE *fl = open_world("a");
+  write_entry(str, fl);
+  fclose(fl);
+  return 0;
+}
+
+void world_init(void)
+{
+  remove("world.bin");
+}
+
+void world_end(void)
+{
+  remove("world.bin");
+}
diff -uprN stk/world.h wldswp/world.h
--- stk/world.h	Wed Dec 31 19:00:00 1969
+++ wldswp/world.h	Fri Jul  3 23:49:51 1998
@@ -0,0 +1,3 @@
+void world_init(void);
+void world_end(void);
+
