Show the number of people watching each game in the watching-menu.

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
This commit is contained in:
Pasi Kallinen 2010-05-01 14:41:54 +00:00
parent 6245e81b59
commit 113fc564ef
5 changed files with 299 additions and 12 deletions

4
TODO
View File

@ -9,8 +9,6 @@
will no longer work, because the cursor won't be moved to the previous will no longer work, because the cursor won't be moved to the previous
line again 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 -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, 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 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 -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? is playing on the account and someone else logins and we start playing?
-allow users to run recover themselves -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, -configurable stuff: allowed chars in usernames,
allow char stripping, ... allow char stripping, ...

View File

@ -144,6 +144,19 @@ if test "$enable_rlimit" = yes; then
fi 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, AC_ARG_WITH(config-file,

View File

@ -53,6 +53,12 @@
#ifdef USE_RLIMIT #ifdef USE_RLIMIT
#include <sys/resource.h> #include <sys/resource.h>
#endif #endif
#ifdef USE_SHMEM
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include <libgen.h> #include <libgen.h>
#include <stdlib.h> #include <stdlib.h>
#include <curses.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]); 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 void
inprogressmenu (int gameid) inprogressmenu (int gameid)
{ {
const char *selectorchars = "abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ"; const char *selectorchars = "abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
int i, menuchoice, len = 20, offset = 0; int i, menuchoice, len = 20, offset = 0;
static dg_sortmode sortmode = NUM_SORTMODES; static dg_sortmode sortmode = NUM_SORTMODES;
time_t ctime;
struct dg_game **games = NULL; struct dg_game **games = NULL;
char ttyrecname[130], gametype[10]; char ttyrecname[130], gametype[10], idletime[10];
int *is_nhext; int *is_nhext;
sigset_t oldmask, toblock; sigset_t oldmask, toblock;
int idx = -1; int idx = -1;
int shm_idx = -1;
int max_height = -1; int max_height = -1;
int selected = -1; int selected = -1;
@ -438,6 +581,11 @@ inprogressmenu (int gameid)
int require_enter = 0; /* TODO: make configurable */ 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) if (sortmode == NUM_SORTMODES)
sortmode = globalconfig.sortmode; sortmode = globalconfig.sortmode;
@ -449,7 +597,10 @@ inprogressmenu (int gameid)
graceful_exit(70); graceful_exit(70);
} }
shm_init(&shm_dg_data, &shm_dg_game);
games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */ 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); games = sort_games (games, len, sortmode);
while (1) while (1)
@ -492,10 +643,15 @@ inprogressmenu (int gameid)
dgl_sortprintw(SORTMODE_WINDOWSIZE, 29, "Size") dgl_sortprintw(SORTMODE_WINDOWSIZE, 29, "Size")
dgl_sortprintw(SORTMODE_STARTTIME, 37, "Start date & time") dgl_sortprintw(SORTMODE_STARTTIME, 37, "Start date & time")
dgl_sortprintw(SORTMODE_IDLETIME, 58, "Idle time") dgl_sortprintw(SORTMODE_IDLETIME, 58, "Idle time")
#ifdef USE_SHMEM
dgl_sortprintw(SORTMODE_WATCHERS, 70, "Watchers")
#endif
#undef dgl_sortprintw #undef dgl_sortprintw
} }
shm_sem_wait(shm_dg_data);
for (i = 0; i < max_height; i++) for (i = 0; i < max_height; i++)
{ {
if (i + offset >= len) if (i + offset >= len)
@ -511,16 +667,40 @@ inprogressmenu (int gameid)
snprintf (gametype, sizeof gametype, "%3dx%3d", snprintf (gametype, sizeof gametype, "%3dx%3d",
games[i + offset]->ws_col, games[i + offset]->ws_row); 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, selectorchars[i], games[i + offset]->name, myconfig[games[i + offset]->gamenum]->shortname, gametype,
games[i + offset]->date, games[i + offset]->time, games[i + offset]->date, games[i + offset]->time,
(time (&ctime) - games[i + offset]->idle_time) / 60, idletime,
(time (&ctime) - games[i + offset]->idle_time) % 60); #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); if (i + offset == selected) attroff(selected_attr);
} }
shm_sem_post(shm_dg_data);
btm = dgl_local_LINES-btm_banner_hei-top_banner_hei; btm = dgl_local_LINES-btm_banner_hei-top_banner_hei;
if (btm > i) btm = i+1; if (btm > i) btm = i+1;
if (len > 0) { if (len > 0) {
@ -592,6 +772,9 @@ inprogressmenu (int gameid)
case 'q': case 'Q': case 'q': case 'Q':
if (is_nhext) free(is_nhext); if (is_nhext) free(is_nhext);
free_populated_games(games, len); free_populated_games(games, len);
#ifdef USE_SHMEM
shmdt(shm_dg_data);
#endif
return; return;
case '.': case '.':
@ -643,7 +826,16 @@ watchgame:
clear (); clear ();
refresh (); refresh ();
endwin (); 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; resizey = games[idx]->ws_row;
resizex = games[idx]->ws_col; resizex = games[idx]->ws_col;
if (loggedin) if (loggedin)
@ -655,6 +847,16 @@ watchgame:
setproctitle("%s", me->username); setproctitle("%s", me->username);
else else
setproctitle("<Anonymous>"); 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 (); initcurses ();
} }
} }
@ -662,6 +864,7 @@ watchgame:
if (selected >= 0 && selected < len) if (selected >= 0 && selected < len)
selectedgame = strdup(games[selected]->name); selectedgame = strdup(games[selected]->name);
games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */ 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); games = sort_games (games, len, sortmode);
if (selectedgame) { if (selectedgame) {
selected = -1; selected = -1;
@ -676,6 +879,9 @@ watchgame:
} }
if (is_nhext) free(is_nhext); if (is_nhext) free(is_nhext);
free_populated_games(games, len); 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])); __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) switch (c)
{ {
@ -2089,6 +2295,26 @@ main (int argc, char** argv)
wall_email_str = strdup(optarg); wall_email_str = strdup(optarg);
break; 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: default:
break; /*ignore */ break; /*ignore */
} }

View File

@ -9,6 +9,10 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <time.h> #include <time.h>
#ifdef USE_SHMEM
#include <semaphore.h>
#endif
#ifndef ARRAY_SIZE #ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif #endif
@ -77,6 +81,22 @@ struct dg_menulist
struct dg_menulist *next; 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 struct dg_game
{ {
char *ttyrec_fn; char *ttyrec_fn;
@ -86,6 +106,9 @@ struct dg_game
time_t idle_time; time_t idle_time;
int ws_row, ws_col; /* Window size */ int ws_row, ws_col; /* Window size */
int gamenum; int gamenum;
int is_in_shm;
int shm_idx;
int nwatchers;
}; };
struct dg_config struct dg_config
@ -157,6 +180,9 @@ typedef enum
SORTMODE_WINDOWSIZE, SORTMODE_WINDOWSIZE,
SORTMODE_STARTTIME, SORTMODE_STARTTIME,
SORTMODE_IDLETIME, SORTMODE_IDLETIME,
#ifdef USE_SHMEM
SORTMODE_WATCHERS,
#endif
NUM_SORTMODES NUM_SORTMODES
} dg_sortmode; } dg_sortmode;
@ -166,11 +192,16 @@ static const char *SORTMODE_NAME[NUM_SORTMODES] = {
"Game", "Game",
"Windowsize", "Windowsize",
"Starttime", "Starttime",
"Idletime" "Idletime",
#ifdef USE_SHMEM
"Watchers",
#endif
}; };
/* Global variables */ /* Global variables */
extern int shm_n_games; /* TODO: make configurable */
extern char* config; /* file path */ extern char* config; /* file path */
extern struct dg_config **myconfig; extern struct dg_config **myconfig;
extern char *chosen_name; extern char *chosen_name;

View File

@ -55,6 +55,8 @@ int loggedin = 0;
char *chosen_name; char *chosen_name;
int num_games = 0; int num_games = 0;
int shm_n_games = 200;
int dgl_local_COLS = -1, dgl_local_LINES = -1; int dgl_local_COLS = -1, dgl_local_LINES = -1;
int curses_resize = 0; int curses_resize = 0;
@ -432,6 +434,19 @@ sort_game_starttime(const void *g1, const void *g2)
return i; 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 ** struct dg_game **
sort_games (struct dg_game **games, int len, dg_sortmode sortmode) 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_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_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; 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: ; default: ;
} }
return games; 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]->idle_time = pstat.st_mtime;
games[len]->gamenum = game; 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); n = read(fd, pidws, sizeof(pidws) - 1);
if (n > 0) if (n > 0)