]> skyeroc.xyz Git - dgamelaunch/commitdiff
Show the number of people watching each game in the watching-menu.
authorPasi Kallinen <paxed@alt.org>
Sat, 1 May 2010 14:41:54 +0000 (14:41 +0000)
committerPasi Kallinen <paxed@alt.org>
Sat, 1 May 2010 14:41:54 +0000 (14:41 +0000)
Also allow sorting by # of watchers.
Requires dgamelaunch to be compiled with --enable-shmem

git-svn-id: svn://katsu.triplehelix.org/dgamelaunch/trunk@536 db0b04b0-f4d1-0310-9a6d-de3e77497b0e

TODO
configure.ac
dgamelaunch.c
dgamelaunch.h
dgl-common.c

diff --git a/TODO b/TODO
index 3bf79e3f6afccb17a33ccb2cd30159408174d1ed..22849bf3e3f4f086600d67747731763a8ba359a5 100644 (file)
--- a/TODO
+++ b/TODO
@@ -9,8 +9,6 @@
    will no longer work, because the cursor won't be moved to the previous
    line again
 
--change the watching-menu paging back to pre-1.5.0 behaviour
- (don't backtrack in the gamelist to keep the last page full)
 -when exiting from watching a player, move the cursor to the last line
  of the screen before enabling the ncurses mode; so when we exit dgl,
  the watched player's game is all visible and the menu prompt doesn't
@@ -118,8 +116,6 @@ or maybe add a new command '' set_charstrip "name" ''
 -public (no-password) accounts? (a per-user flag) what happens when someone
  is playing on the account and someone else logins and we start playing?
 -allow users to run recover themselves
--make dgl show # of watchers. this would probably require adding a
- shared memory block to keep track of who is watching who...
 -configurable stuff: allowed chars in usernames,
  allow char stripping, ...
 
index 8034a99abefa9191f98d458f83ffe38e2f590ad4..e09d6d4989b8963263ec338441494a4379bd2f78 100644 (file)
@@ -144,6 +144,19 @@ if test "$enable_rlimit" = yes; then
 fi
 
 
+AC_ARG_ENABLE(shmem,
+[AC_HELP_STRING([--enable-shmem], [Use a shared memory block to show number of watchers.])],
+[enable_shmem=yes], [])
+
+if test "$enable_shmem" = yes; then
+   AC_CHECK_HEADERS([semaphore.h], [], [AC_MSG_ERROR([semaphore.h not found.])], [])
+   AC_CHECK_HEADERS([sys/ipc.h], [], [AC_MSG_ERROR([sys/ipc.h not found.])], [])
+   AC_CHECK_HEADERS([sys/shm.h], [], [AC_MSG_ERROR([sys/shm.h not found.])], [])
+   AC_MSG_RESULT([Enabled showing number of watchers.])
+   AC_DEFINE(USE_SHMEM,1,[Use shared memory block])
+   LIBS="$LIBS -lrt"
+   # or -pthread?
+fi
 
 
 AC_ARG_WITH(config-file,
index 6cfa2c69102a513b57c01e9910a96bce6740bacd..434628db81758bf79dd6864097b29976bda16c01 100644 (file)
 #ifdef USE_RLIMIT
 #include <sys/resource.h>
 #endif
+
+#ifdef USE_SHMEM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
 #include <libgen.h>
 #include <stdlib.h>
 #include <curses.h>
@@ -408,18 +414,155 @@ drawbanner (struct dg_banner *ban, unsigned int start_line, unsigned int howmany
     mvaddstr (start_line + i, 1, ban->lines[i]);
 }
 
+void
+shm_sem_wait(struct dg_shm *shm_dg_data)
+{
+#ifdef USE_SHMEM
+    if (sem_wait(&(shm_dg_data->dg_sem)) == -1) {
+       debug_write("sem_wait");
+       graceful_exit(77);
+    }
+#endif
+}
+
+void
+shm_sem_post(struct dg_shm *shm_dg_data)
+{
+#ifdef USE_SHMEM
+    if (sem_post(&(shm_dg_data->dg_sem)) == -1) {
+       debug_write("sem_post");
+       graceful_exit(78);
+    }
+#endif
+}
+
+void
+shm_update(struct dg_shm *shm_dg_data, struct dg_game **games, int len)
+{
+#ifdef USE_SHMEM
+    int di, i;
+    struct dg_shm_game *shm_dg_game = (struct dg_shm_game *)(shm_dg_data + sizeof(struct dg_shm));
+
+    shm_sem_wait(shm_dg_data);
+
+    for (di = 0; di < shm_dg_data->max_n_games; di++)
+       if (shm_dg_game[di].in_use) {
+           int delgame = 1;
+           for (i = 0; i < len; i++) {
+               if (!strcmp(games[i]->ttyrec_fn, shm_dg_game[di].ttyrec_fn)) {
+                   delgame = 0;
+                   games[i]->is_in_shm = 1;
+                   games[i]->shm_idx = di;
+                   games[i]->nwatchers = shm_dg_game[di].nwatchers;
+                   break;
+               }
+           }
+           if (delgame) {
+               shm_dg_game[di].in_use = 0;
+               if (shm_dg_data->cur_n_games > 0) shm_dg_data->cur_n_games--;
+           }
+       }
+
+    if (shm_dg_data->cur_n_games < shm_dg_data->max_n_games) {
+       for (i = 0; i < len; i++)
+           if (!games[i]->is_in_shm) {
+               for (di = 0; di < shm_dg_data->max_n_games; di++)
+                   if (!shm_dg_game[di].in_use) {
+                       shm_dg_game[di].in_use = 1;
+                       shm_dg_game[di].nwatchers = 0;
+                       games[i]->nwatchers = 0;
+                       games[i]->is_in_shm = 1;
+                       games[i]->shm_idx = di;
+                       shm_dg_data->cur_n_games++;
+                       strncpy(shm_dg_game[di].ttyrec_fn, games[i]->ttyrec_fn, 150);
+                       break;
+                   }
+           }
+    }
+
+    shm_sem_post(shm_dg_data);
+#endif
+}
+
+void
+shm_mk_keys(key_t *shm_key, key_t *shm_sem_key)
+{
+#ifdef USE_SHMEM
+    if ((*shm_key = ftok("dgamelaunch", 'R')) == -1) {
+       debug_write("ftok shm_key");
+       graceful_exit(71);
+    }
+    if ((*shm_sem_key = ftok("dgamelaunch", 'S')) == -1) {
+       debug_write("ftok shm_sem_key");
+       graceful_exit(72);
+    }
+#endif
+}
+
+void
+shm_init(struct dg_shm **shm_dg_data, struct dg_shm_game **shm_dg_game)
+{
+#ifdef USE_SHMEM
+  key_t shm_key;
+  key_t shm_sem_key;
+  int   shm_id;
+  int   shm_size;
+  void *shm_data = NULL;
+  int   shm_data_existed = 0;
+
+  shm_mk_keys(&shm_key, &shm_sem_key);
+
+  /* max. shm_n_games simultaneous games recorded in the shared memory */
+  shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
+
+  /* connect to (and possibly create) the segment */
+  if ((shm_id = shmget(shm_key, shm_size, 0644 | IPC_CREAT | IPC_EXCL)) == -1) {
+      /* creation failed, so it already exists. attach to it */
+      shm_data_existed = 1;
+      if ((shm_id = shmget(shm_key, shm_size, 0644)) == -1) {
+         debug_write("shmget");
+         graceful_exit(73);
+      }
+  }
+
+  /* attach to the segment to get a pointer to it: */
+  shm_data = shmat(shm_id, (void *)0, 0);
+  if (shm_data == (char *)(-1)) {
+      debug_write("shmat");
+      graceful_exit(74);
+  }
+  if (!shm_data) {
+      debug_write("shm_data == null");
+      graceful_exit(75);
+  }
+
+  (*shm_dg_data) = (struct dg_shm *)shm_data;
+  (*shm_dg_game) = (struct dg_shm_game *)(shm_data + sizeof(struct dg_shm));
+
+  if (!shm_data_existed && shm_data) {
+      memset(*shm_dg_game, 0, shm_n_games*sizeof(struct dg_shm_game));
+      (*shm_dg_data)->max_n_games = shm_n_games;
+      (*shm_dg_data)->cur_n_games = 0;
+      if (sem_init(&((*shm_dg_data)->dg_sem), 1,1) == -1) {
+         debug_write("sem_init");
+         graceful_exit(76);
+      }
+  }
+#endif /* USE_SHMEM */
+}
+
 void
 inprogressmenu (int gameid)
 {
     const char *selectorchars = "abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
   int i, menuchoice, len = 20, offset = 0;
   static dg_sortmode sortmode = NUM_SORTMODES;
-  time_t ctime;
   struct dg_game **games = NULL;
-  char ttyrecname[130], gametype[10];
+  char ttyrecname[130], gametype[10], idletime[10];
   int *is_nhext;
   sigset_t oldmask, toblock;
   int idx = -1;
+  int shm_idx = -1;
   int max_height = -1;
   int selected = -1;
 
@@ -438,6 +581,11 @@ inprogressmenu (int gameid)
 
   int require_enter = 0; /* TODO: make configurable */
 
+  int di;
+
+  struct dg_shm *shm_dg_data = NULL;
+  struct dg_shm_game *shm_dg_game = NULL;
+
   if (sortmode == NUM_SORTMODES)
       sortmode = globalconfig.sortmode;
 
@@ -449,7 +597,10 @@ inprogressmenu (int gameid)
       graceful_exit(70);
   }
 
+  shm_init(&shm_dg_data, &shm_dg_game);
+
   games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
+  shm_update(shm_dg_data, games, len);
   games = sort_games (games, len, sortmode);
 
   while (1)
@@ -492,10 +643,15 @@ inprogressmenu (int gameid)
          dgl_sortprintw(SORTMODE_WINDOWSIZE, 29, "Size")
          dgl_sortprintw(SORTMODE_STARTTIME,  37, "Start date & time")
          dgl_sortprintw(SORTMODE_IDLETIME,   58, "Idle time")
+#ifdef USE_SHMEM
+         dgl_sortprintw(SORTMODE_WATCHERS,   70, "Watchers")
+#endif
 
 #undef dgl_sortprintw
       }
 
+      shm_sem_wait(shm_dg_data);
+
       for (i = 0; i < max_height; i++)
         {
           if (i + offset >= len)
@@ -511,16 +667,40 @@ inprogressmenu (int gameid)
            snprintf (gametype, sizeof gametype, "%3dx%3d",
                games[i + offset]->ws_col, games[i + offset]->ws_row);
 
-          mvprintw (top_banner_hei + 1 + i, 1, "%c) %-15s  %-5s  %s  %s %s  %ldm %lds",
+#ifdef USE_SHMEM
+# define WATCH_LINE_FORMAT "%c) %-15s  %-5s  %s  %s %s  %-10s  %i"
+#else
+# define WATCH_LINE_FORMAT "%c) %-15s  %-5s  %s  %s %s  %-10s"
+#endif
+         {
+             time_t ctime;
+             (void) time(&ctime);
+             long secs = (ctime - games[i + offset]->idle_time) % 60;
+             long mins = (ctime - games[i + offset]->idle_time) / 60;
+             long hours= (ctime - games[i + offset]->idle_time) / (60*60);
+             if (hours)
+                 snprintf(idletime, 10, "%ldh %ldm", hours, mins);
+             else
+                 snprintf(idletime, 10, "%ldm %lds", mins, secs);
+         }
+
+          mvprintw (top_banner_hei + 1 + i, 1, WATCH_LINE_FORMAT,
                     selectorchars[i], games[i + offset]->name, myconfig[games[i + offset]->gamenum]->shortname, gametype,
                     games[i + offset]->date, games[i + offset]->time,
-                    (time (&ctime) - games[i + offset]->idle_time) / 60,
-                    (time (&ctime) - games[i + offset]->idle_time) % 60);
+                   idletime,
+#ifdef USE_SHMEM
+                   (games[i+offset]->is_in_shm ? shm_dg_game[games[i+offset]->shm_idx].nwatchers : -1)
+#else
+                   0
+#endif
+                   );
 
          if (i + offset == selected) attroff(selected_attr);
 
         }
 
+      shm_sem_post(shm_dg_data);
+
       btm = dgl_local_LINES-btm_banner_hei-top_banner_hei;
       if (btm > i) btm = i+1;
       if (len > 0) {
@@ -592,6 +772,9 @@ inprogressmenu (int gameid)
        case 'q': case 'Q':
            if (is_nhext) free(is_nhext);
            free_populated_games(games, len);
+#ifdef USE_SHMEM
+           shmdt(shm_dg_data);
+#endif
           return;
 
        case '.':
@@ -643,7 +826,16 @@ watchgame:
               clear ();
               refresh ();
               endwin ();
-
+#ifdef USE_SHMEM
+             if (games[idx]->is_in_shm) {
+                 shm_idx = games[idx]->shm_idx;
+                 shm_sem_wait(shm_dg_data);
+                 if (shm_dg_game[shm_idx].in_use &&
+                     !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn))
+                     shm_dg_game[shm_idx].nwatchers++;
+                 shm_sem_post(shm_dg_data);
+             }
+#endif
              resizey = games[idx]->ws_row;
              resizex = games[idx]->ws_col;
              if (loggedin)
@@ -655,6 +847,16 @@ watchgame:
                  setproctitle("%s", me->username);
              else
                  setproctitle("<Anonymous>");
+#ifdef USE_SHMEM
+             if (games[idx]->is_in_shm) {
+                 shm_sem_wait(shm_dg_data);
+                 if (shm_dg_game[shm_idx].in_use &&
+                     !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn) &&
+                     (shm_dg_game[shm_idx].nwatchers > 0))
+                     shm_dg_game[shm_idx].nwatchers--;
+                 shm_sem_post(shm_dg_data);
+             }
+#endif
               initcurses ();
             }
         }
@@ -662,6 +864,7 @@ watchgame:
       if (selected >= 0 && selected < len)
          selectedgame = strdup(games[selected]->name);
       games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
+      shm_update(shm_dg_data, games, len);
       games = sort_games (games, len, sortmode);
       if (selectedgame) {
          selected = -1;
@@ -676,6 +879,9 @@ watchgame:
     }
   if (is_nhext) free(is_nhext);
   free_populated_games(games, len);
+#ifdef USE_SHMEM
+  shmdt(shm_dg_data);
+#endif
 }
 
 /* ************************************************************* */
@@ -2061,7 +2267,7 @@ main (int argc, char** argv)
 
   __progname = basename(strdup(argv[0]));
 
-  while ((c = getopt(argc, argv, "qh:pf:aeW:")) != -1)
+  while ((c = getopt(argc, argv, "qh:pf:aeW:S")) != -1)
   {
     switch (c)
     {
@@ -2089,6 +2295,26 @@ main (int argc, char** argv)
        wall_email_str = strdup(optarg);
        break;
 
+    case 'S': /* Free the shared memory block */
+       {
+#ifdef USE_SHMEM
+           key_t shm, sem;
+           int   shm_id;
+           int shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
+           shm_mk_keys(&shm, &sem);
+           if ((shm_id = shmget(shm, shm_size, 0644)) != -1) {
+               shmctl(shm_id, IPC_RMID, NULL);
+               if (!silent) fprintf(stderr, "shmem block freed.\n");
+           } else {
+               if (!silent) fprintf(stderr, "nonexistent shmem block.\n");
+           }
+#else
+           if (!silent) fprintf(stderr, "warning: dgamelaunch was compiled without shmem.\n");
+#endif
+           graceful_exit(0);
+       }
+       break;
+
       default:
        break; /*ignore */
     }
index b78cd42464ca2d9a02bacde15078984c0131fe6a..cfd56f6908648145c1d7d8357066b709e8067649 100644 (file)
@@ -9,6 +9,10 @@
 #include <sys/stat.h>
 #include <time.h>
 
+#ifdef USE_SHMEM
+#include <semaphore.h>
+#endif
+
 #ifndef ARRAY_SIZE
 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 #endif
@@ -77,6 +81,22 @@ struct dg_menulist
     struct dg_menulist *next;
 };
 
+struct dg_shm
+{
+#ifdef USE_SHMEM
+    sem_t dg_sem;
+#endif
+    int max_n_games;
+    int cur_n_games;
+};
+
+struct dg_shm_game
+{
+    int  in_use;
+    int  nwatchers;
+    char ttyrec_fn[150];
+};
+
 struct dg_game
 {
   char *ttyrec_fn;
@@ -86,6 +106,9 @@ struct dg_game
   time_t idle_time;
   int ws_row, ws_col; /* Window size */
   int gamenum;
+  int is_in_shm;
+  int shm_idx;
+  int nwatchers;
 };
 
 struct dg_config
@@ -157,6 +180,9 @@ typedef enum
     SORTMODE_WINDOWSIZE,
     SORTMODE_STARTTIME,
     SORTMODE_IDLETIME,
+#ifdef USE_SHMEM
+    SORTMODE_WATCHERS,
+#endif
     NUM_SORTMODES
 } dg_sortmode;
 
@@ -166,11 +192,16 @@ static const char *SORTMODE_NAME[NUM_SORTMODES] = {
     "Game",
     "Windowsize",
     "Starttime",
-    "Idletime"
+    "Idletime",
+#ifdef USE_SHMEM
+    "Watchers",
+#endif
 };
 
 
 /* Global variables */
+extern int shm_n_games; /* TODO: make configurable */
+
 extern char* config; /* file path */
 extern struct dg_config **myconfig;
 extern char *chosen_name;
index dd45c154ad7abf4a32a26fc2a6f89b22d6008123..c572928fc919cf25aa0d7caf37a69cab5d29b4da 100644 (file)
@@ -55,6 +55,8 @@ int loggedin = 0;
 char *chosen_name;
 int num_games = 0;
 
+int shm_n_games = 200;
+
 int dgl_local_COLS = -1, dgl_local_LINES = -1;
 int curses_resize = 0;
 
@@ -432,6 +434,19 @@ sort_game_starttime(const void *g1, const void *g2)
     return i;
 }
 
+static int
+sort_game_watchers(const void *g1, const void *g2)
+{
+    const struct dg_game *game1 = *(const struct dg_game **)g1;
+    const struct dg_game *game2 = *(const struct dg_game **)g2;
+    int i = dglsign(game1->nwatchers - game2->nwatchers);
+    if (!i)
+       i = strcmp(game1->time, game2->time);
+    if (!i)
+       return strcasecmp(game1->name, game2->name);
+    return i;
+}
+
 struct dg_game **
 sort_games (struct dg_game **games, int len, dg_sortmode sortmode)
 {
@@ -441,6 +456,9 @@ sort_games (struct dg_game **games, int len, dg_sortmode sortmode)
     case SORTMODE_WINDOWSIZE: qsort(games, len, sizeof(struct dg_game *), sort_game_windowsize); break;
     case SORTMODE_IDLETIME: qsort(games, len, sizeof(struct dg_game *), sort_game_idletime); break;
     case SORTMODE_STARTTIME: qsort(games, len, sizeof(struct dg_game *), sort_game_starttime); break;
+#ifdef USE_SHMEM
+    case SORTMODE_WATCHERS: qsort(games, len, sizeof(struct dg_game *), sort_game_watchers); break;
+#endif
     default: ;
     }
     return games;
@@ -573,6 +591,9 @@ populate_games (int xgame, int *l, struct dg_user *me)
               games[len]->idle_time = pstat.st_mtime;
 
              games[len]->gamenum = game;
+             games[len]->is_in_shm = 0;
+             games[len]->nwatchers = 0;
+             games[len]->shm_idx = -1;
 
              n = read(fd, pidws, sizeof(pidws) - 1);
              if (n > 0)