"short_name" { return TYPE_GAME_SHORT_NAME; }
"game_id" { return TYPE_GAME_ID; }
"game_path" { return TYPE_PATH_GAME; }
+"watch_path" { return TYPE_PATH_WATCH; }
"dglroot" { return TYPE_PATH_DGLDIR; }
"spooldir" { return TYPE_PATH_SPOOL; }
"banner" { return TYPE_PATH_BANNER; }
"lockfile" { return TYPE_PATH_LOCKFILE; }
"inprogressdir" { return TYPE_PATH_INPROGRESS; }
"game_args" { return TYPE_GAME_ARGS; }
+"watch_args" { return TYPE_WATCH_ARGS; }
extra_info_file { return TYPE_EXTRA_INFO_FILE; }
"max_idle_time" { return TYPE_MAX_IDLE_TIME; }
"rc_fmt" { return TYPE_RC_FMT; }
watch_columns { return TYPE_WATCH_COLUMNS; }
commands { return TYPE_CMDQUEUE; }
postcommands { return TYPE_POSTCMDQUEUE; }
+watchcommands { return TYPE_WATCHCMDQUEUE; }
encoding { return TYPE_ENCODING; }
locale { return TYPE_LOCALE; }
default_term { return TYPE_DEFTERM; }
%token <i> TYPE_DGLCMD0 TYPE_DGLCMD1 TYPE_DGLCMD2
%token TYPE_DEFINE_GAME
%token <i> TYPE_BOOL
+%token TYPE_PATH_WATCH TYPE_WATCH_ARGS TYPE_WATCHCMDQUEUE
%%
myconfig[ncnf]->postcmdqueue = curr_cmdqueue;
curr_cmdqueue = NULL;
}
+ | TYPE_WATCHCMDQUEUE
+ {
+ if (myconfig[ncnf]->watchcmdqueue) {
+ fprintf(stderr, "%s:%d: watchcommand queue defined twice, bailing out\n",
+ config, line);
+ exit(1);
+ }
+ }
+ '=' cmdlist
+ {
+ myconfig[ncnf]->watchcmdqueue = curr_cmdqueue;
+ curr_cmdqueue = NULL;
+ }
| TYPE_GAME_ARGS '=' game_args_list
{
/* nothing */
}
+ | TYPE_WATCH_ARGS '=' watch_args_list
+ {
+ /* nothing */
+ }
| TYPE_EXTRA_INFO_FILE '=' TYPE_VALUE
{
myconfig[ncnf]->extra_info_file = strdup($3);
myconfig[ncnf]->game_path = strdup ($3);
break;
+ case TYPE_PATH_WATCH:
+ if (myconfig[ncnf]->watch_path) free(myconfig[ncnf]->watch_path);
+ myconfig[ncnf]->watch_path = strdup ($3);
+ break;
+
case TYPE_NAME_GAME:
if (myconfig[ncnf]->game_name) free (myconfig[ncnf]->game_name);
myconfig[ncnf]->game_name = strdup($3);
}
;
+watch_arg : TYPE_VALUE
+ {
+ char **tmpargs;
+ if (myconfig[ncnf]->watch_args) {
+ myconfig[ncnf]->num_wargs++;
+ tmpargs = calloc((myconfig[ncnf]->num_wargs+1), sizeof(char *));
+ memcpy(tmpargs, myconfig[ncnf]->watch_args, (myconfig[ncnf]->num_wargs * sizeof(char *)));
+ free(myconfig[ncnf]->watch_args);
+ myconfig[ncnf]->watch_args = tmpargs;
+ } else {
+ myconfig[ncnf]->num_wargs = 1;
+ myconfig[ncnf]->watch_args = calloc(2, sizeof(char *));
+ }
+ myconfig[ncnf]->watch_args[(myconfig[ncnf]->num_wargs)-1] = strdup($1);
+ myconfig[ncnf]->watch_args[(myconfig[ncnf]->num_wargs)] = 0;
+ }
+ ;
+
game_args_list : game_arg
| game_arg ',' game_args_list
;
+watch_args_list : watch_arg
+ | watch_arg ',' watch_args_list
+ ;
+
game_definitions : game_definition
| game_definition game_definitions
;
| TYPE_PATH_CHROOT { $$ = TYPE_PATH_CHROOT; }
| TYPE_ALLOW_REGISTRATION { $$ = TYPE_ALLOW_REGISTRATION; }
| TYPE_PATH_GAME { $$ = TYPE_PATH_GAME; }
+ | TYPE_PATH_WATCH { $$ = TYPE_PATH_WATCH; }
| TYPE_NAME_GAME { $$ = TYPE_NAME_GAME; }
| TYPE_GAME_SHORT_NAME { $$ = TYPE_GAME_SHORT_NAME; }
| TYPE_GAME_ID { $$ = TYPE_GAME_ID; }
case TYPE_MENU_MAX_IDLE_TIME: return "menu_max_idle_time";
case TYPE_PATH_CHROOT: return "chroot_path";
case TYPE_PATH_GAME: return "game_path";
+ case TYPE_PATH_WATCH: return "watch_path";
case TYPE_NAME_GAME: return "game_name";
case TYPE_ALLOW_REGISTRATION: return "allow_new_nicks";
case TYPE_GAME_SHORT_NAME: return "short_name";
case TYPE_PATH_TTYREC: return "ttyrecdir";
case TYPE_PATH_INPROGRESS: return "inprogressdir";
case TYPE_GAME_ARGS: return "game_args";
+ case TYPE_WATCH_ARGS: return "watch_args";
case TYPE_MAX_IDLE_TIME: return "max_idle_time";
case TYPE_RC_FMT: return "rc_fmt";
case TYPE_WATCH_SORTMODE: return "sortmode";
break;
case SORTMODE_WINDOWSIZE:
+ if (myconfig[game->gamenum]->watch_path) {
+ snprintf(data, bufsz, "N/A");
+ *hilite = CLR_GREEN;
+ break;
+ }
snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row);
if (showplayers)
snprintf(data, bufsz, "%dx%d", game->ws_col, game->ws_row);
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, me);
shm_update(shm_dg_data, games, len);
games = sort_games (games, len, sortmode);
setproctitle("%s [watching %s]", me->username, chosen_name);
else
setproctitle("<Anonymous> [watching %s]", chosen_name);
- ttyplay_main (ttyrecname, 1, resizex, resizey);
+ if (myconfig[games[idx]->gamenum]->watch_path) {
+ pid_t child;
+ int gnum = games[idx]->gamenum;
+ char **wargs = (char **)malloc(sizeof(char *) * (myconfig[gnum]->num_wargs+1));
+ dgl_exec_cmdqueue_w(myconfig[gnum]->watchcmdqueue, gnum, me, chosen_name);
+ /* fix the variables in the arguments */
+ for (i = 0; i < myconfig[gnum]->num_wargs; i++) {
+ wargs[i] = strdup(dgl_format_str(gnum, me, myconfig[gnum]->watch_args[i], chosen_name));
+ }
+ wargs[myconfig[gnum]->num_wargs] = NULL;
+ /* tidy up signals before launching external process */
+ signal(SIGWINCH, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ idle_alarm_set_enabled(0);
+ /* launch program */
+ child = fork();
+ if (child < 0) {
+ perror ("fork");
+ fail ();
+ }
+ if (child == 0) {
+ execvp (myconfig[gnum]->watch_path, wargs);
+ } else {
+ int status;
+ (void) wait(&status);
+ }
+ /* reset signals on return */
+ idle_alarm_set_enabled(1);
+ signal(SIGHUP, catch_sighup);
+ signal(SIGINT, catch_sighup);
+ signal(SIGQUIT, catch_sighup);
+ signal(SIGTERM, catch_sighup);
+ signal(SIGWINCH, sigwinch_func);
+ /* free temporary watch args */
+ for (i = 0; i < myconfig[gnum]->num_wargs; i++) {
+ free(wargs[i]);
+ }
+ free(wargs);
+ } else {
+ ttyplay_main (ttyrecname, 1, resizex, resizey);
+ }
if (loggedin)
setproctitle("%s", me->username);
else
if (selected >= 0 && selected < len)
selectedgame = strdup(games[selected]->name);
- games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
+ games = populate_games (gameid, &len, me);
shm_update(shm_dg_data, games, len);
games = sort_games (games, len, sortmode);
if (selectedgame) {
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, me);
shm_update(shm_dg_data, games, len);
games = sort_games (games, len, sortmode);
#ifdef USE_NCURSES_COLOR
# define CLR_NORMAL COLOR_PAIR(11) | A_NORMAL
# define CLR_RED COLOR_PAIR(COLOR_RED) | A_NORMAL
+# define CLR_GREEN COLOR_PAIR(COLOR_GREEN) | A_NORMAL
#else
# define CLR_NORMAL 0
# define CLR_RED 0
+# define CLR_GREEN 0
#endif
extern int color_remap[];
struct dg_config
{
char* game_path;
+ char* watch_path;
char* game_name;
char* game_id;
char* shortname;
char* spool;
char* inprogressdir;
int num_args; /* # of bin_args */
+ int num_wargs; /* # of watch_args */
char **bin_args; /* args for game binary */
+ char **watch_args; /* args for watch binary */
char *rc_fmt;
struct dg_cmdpart *cmdqueue;
struct dg_cmdpart *postcmdqueue;
+ struct dg_cmdpart *watchcmdqueue;
int max_idle_time;
char *extra_info_file;
int encoding; // -1 = run --print-charset
extern struct dg_menu *dgl_find_menu(char *menuname);
extern int dgl_exec_cmdqueue(struct dg_cmdpart *queue, int game, struct dg_user *me);
+extern int dgl_exec_cmdqueue_w(struct dg_cmdpart *queue, int game, struct dg_user *me, char *playername);
extern void free_populated_games(struct dg_game **games, int len);
extern struct dg_game **populate_games(int game, int *l, struct dg_user *me);
struct dg_config **myconfig = NULL;
struct dg_config defconfig = {
/* game_path = */ "/bin/nethack",
+ /* watch_path = */ NULL,
/* game_name = */ "NetHack",
/* game_id = */ NULL,
/* shortname = */ "NH",
/* spool = */ "/var/mail/",
/* inprogressdir = */ "%rinprogress/",
/* num_args = */ 0,
+ /* num_wargs = */ 0,
/* bin_args = */ NULL,
+ /* watch_args = */ NULL,
/* rc_fmt = */ "%rrcfiles/%n.nethackrc", /* [dglroot]rcfiles/[username].nethackrc */
/* cmdqueue = */ NULL,
/* postcmdqueue = */ NULL,
+ /* watchcmdqueue = */ NULL,
/* max_idle_time = */ 0,
/* extra_info_file = */ NULL,
/* encoding */ 0
/*
* replace following codes with variables:
* %u == shed_uid (number)
+ * %l == logged-in user (string; from 'me'. Empty string if 'me' is null)
* %n == user name (string; gotten from 'me', or from 'plrname' if 'me' is null)
* %r == chroot (string) (aka "dglroot" config var)
* %g == game name
* %s == short game name
* %t == ttyrec file (full path&name) of the last game played.
+ * %w == 'watched' player (string; from 'plrname', or 'me' if 'plrname' is null)
+ * %N, %W, %L (char; first character of their lowercase counterparts)
*/
char *
dgl_format_str(int game, struct dg_user *me, char *str, char *plrname)
f = str;
p = buf;
+ *p = '\0';
end = buf + sizeof(buf) - 10;
while (*f) {
if (ispercent) {
switch (*f) {
- case 'u':
+ case 'u':
snprintf (p, end + 1 - p, "%d", globalconfig.shed_uid);
while (*p != '\0')
p++;
break;
+ case 'L':
+ if (me) {
+ *p = me->username[0];
+ p++;
+ }
+ *p = '\0';
+ break;
+ case 'l':
+ if (me) snprintf (p, end + 1 - p, "%s", me->username);
+ while (*p != '\0')
+ p++;
+ break;
case 'N':
if (me) *p = me->username[0];
else if (plrname) *p = plrname[0];
p++;
*p = '\0';
break;
- case 'n':
+ case 'n':
if (me) snprintf (p, end + 1 - p, "%s", me->username);
else if (plrname) snprintf(p, end + 1 - p, "%s", plrname);
else return NULL;
while (*p != '\0')
p++;
break;
- case 'g':
+ case 'g':
if (game >= 0 && game < num_games && myconfig[game]) snprintf (p, end + 1 - p, "%s", myconfig[game]->game_name);
else return NULL;
while (*p != '\0')
while (*p != '\0')
p++;
break;
+ case 'W':
+ if (plrname) *p = plrname[0];
+ else if (me) *p = me->username[0];
+ else return NULL;
+ p++;
+ *p = '\0';
+ break;
+ case 'w':
+ if (plrname) snprintf(p, end + 1 - p, "%s", plrname);
+ else if (me) snprintf (p, end + 1 - p, "%s", me->username);
+ else return NULL;
+ while (*p != '\0')
+ p++;
+ break;
default:
*p = *f;
if (p < end)
}
int
-dgl_exec_cmdqueue(struct dg_cmdpart *queue, int game, struct dg_user *me)
+dgl_exec_cmdqueue_w(struct dg_cmdpart *queue, int game, struct dg_user *me, char *playername)
{
int i;
struct dg_cmdpart *tmp = queue;
return_from_submenu = 0;
while (tmp && !return_from_submenu) {
- if (tmp->param1) strcpy(p1, dgl_format_str(game, me, tmp->param1, NULL));
- if (tmp->param2) strcpy(p2, dgl_format_str(game, me, tmp->param2, NULL));
+ if (tmp->param1) strcpy(p1, dgl_format_str(game, me, tmp->param1, playername));
+ if (tmp->param2) strcpy(p2, dgl_format_str(game, me, tmp->param2, playername));
switch (tmp->cmd) {
default: break;
}
+int
+dgl_exec_cmdqueue(struct dg_cmdpart *queue, int game, struct dg_user *me)
+{
+ return dgl_exec_cmdqueue_w(queue, game, me, NULL);
+}
static int
sort_game_username(const void *g1, const void *g2)
}
replacestr++;
- ttrecdir = dgl_format_str(game, me, myconfig[game]->ttyrecdir, playername);
+ ttrecdir = dgl_format_str(game, NULL, myconfig[game]->ttyrecdir, playername);
if (!ttrecdir) continue;
snprintf (ttyrecname, 130, "%s%s", ttrecdir, replacestr);
# defined above.
# Parameters to the commands are subject to variable substitution:
# %r = dglroot, as defined above
-# %n = user nick, if user is logged in
-# %N = first character of user name, if user is logged in
+# %n = user nick, if user is logged in, or user being watched.
+# %l = user nick, if logged in, otherwise empty string.
+# %w = user selected from the watch menu (or logged in user if no user selected)
+# %N, %L, %W = first character of the above
# %u = shed_uid, as defined above, but numeric
# %g = game name, if user has selected a game.
# %s = short game name, if user has selected a game.
# encoding = "unicode"
#}
+#
+# fiqhack example - showing watch mode options
+#
+#
+
+#DEFINE {
+# game_path = "/fiqhackdir/fiqhack"
+# game_name = "FIQHack 4.3.0"
+# short_name = "fiqhack"
+#
+# game_args = "/fiqhackdir/fiqhack",
+# "-H", "/fiqhackdir/data",
+# "-U", "%ruserdata/%N/%n/fiqhack"
+#
+# spooldir = "/mail/"
+# #rc_template = "/dgl-default-rcfile.gh"
+#
+# #rc_fmt = "%ruserdata/%N/%n/%n.ghrc"
+#
+# inprogressdir = "%rinprogress-fh/"
+# extra_info_file = "%rextrainfo-fh/%n.extrainfo"
+#
+# # The place where ttyrecs are stored for this game.
+# # If this is not defined, ttyrecs are not saved for this game.
+# # ttyrecs are not necessary for spectating if watch_path is configured.
+# ttyrecdir = "%ruserdata/%N/%n/ttyrec/"
+#
+#
+# commands = setenv "NH4SERVERUSER" "%n",
+# setenv "NHMAILBOX" "/mail/%n",
+# setenv "SIMPLEMAIL" "1",
+# unlink "/mail/%n"
+#
+# # The executable to use for watching instead of the built-in ttyrec player
+# watch_path = "/fiqhackdir/fiqhack"
+#
+# # The args to call it with. As with game_args, argv[0] needs to be included
+# watch_args = "/fiqhackdir/fiqhack",
+# "-H", "/fiqhackdir/data",
+# # we use %n here so anonymous viewers can inherit the config
+# # of the player. The watch program needs to ensure the watcher
+# # can't modify anything in here that would affect the player.
+# "-U", "%ruserdata/%N/%n/fiqhack",
+# # %w is the player to watch
+# "-W", "%ruserdata/%W/%w/fiqhack"
+#
+# # these are run before watching commences.
+# # Use %l so the watching program knows if the watcher is logged in.
+# # The watching program should disable mail for anonymous watchers.
+# watchcommands = setenv "NH4SERVERUSER" "%w",
+# setenv "NH4WATCHER" "%l",
+# setenv "NHMAILBOX" "/mail/%w"
+#}
#
# the second game