Crossfire Client, Branches  R11627
script.c
Go to the documentation of this file.
1 const char * const rcsid_common_script_c =
2  "$Id: script.c 9364 2008-06-22 21:53:03Z quinet $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2003 Mark Wedel & Crossfire Development Team
7  This source file also Copyright (C) 2003 Preston Crow
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 
23  The author can be reached via e-mail to crossfire-devel@real-time.com
24 */
25 
26 /* This file has its own script.h for prototypes, so don't want to include
27  * this when doing a 'make proto'
28  */
29 #ifndef CPROTO
30 
31 
32 /*
33  * This file handles the client-side scripting interface.
34  *
35  * Each script is an external process that keeps two pipes open between the
36  * client and the script (one in each direction). When the script starts,
37  * it defaults to receiving no data from the client. Normally, the first
38  * command it sends to the client will be a request to have certain types
39  * of data sent to the script as the client receives them from the server
40  * (such as drawinfo commands). The script can also request current
41  * information from the client, such as the contents of the inventory or
42  * the map data (either live or last viewed "fog-of-war" data). The script
43  * can also send commands for the client to pass to the server.
44  *
45  * Script Commands:
46  *
47  * watch <command type>
48  * whenever the server sends the given command type to the client, also send
49  * a copy to the script.
50  * Note that this checked before the client processes the command, so it will
51  * automatically handle new options that may be added in the future.
52  * If the command type is NULL, all commands are watched.
53  *
54  * unwatch <command type>
55  * turn off a previous watch command. There may be a slight delay in
56  * response before the command is processed, so some unwanted data may
57  * still be sent to the script.
58  *
59  * request <data type>
60  * have the client send the given data to the script.
61  *
62  * issue [<repeat> <must_send>] <command>
63  * issue the specified command to the server.
64  * if <repeat> isn't numeric then the command is sent directly
65  * For "lock" and "mark" only, the parameters are converted to binary.
66  *
67  * draw <color> <text>
68  * display the text in the specified color as if the server had sent
69  * a drawinfo command.
70  *
71  * monitor
72  * send the script a copy of every command that is sent to the server.
73  *
74  * unmonitor
75  * turn off monitoring.
76  *
77  * sync <#>
78  * wait until the server has acknowledged all but <#> commands have been received
79  *
80  *
81  * To implement this:
82  *
83  * Processing script commands: gtk/gx11.c:do_network() and
84  * x11/x11.c:event_loop() are modified to also watch for input from scripts
85  * in the select() call, in which case script_process(fd) in this file is
86  * called.
87  *
88  * Handling watches: common/client.c:DoClient() is modified to pass a copy
89  * of each command to script_watch() before checking for it in the table.
90  *
91  * Handling of monitor: common/player.c:send_command() is modified to pass
92  * a copy of each command to script_monitor() before sending to the server.
93  *
94  * Handling of requests: global variables are directly accessed from within
95  * this file.
96  *
97  * Handling of issues: send_command() is called directly. Note that this
98  * command will be sent to any scripts that are monitoring output.
99  *
100  * Launching new scripts: common/player.c:extended_command() is extended to
101  * add a check for "script <scriptname>" as an additional command, calling
102  * script_init(). Also added is the "scripts" command to list all running
103  * scripts, the "scriptkill" command to terminate a script (close the pipes
104  * and assume it takes the hint), and the "scripttell" command to send a
105  * message to a running script.
106  */
107 
108 /*
109  * Include files
110  */
111 
112 /*
113 This does not work under Windows for now.
114 Someday this will be fixed :)
115 */
116 
117 #ifndef WIN32
118 #include <errno.h>
119 #include <sys/types.h>
120 #include <sys/socket.h>
121 #include <sys/wait.h>
122 #endif
123 #include <ctype.h>
124 
125 #include <client.h>
126 #include <external.h>
127 #include <script.h>
128 #include <p_cmd.h>
129 #include "mapdata.h"
130 
131 /*
132  * Data structures
133  */
134 struct script {
135  char *name; /* the script name */
136  char *params; /* the script parameters, if any */
137 #ifndef WIN32
138  int out_fd; /* the file descriptor to which the client writes to the script */
139  int in_fd; /* the file descriptor from which we read commands from the script */
140 #else
141  HANDLE out_fd; /* the file descriptor to which the client writes to the script */
142  HANDLE in_fd; /* the file descriptor from which we read commands from the script */
143 #endif /* WIN32 */
144  int monitor; /* true if this script is monitoring commands sent to the server */
145  int num_watch; /* number of commands we're watching */
146  char **watch; /* array of commands that we're watching */
147  int cmd_count; /* bytes already read in */
148  char cmd[1024]; /* command from the script */
149 #ifndef WIN32
150  int pid;
151 #else
152  DWORD pid; /* Handle to Win32 process ID */
153  HANDLE process; /* Handle of Win32 process */
154 #endif
156 };
157 
158 /*
159  * Global variables
160  */
161 static struct script *scripts = NULL;
162 static int num_scripts = 0;
163 
164 /*
165  * Prototypes
166  */
167 static int script_by_name(const char *name);
168 static void script_dead(int i);
169 static void script_process_cmd(int i);
170 static void send_map(int i,int x,int y);
171 static void script_send_item(int i, const char *head, const item *it);
172 
173 
174 /*
175  * Functions
176  */
177 
178 #ifdef WIN32
179 
180 #define write(x,y,z) emulate_write(x,y,z)
181 #define read(x,y,z) emulate_read(x,y,z)
182 
183 static int emulate_read(HANDLE fd, char *buf, int len)
184 {
185  DWORD dwBytesRead;
186  BOOL rc;
187 
188  FlushFileBuffers(fd);
189  rc = ReadFile(fd, buf, len, &dwBytesRead, NULL);
190  if (rc == FALSE)
191  return(-1);
192  buf[dwBytesRead] = '\0';
193 
194  return(dwBytesRead);
195 }
196 
197 static int emulate_write(HANDLE fd, const char *buf, int len)
198 {
199  DWORD dwBytesWritten;
200  BOOL rc;
201 
202  rc = WriteFile(fd, buf, len, &dwBytesWritten, NULL);
203  FlushFileBuffers(fd);
204  if (rc == FALSE)
205  return(-1);
206 
207  return(dwBytesWritten);
208 }
209 
210 
211 #endif /* WIN32 */
212 
213 void script_init(const char *cparams)
214 {
215 #ifndef WIN32
216  int pipe1[2];
217 #ifdef USE_PIPE
218  int pipe2[2];
219 #endif
220  int pid;
221  char *name, *args, params[MAX_BUF];
222 
223  if ( !cparams )
224  {
225  draw_info( "Please specifiy a script to launch!", NDI_RED );
226  return;
227  }
228 
229  /* cparams as passed in is a const value, so need to copy it
230  * to data we can write over.
231  */
232  strncpy(params, cparams, MAX_BUF-1);
233  params[MAX_BUF-1]=0;
234 
235 
236  /* Get name and args */
237  name=params;
238  args=name;
239  while ( *args && *args!=' ' ) ++args;
240  while ( *args && *args==' ' ) *args++ = '\0';
241  if ( *args==0 )
242  {
243  args=NULL;
244  }
245 
246 #ifdef USE_PIPE
247  /* Create two pipes */
248  if ( pipe(pipe1) )
249  {
250  draw_info("Unable to start script--pipe failed",NDI_RED);
251  return;
252  }
253  if ( pipe(pipe2) )
254  {
255  close(pipe1[0]);
256  close(pipe1[1]);
257  draw_info("Unable to start script--pipe failed",NDI_RED);
258  return;
259  }
260 #else
261  /* Create a pair of sockets */
262  if ( socketpair(PF_LOCAL,SOCK_STREAM,AF_LOCAL,pipe1) )
263  {
264  draw_info("Unable to start script--socketpair failed",NDI_RED);
265  return;
266  }
267 #endif
268 
269  /* Fork */
270  pid=fork();
271  if (pid==-1)
272  {
273  close(pipe1[0]);
274  close(pipe1[1]);
275 #ifdef USE_PIPE
276  close(pipe2[0]);
277  close(pipe2[1]);
278 #endif
279  draw_info("Unable to start script--fork failed",NDI_RED);
280  return;
281  }
282 
283  /* Child--set stdin/stdout to the pipes, then exec */
284  if ( pid==0 )
285  {
286  int i;
287  int r;
288  char *argv[256];
289 
290  /* Fill in argv[] */
291  argv[0]=name;
292  i=1;
293  while (args && *args && i < sizeof(argv)/sizeof(*argv)-1)
294  {
295  argv[i++]=args;
296  while ( *args && *args!=' ' ) ++args;
297  while ( *args && *args==' ' ) *args++ = '\0';
298  }
299  argv[i]=NULL;
300 
301  /* Clean up file descriptor space */
302  r=dup2(pipe1[0],0);
303  if ( r != 0 ) {
304  fprintf(stderr,"Script Child: Failed to set pipe1 as stdin\n");
305  }
306 #ifdef USE_PIPE
307  r=dup2(pipe2[1],1);
308 #else
309  r=dup2(pipe1[0],1);
310 #endif
311  if ( r != 1 ) {
312  fprintf(stderr,"Script Child: Failed to set pipe2 as stdout\n");
313  }
314  for (i=3;i<100;++i) close(i);
315 
316  /* EXEC */
317  r = execvp(argv[0],argv);
318 
319  /* If we get here, then there's been an failure of some sort.
320  * In my case, it's often that I don't know what script name to
321  * give to /script, so exec() can't find the script.
322  *
323  * Forward the error back to the client, using the script pipes.
324  */
325 
326  if (r != -1) {
327  printf("draw %d Script child: no error, but no execvp().\n", NDI_RED);
328  } else {
329  printf("draw %d Script child failed to start: %s\n", NDI_RED, strerror(errno));
330  }
331 
332  exit(1);
333  }
334 
335  /* Close the child's pipe ends */
336  close(pipe1[0]);
337 #ifdef USE_PIPE
338  close(pipe2[1]);
339 #endif
340 
341  if (fcntl(pipe1[1], F_SETFL, O_NDELAY)==-1) {
342  LOG(LOG_WARNING,"common::script_init","Error on fcntl.");
343  }
344 
345  /* realloc script array to add new entry; fill in the data */
346  scripts=realloc(scripts,sizeof(scripts[0])*(num_scripts+1));
347  scripts[num_scripts].name=strdup(name);
348  scripts[num_scripts].params=args?strdup(args):NULL;
349  scripts[num_scripts].out_fd=pipe1[1];
350 #ifdef USE_PIPE
351  scripts[num_scripts].in_fd=pipe2[0];
352 #else
353  scripts[num_scripts].in_fd=pipe1[1];
354 #endif
355  scripts[num_scripts].monitor=0;
356  scripts[num_scripts].num_watch=0;
357  scripts[num_scripts].watch=NULL;
358  scripts[num_scripts].cmd_count=0;
359  scripts[num_scripts].pid=pid;
360  scripts[num_scripts].sync_watch = -1;
361  ++num_scripts;
362 
363 #else /* WIN32 */
364 
365  char *name,*args;
366  char params[ MAX_BUF ];
367  SECURITY_ATTRIBUTES saAttr;
368  PROCESS_INFORMATION piProcInfo;
369  STARTUPINFO siStartupInfo;
370  HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd;
371  HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout;
372 
373  if ( !cparams )
374  {
375  draw_info( "Please specifiy a script to launch!", NDI_RED );
376  return;
377  }
378 
379  strncpy(params, cparams, MAX_BUF-1);
380  params[MAX_BUF-1] = '\0';
381 
382  /* Get name and args */
383  name=params;
384  args=name;
385  while ( *args && *args!=' ' ) ++args;
386  while ( *args && *args==' ' ) *args++ = '\0';
387  if ( *args==0 )
388  {
389  args=NULL;
390  }
391 
392  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
393  saAttr.bInheritHandle = TRUE;
394  saAttr.lpSecurityDescriptor = NULL;
395 
396  hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
397  if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
398  {
399  draw_info("Script support: stdout CreatePipe() failed", NDI_RED);
400  return;
401  }
402 
403  if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
404  {
405  draw_info("Script support: failed to redirect stdout using SetStdHandle()", NDI_RED);
406  return;
407  }
408 
409  if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(),
410  &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
411  {
412  draw_info("Script support: failed to duplicate stdout using DuplicateHandle()", NDI_RED);
413  return;
414  }
415 
416  CloseHandle(hChildStdoutRd);
417 
418  hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
419  if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
420  {
421  draw_info("Script support: stdin CreatePipe() failed", NDI_RED);
422  return;
423  }
424 
425  if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
426  {
427  draw_info("Script support: failed to redirect stdin using SetStdHandle()", NDI_RED);
428  return;
429  }
430 
431  if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(),
432  &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
433  {
434  draw_info("Script support: failed to duplicate stdin using DuplicateHandle()", NDI_RED);
435  return;
436  }
437 
438  CloseHandle(hChildStdinWr);
439 
440  ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
441  ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO));
442  siStartupInfo.cb = sizeof(STARTUPINFO);
443 
444  if (args)
445  args[-1] = ' ';
446 
447  if (!CreateProcess(NULL, name, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo))
448  {
449  draw_info("Script support: CreateProcess() failed", NDI_RED);
450  return;
451  }
452 
453  CloseHandle(piProcInfo.hThread);
454 
455  if (args)
456  args[-1] = '\0';
457 
458  if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
459  {
460  draw_info("Script support: restoring original stdin failed", NDI_RED);
461  return;
462  }
463 
464  if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
465  {
466  draw_info("Script support: restoring original stdout failed", NDI_RED);
467  return;
468  }
469 
470  /* realloc script array to add new entry; fill in the data */
471  scripts=realloc(scripts,sizeof(scripts[0])*(num_scripts+1));
472  scripts[num_scripts].name=strdup(name);
473  scripts[num_scripts].params=args?strdup(args):NULL;
474  scripts[num_scripts].out_fd=hChildStdinWrDup;
475  scripts[num_scripts].in_fd=hChildStdoutRdDup;
476  scripts[num_scripts].monitor=0;
477  scripts[num_scripts].num_watch=0;
478  scripts[num_scripts].watch=NULL;
479  scripts[num_scripts].cmd_count=0;
480  scripts[num_scripts].pid=piProcInfo.dwProcessId;
481  scripts[num_scripts].process = piProcInfo.hProcess;
482  scripts[num_scripts].sync_watch = -1;
483  ++num_scripts;
484 
485 #endif /* WIN32 */
486 }
487 
488 void script_sync(int commdiff)
489 {
490  int i;
491 
492  if (commdiff<0) commdiff +=256;
493  for (i=0;i<num_scripts; ++i) {
494  if ( commdiff <= scripts[i].sync_watch && scripts[i].sync_watch >= 0 ) {
495  char buf[1024];
496 
497  snprintf(buf, sizeof(buf), "sync %d\n",commdiff);
498  write(scripts[i].out_fd,buf,strlen(buf));
499  scripts[i].sync_watch = -1;
500  }
501  }
502 }
503 
504 void script_list(void)
505 {
506  if ( num_scripts == 0 )
507  {
508  draw_info("No scripts are currently running",NDI_BLACK);
509  }
510  else
511  {
512  int i;
513  char buf[1024];
514 
515  snprintf(buf, sizeof(buf), "%d scripts currently running:",num_scripts);
516  draw_info(buf,NDI_BLACK);
517  for ( i=0;i<num_scripts;++i)
518  {
519  if ( scripts[i].params )
520  snprintf(buf, sizeof(buf), "%d %s %s",i+1,scripts[i].name,scripts[i].params);
521  else
522  snprintf(buf, sizeof(buf), "%d %s",i+1,scripts[i].name);
523  draw_info(buf,NDI_BLACK);
524  }
525  }
526 }
527 
528 void script_kill(const char *params)
529 {
530  int i;
531 
532  /* Verify that the number is a valid array entry */
533  i=script_by_name(params);
534  if (i<0 || i>=num_scripts)
535  {
536  draw_info("No such running script",NDI_BLACK);
537  return;
538  }
539 #ifndef WIN32
540  kill(scripts[i].pid,SIGHUP);
541 #else
542  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid);
543 #endif /* WIN32 */
544  draw_info( "Killed script.", NDI_RED );
545  script_dead(i);
546 }
547 
548 #ifdef WIN32
549 void script_killall(void)
550 {
551  while (num_scripts > 0)
552  {
553  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid);
554  script_dead(0);
555  }
556 }
557 #endif /* WIN32 */
558 
559 void script_fdset(int *maxfd,fd_set *set)
560 {
561 #ifndef WIN32
562  int i;
563 
564  for ( i=0;i<num_scripts;++i)
565  {
566  FD_SET(scripts[i].in_fd,set);
567  if ( scripts[i].in_fd >= *maxfd ) *maxfd = scripts[i].in_fd+1;
568  }
569 #endif /* WIN32 */
570 }
571 
572 void script_process(fd_set *set)
573 {
574  int i;
575  int r;
576 #ifdef WIN32
577  DWORD nAvailBytes = 0;
578  char cTmp;
579  BOOL bRC;
580  DWORD dwStatus;
581  BOOL bStatus;
582 #endif
583 
584 
585  /* Determine which script's fd is set */
586  for(i=0;i<num_scripts;++i)
587  {
588 #ifndef WIN32
589  if ( FD_ISSET(scripts[i].in_fd,set) )
590 #else
591 
592  bStatus = GetExitCodeProcess(scripts[i].process,&dwStatus);
593  bRC = PeekNamedPipe(scripts[i].in_fd, &cTmp, 1, NULL, &nAvailBytes, NULL);
594  if (nAvailBytes)
595 #endif /* WIN32 */
596  {
597  /* Read in script[i].cmd */
598  r=read(scripts[i].in_fd,scripts[i].cmd+scripts[i].cmd_count,sizeof(scripts[i].cmd)-scripts[i].cmd_count-1);
599  if ( r>0 )
600  {
601  scripts[i].cmd_count+=r;
602  }
603 #ifndef WIN32
604  else if ( r==0 || errno==EBADF )
605 #else
606  else if ( r==0 || GetLastError() == ERROR_BROKEN_PIPE )
607 #endif
608  {
609  /* Script has exited; delete it */
610  script_dead(i);
611  return;
612  }
613  /* If a newline or full buffer has been reached, process it */
614  scripts[i].cmd[scripts[i].cmd_count]=0; /* terminate string */
615  while ( scripts[i].cmd_count == sizeof(scripts[i].cmd)-1
616 #ifndef WIN32
617  || strchr(scripts[i].cmd,'\n') )
618 #else
619  || strchr(scripts[i].cmd,'\r\n') )
620 #endif /* WIN32 */
621  {
623  scripts[i].cmd[scripts[i].cmd_count]=0; /* terminate string */
624  }
625  return; /* Only process one script at a time */
626  }
627 #ifdef WIN32
628  else if (!bRC || ( bStatus && ( dwStatus != STILL_ACTIVE ) ) ) /* Error: assume dead */
629  script_dead(i);
630 #endif /* WIN32 */
631  }
632 }
633 
634 void script_watch(const char *cmd, const uint8 *data, const int data_len, const enum CmdFormat format)
635 {
636  int i;
637  int w;
638  int l, len;
639 
640  /* For each script... */
641  for (i=0;i<num_scripts;++i)
642  {
643  /* For each watch... */
644  for (w=0;w<scripts[i].num_watch;++w)
645  {
646  len = data_len;
647  /* Does this command match our watch? */
648  l=strlen(scripts[i].watch[w]);
649  if ( !l || strncmp(cmd,scripts[i].watch[w],l)==0 )
650  {
651  char buf[10240];
652  if ( !len ) snprintf(buf, sizeof(buf), "watch %s\n",cmd);
653  else switch (format) {
654  case ASCII:
655  snprintf(buf, sizeof(buf), "watch %s %s\n",cmd,data);
656  break;
657  case SHORT_INT:
658  snprintf(buf, sizeof(buf), "watch %s %d %d\n",cmd,GetShort_String(data),GetInt_String(data+2));
659  break;
660  case SHORT_ARRAY:
661  {
662  int be;
663  int p;
664 
665  be=snprintf(buf, sizeof(buf), "watch %s",cmd);
666  for(p=0;p*2<len && p<100;++p) {
667  be+=snprintf(buf+be, sizeof(buf)-be, " %d",GetShort_String(data+p*2));
668  }
669  be+=snprintf(buf+be, sizeof(buf)-be, "\n");
670  }
671  break;
672  case INT_ARRAY:
673  {
674  int be;
675  int p;
676 
677  be=snprintf(buf, sizeof(buf), "watch %s",cmd);
678  for(p=0;p*4<len;++p) {
679  be+=snprintf(buf+be, sizeof(buf)-be, " %d",GetInt_String(data+p*4));
680  }
681  be+=snprintf(buf+be, sizeof(buf)-be, "\n");
682  }
683  break;
684  case STATS:
685  {
686  /*
687  * We cheat here and log each stat as a separate command, even
688  * if the server sent a bunch of updates as a single message;
689  * most scripts will be easier to write if they only parse a fixed
690  * format.
691  */
692  int be = 0;
693  while (len) {
694  int c; /* which stat */
695 
696  be+=snprintf(buf+be, sizeof(buf)-be, "watch %s",cmd);
697  c=*data;
698  ++data; --len;
700  be+=snprintf(buf+be, sizeof(buf)-be, " resists %d %d\n",c,GetShort_String(data));
701  data+=2; len-=2;
702  } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
703  be+=snprintf(buf+be, sizeof(buf)-be, " skill %d %d %" FMT64 "\n",c,*data,GetInt64_String(data+1));
704  data+=9; len-=9;
705  } else switch (c) {
706  case CS_STAT_HP:
707  be+=snprintf(buf+be, sizeof(buf)-be, " hp %d\n",GetShort_String(data));
708  data+=2; len-=2; break;
709  case CS_STAT_MAXHP:
710  be+=snprintf(buf+be, sizeof(buf)-be, " maxhp %d\n",GetShort_String(data));
711  data+=2; len-=2; break;
712  case CS_STAT_SP:
713  be+=snprintf(buf+be, sizeof(buf)-be, " sp %d\n",GetShort_String(data));
714  data+=2; len-=2; break;
715  case CS_STAT_MAXSP:
716  be+=snprintf(buf+be, sizeof(buf)-be, " maxspp %d\n",GetShort_String(data));
717  data+=2; len-=2; break;
718  case CS_STAT_GRACE:
719  be+=snprintf(buf+be, sizeof(buf)-be, " grace %d\n",GetShort_String(data));
720  data+=2; len-=2; break;
721  case CS_STAT_MAXGRACE:
722  be+=snprintf(buf+be, sizeof(buf)-be, " maxgrace %d\n",GetShort_String(data));
723  data+=2; len-=2; break;
724  case CS_STAT_STR:
725  be+=snprintf(buf+be, sizeof(buf)-be, " str %d\n",GetShort_String(data));
726  data+=2; len-=2; break;
727  case CS_STAT_INT:
728  be+=snprintf(buf+be, sizeof(buf)-be, " int %d\n",GetShort_String(data));
729  data+=2; len-=2; break;
730  case CS_STAT_POW:
731  be+=snprintf(buf+be, sizeof(buf)-be, " pow %d\n",GetShort_String(data));
732  data+=2; len-=2; break;
733  case CS_STAT_WIS:
734  be+=snprintf(buf+be, sizeof(buf)-be, " wis %d\n",GetShort_String(data));
735  data+=2; len-=2; break;
736  case CS_STAT_DEX:
737  be+=snprintf(buf+be, sizeof(buf)-be, " dex %d\n",GetShort_String(data));
738  data+=2; len-=2; break;
739  case CS_STAT_CON:
740  be+=snprintf(buf+be, sizeof(buf)-be, " con %d\n",GetShort_String(data));
741  data+=2; len-=2; break;
742  case CS_STAT_CHA:
743  be+=snprintf(buf+be, sizeof(buf)-be, " cha %d\n",GetShort_String(data));
744  data+=2; len-=2; break;
745  case CS_STAT_EXP:
746  be+=snprintf(buf+be, sizeof(buf)-be, " exp %d\n",GetInt_String(data));
747  data+=4; len-=4; break;
748  case CS_STAT_EXP64:
749  be+=snprintf(buf+be, sizeof(buf)-be, " exp %" FMT64 "\n",GetInt64_String(data));
750  data+=8; len-=8; break;
751  case CS_STAT_LEVEL:
752  be+=snprintf(buf+be, sizeof(buf)-be, " level %d\n",GetShort_String(data));
753  data+=2; len-=2; break;
754  case CS_STAT_WC:
755  be+=snprintf(buf+be, sizeof(buf)-be, " wc %d\n",GetShort_String(data));
756  data+=2; len-=2; break;
757  case CS_STAT_AC:
758  be+=snprintf(buf+be, sizeof(buf)-be, " ac %d\n",GetShort_String(data));
759  data+=2; len-=2; break;
760  case CS_STAT_DAM:
761  be+=snprintf(buf+be, sizeof(buf)-be, " dam %d\n",GetShort_String(data));
762  data+=2; len-=2; break;
763  case CS_STAT_ARMOUR:
764  be+=snprintf(buf+be, sizeof(buf)-be, " armour %d\n",GetShort_String(data));
765  data+=2; len-=2; break;
766  case CS_STAT_SPEED:
767  be+=snprintf(buf+be, sizeof(buf)-be, " speed %d\n",GetInt_String(data));
768  data+=4; len-=4; break;
769  case CS_STAT_FOOD:
770  be+=snprintf(buf+be, sizeof(buf)-be, " food %d\n",GetShort_String(data));
771  data+=2; len-=2; break;
772  case CS_STAT_WEAP_SP:
773  be+=snprintf(buf+be, sizeof(buf)-be, " weap_sp %d\n",GetInt_String(data));
774  data+=4; len-=4; break;
775  case CS_STAT_FLAGS:
776  be+=snprintf(buf+be, sizeof(buf)-be, " flags %d\n",GetShort_String(data));
777  data+=2; len-=2; break;
778  case CS_STAT_WEIGHT_LIM:
779  be+=snprintf(buf+be, sizeof(buf)-be, " weight_lim %d\n",GetInt_String(data));
780  data+=4; len-=4; break;
787  be+=snprintf(buf+be, sizeof(buf)-be, " skillexp %d %d\n",c,GetInt_String(data));
788  data+=4; len-=4; break;
795  be+=snprintf(buf+be, sizeof(buf)-be, " skilllevel %d %d\n",c,GetShort_String(data));
796  data+=2; len-=2; break;
797 
798  case CS_STAT_RANGE: {
799  int rlen=*data;
800  ++data; --len;
801  be+=snprintf(buf+be, sizeof(buf)-be, " range %*.*s\n",rlen,rlen,data);
802  data+=rlen; len-=rlen; break;
803  }
804  case CS_STAT_TITLE: {
805  int rlen=*data;
806  ++data; --len;
807  be+=snprintf(buf+be, sizeof(buf)-be, " title %*.*s\n",rlen,rlen,data);
808  data+=rlen; len-=rlen; break;
809  }
810  default:
811  be+=snprintf(buf+be, sizeof(buf)-be, " unknown %d %d bytes left\n",c,len);
812  len=0;
813  }
814  }
815  }
816  break;
817  case MIXED:
818  /* magicmap */
819  /* mapextended */
820  /* item1 item2 */
821  /* upditem */
822  /* image image2 */
823  /* face face1 face2 */
824  /* sound */
825  /* player */
826  /*
827  * If we find that scripts need data from any of the above, we can
828  * write special-case code as with stats. In the meantime, fall
829  * through and just give a hex dump. Script writers should not
830  * depend on that data format.
831  */
832  case NODATA:
833  default: {
834  int be;
835  int p;
836 
837  /*we may receive an null data, in which case len has no meaning*/
838  if (!data)
839  len=0;
840  be=snprintf(buf, sizeof(buf), "watch %s %d bytes unparsed:",cmd,len);
841  for(p=0;p<len && p<100;++p) {
842  be+=snprintf(buf+be, sizeof(buf)-be, " %02x",data[p]);
843  }
844  be+=snprintf(buf+be, sizeof(buf)-be, "\n");
845  }
846  break;
847  }
848  write(scripts[i].out_fd,buf,strlen(buf));
849  }
850  }
851  }
852 }
853 
854 void script_monitor(const char *command, int repeat, int must_send)
855 {
856  int i;
857 
858  /* For each script... */
859  for (i=0;i<num_scripts;++i)
860  {
861  /* Do we send the command? */
862  if ( scripts[i].monitor )
863  {
864  char buf[1024];
865 
866  snprintf(buf, sizeof(buf), "monitor %d %d %s\n",repeat,must_send,command);
867  write(scripts[i].out_fd,buf,strlen(buf));
868  }
869  }
870 }
871 
872 void script_monitor_str(const char *command)
873 {
874  int i;
875 
876  /* For each script... */
877  for (i=0;i<num_scripts;++i)
878  {
879  /* Do we send the command? */
880  if ( scripts[i].monitor )
881  {
882  char buf[1024];
883 
884  snprintf(buf, sizeof(buf), "monitor %s\n",command);
885  write(scripts[i].out_fd,buf,strlen(buf));
886  }
887  }
888 }
889 
890 void script_tell(const char *params)
891 {
892  int i;
893 
894  /* Find the script */
895  i=script_by_name(params);
896  if ( i<0 )
897  {
898  draw_info("No such running script",NDI_BLACK);
899  return;
900  }
901 
902  /* Send the message */
903  write(scripts[i].out_fd,"scripttell ",11);
904  write(scripts[i].out_fd,params,strlen(params));
905  write(scripts[i].out_fd,"\n",1);
906 }
907 
908 static int script_by_name(const char *name)
909 {
910  int i;
911  int l;
912 
913  if ( name==NULL )
914  {
915  return(num_scripts==1?0:-1);
916  }
917 
918  /* Parse script number */
919  if ( isdigit(*name) )
920  {
921  i=atoi(name);
922  --i;
923  if (i>=0 && i<num_scripts) return(i);
924  }
925 
926  /* Parse script name */
927  l=0;
928  while ( name[l] && name[l]!=' ' ) ++l;
929  for (i=0;i<num_scripts;++i)
930  {
931  if ( strncmp(name,scripts[i].name,l)==0 ) return(i);
932  }
933  return(-1);
934 }
935 
936 static void script_dead(int i)
937 {
938  int w;
939 
940  /* Release resources */
941 #ifndef WIN32
942  close(scripts[i].in_fd);
943  close(scripts[i].out_fd);
944 #else
945  CloseHandle(scripts[i].in_fd);
946  CloseHandle(scripts[i].out_fd);
947  CloseHandle(scripts[i].process);
948 #endif
949  free(scripts[i].name);
950  free(scripts[i].params);
951  for(w=0;w<scripts[i].num_watch;++w) free(scripts[i].watch[w]);
952  free(scripts[i].watch);
953 
954 #ifndef WIN32
955  waitpid(-1,NULL,WNOHANG);
956 #endif
957 
958  /* Move scripts with higher index numbers down one slot */
959  if ( i < (num_scripts-1) )
960  {
961  memmove(&scripts[i],&scripts[i+1],sizeof(scripts[i])*(num_scripts-i-1));
962  }
963 
964  /* Update our count */
965  --num_scripts;
966 }
967 
968 static void send_map(int i,int x,int y)
969 {
970  char buf[1024];
971 
972  if (x<0 || y<0 || the_map.x<=x || the_map.y<=y)
973  {
974  snprintf(buf, sizeof(buf), "request map %d %d unknown\n",x,y);
975  write(scripts[i].out_fd,buf,strlen(buf));
976  }
977  /*** FIXME *** send more relevant data ***/
978  snprintf(buf, sizeof(buf), "request map %d %d %d %c %c %c %c"
979  " smooth %d %d %d heads %d %d %d tails %d %d %d\n",
980  x,y,the_map.cells[x][y].darkness,
981  'n'+('y'-'n')*the_map.cells[x][y].need_update,
982  'n'+('y'-'n')*the_map.cells[x][y].have_darkness,
983  'n'+('y'-'n')*the_map.cells[x][y].need_resmooth,
984  'n'+('y'-'n')*the_map.cells[x][y].cleared,
985  the_map.cells[x][y].smooth[0],the_map.cells[x][y].smooth[1],the_map.cells[x][y].smooth[2],
986  the_map.cells[x][y].heads[0].face,the_map.cells[x][y].heads[1].face,the_map.cells[x][y].heads[2].face,
987  the_map.cells[x][y].tails[0].face,the_map.cells[x][y].tails[1].face,the_map.cells[x][y].tails[2].face
988  );
989  write(scripts[i].out_fd,buf,strlen(buf));
990 }
991 
992 static void script_process_cmd(int i)
993 {
994  char cmd[1024];
995  char *c;
996  int l;
997 
998  /*
999  * Strip out just this one command
1000  */
1001  for (l=0;l<scripts[i].cmd_count;++l)
1002  {
1003  if ( scripts[i].cmd[l]=='\n' ) break;
1004  }
1005  ++l;
1006  memcpy(cmd,scripts[i].cmd,l);
1007 #ifndef WIN32
1008  cmd[l-1]=0;
1009 #else
1010  cmd[l-2]=0;
1011 #endif
1012  if ( l<scripts[i].cmd_count )
1013  {
1014  memmove(scripts[i].cmd,scripts[i].cmd+l,scripts[i].cmd_count-l);
1015  scripts[i].cmd_count-=l;
1016  }
1017  else
1018  {
1019  scripts[i].cmd_count=0;
1020  }
1021 
1022  /*
1023  * Now the data in scripts[i] is ready for the next read.
1024  * We have a complete command in cmd[].
1025  * Process it.
1026  */
1027  /*
1028  * Script commands
1029  *
1030  * watch <command type>
1031  * unwatch <command type>
1032  * request <data type>
1033  * issue <repeat> <must_send> <command>
1034  * localcmd <command> [<params>]
1035  * draw <color> <text>
1036  * monitor
1037  * unmonitor
1038  */
1039  if ( strncmp(cmd,"sync",4)==0 ) {
1040  c=cmd+4;
1041  while ( *c && *c!=' ' ) ++c;
1042  while ( *c==' ' ) ++c;
1043  scripts[i].sync_watch = -1;
1044  if ( isdigit(*c) ) {
1045  scripts[i].sync_watch = atoi(c);
1046  }
1047  script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */
1048  }
1049  else if ( strncmp(cmd,"watch",5)==0 ) {
1050  c=cmd+5;
1051  while ( *c && *c!=' ' ) ++c;
1052  while ( *c==' ' ) ++c;
1053  c=strdup(c);
1054  scripts[i].watch=realloc(scripts[i].watch,(scripts[i].num_watch+1)*sizeof(scripts[i].watch[1]));
1055  scripts[i].watch[scripts[i].num_watch]=c;
1056  ++scripts[i].num_watch;
1057  }
1058  else if ( strncmp(cmd,"unwatch",7)==0 ) {
1059  int w;
1060 
1061  c=cmd+7;
1062  while ( *c && *c!=' ' ) ++c;
1063  while ( *c==' ' ) ++c;
1064  for (w=0;w<scripts[i].num_watch;++w) {
1065  if ( strcmp(c,scripts[i].watch[w])==0 ) {
1066  free(scripts[i].watch[w]);
1067  while ( w+1<scripts[i].num_watch ) {
1068  scripts[i].watch[w]=scripts[i].watch[w+1];
1069  ++w;
1070  }
1071  --scripts[i].num_watch;
1072  break;
1073  }
1074  }
1075  }
1076  else if ( strncmp(cmd,"request",7)==0 ) {
1077  c=cmd+7;
1078  while ( *c && *c!=' ' ) ++c;
1079  while ( *c==' ' ) ++c;
1080  if ( !*c ) return; /* bad request */
1081  /*
1082  * Request information from the client's view of the world
1083  * (mostly defined in client.h)
1084  *
1085  * Valid requests:
1086  *
1087  * player Return the player's tag and title
1088  * range Return the type and name of the currently selected range attack
1089  * stat <type> Return the specified stats
1090  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1091  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1092  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1093  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1094  * stat resists Return resistances
1095  * stat paths Return spell paths: attuned, repelled, denied.
1096  * weight Return maxweight, weight
1097  * flags Return flags (fire, run)
1098  * items inv Return a list of items in the inventory, one per line
1099  * items actv Return a list of inventory items that are active, one per line
1100  * items on Return a list of items under the player, one per line
1101  * items cont Return a list of items in the open container, one per line
1102  * map pos Return the players x,y within the current map
1103  * map near Return the 3x3 grid of the map centered on the player
1104  * map all Return all the known map information
1105  * map <x> <y> Return the information about square x,y in the current map
1106  * skills Return a list of all skill names, one per line (see also stat xp)
1107  * spells Return a list of known spells, one per line
1108  */
1109  if (strncmp(c, "player", 6) == 0) {
1110  char buf[1024];
1111 
1112  snprintf(buf, sizeof(buf), "request player %d %s\n", cpl.ob->tag, cpl.title);
1113  write(scripts[i].out_fd, buf, strlen(buf));
1114  }
1115  else if ( strncmp(c,"range",5)==0 ) {
1116  char buf[1024];
1117 
1118  snprintf(buf, sizeof(buf), "request range %s\n",cpl.range);
1119  write(scripts[i].out_fd,buf,strlen(buf));
1120  }
1121  else if ( strncmp(c,"weight",5)==0 ) {
1122  char buf[1024];
1123 
1124  snprintf(buf, sizeof(buf), "request weight %d %d\n",cpl.stats.weight_limit,(int)(cpl.ob->weight*1000));
1125  write(scripts[i].out_fd,buf,strlen(buf));
1126  }
1127  else if ( strncmp(c,"stat ",5)==0 ) {
1128  c+=4;
1129  while ( *c && *c!=' ' ) ++c;
1130  while ( *c==' ' ) ++c;
1131  if ( !*c ) return; /* bad request */
1132  /*
1133  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1134  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1135  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1136  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1137  * stat resists Return resistances
1138  */
1139  if ( strncmp(c,"stats",5)==0 ) {
1140  char buf[1024];
1141 
1142  snprintf(buf, sizeof(buf), "request stat stats %d %d %d %d %d %d %d\n",cpl.stats.Str,cpl.stats.Con,cpl.stats.Dex,cpl.stats.Int,cpl.stats.Wis,cpl.stats.Pow,cpl.stats.Cha);
1143  write(scripts[i].out_fd,buf,strlen(buf));
1144  }
1145  else if ( strncmp(c,"cmbt",4)==0 ) {
1146  char buf[1024];
1147 
1148  snprintf(buf, sizeof(buf), "request stat cmbt %d %d %d %d %d\n",cpl.stats.wc,cpl.stats.ac,cpl.stats.dam,cpl.stats.speed,cpl.stats.weapon_sp);
1149  write(scripts[i].out_fd,buf,strlen(buf));
1150  }
1151  else if ( strncmp(c,"hp",2)==0 ) {
1152  char buf[1024];
1153 
1154  snprintf(buf, sizeof(buf), "request stat hp %d %d %d %d %d %d %d\n",cpl.stats.hp,cpl.stats.maxhp,cpl.stats.sp,cpl.stats.maxsp,cpl.stats.grace,cpl.stats.maxgrace,cpl.stats.food);
1155  write(scripts[i].out_fd,buf,strlen(buf));
1156  }
1157  else if ( strncmp(c,"xp",2)==0 ) {
1158  char buf[1024];
1159  int s;
1160 
1161  snprintf(buf, sizeof(buf), "request stat xp %d %" FMT64 ,cpl.stats.level,cpl.stats.exp);
1162  write(scripts[i].out_fd,buf,strlen(buf));
1163  for(s=0;s<MAX_SKILL;++s) {
1164  snprintf(buf, sizeof(buf), " %d %" FMT64 ,cpl.stats.skill_level[s],cpl.stats.skill_exp[s]);
1165  write(scripts[i].out_fd,buf,strlen(buf));
1166  }
1167  write(scripts[i].out_fd,"\n",1);
1168  }
1169  else if ( strncmp(c,"resists",7)==0 ) {
1170  char buf[1024];
1171  int s;
1172 
1173  snprintf(buf, sizeof(buf), "request stat resists");
1174  write(scripts[i].out_fd,buf,strlen(buf));
1175  for(s=0;s<30;++s) {
1176  snprintf(buf, sizeof(buf), " %d",cpl.stats.resists[s]);
1177  write(scripts[i].out_fd,buf,strlen(buf));
1178  }
1179  write(scripts[i].out_fd,"\n",1);
1180  }
1181  else if (strncmp(c, "paths", 2) == 0) {
1182  char buf[1024];
1183 
1184  snprintf(buf, sizeof(buf), "request stat paths %d %d %d\n", cpl.stats.attuned, cpl.stats.repelled, cpl.stats.denied);
1185  write(scripts[i].out_fd, buf, strlen(buf));
1186  }
1187  }
1188  else if ( strncmp(c,"flags",5)==0 ) {
1189  char buf[1024];
1190 
1191  snprintf(buf, sizeof(buf), "request flags %d %d %d %d\n",cpl.stats.flags,cpl.fire_on,cpl.run_on,cpl.no_echo);
1192  write(scripts[i].out_fd,buf,strlen(buf));
1193  }
1194  else if ( strncmp(c,"items ",6)==0 ) {
1195  c+=5;
1196  while ( *c && *c!=' ' ) ++c;
1197  while ( *c==' ' ) ++c;
1198  if ( !*c ) return; /* bad request */
1199  /*
1200  * items inv Return a list of items in the inventory, one per line
1201  * items actv Return a list of inventory items that are active, one per line
1202  * items on Return a list of items under the player, one per line
1203  * items cont Return a list of items in the open container, one per line
1204  */
1205  if ( strncmp(c,"inv",3)==0 ) {
1206  char *buf;
1207 
1208  item *it=cpl.ob->inv;
1209  while (it) {
1210  script_send_item(i,"request items inv ",it);
1211  it=it->next;
1212  }
1213  buf="request items inv end\n";
1214  write(scripts[i].out_fd,buf,strlen(buf));
1215  }
1216  if ( strncmp(c,"actv",4)==0 ) {
1217  char *buf;
1218 
1219  item *it=cpl.ob->inv;
1220  while (it) {
1221  if (it->applied) script_send_item(i,"request items actv ",it);
1222  it=it->next;
1223  }
1224  buf="request items actv end\n";
1225  write(scripts[i].out_fd,buf,strlen(buf));
1226  }
1227  if ( strncmp(c,"on",2)==0 ) {
1228  char *buf;
1229 
1230  item *it=cpl.below->inv;
1231  while (it) {
1232  script_send_item(i,"request items on ",it);
1233  it=it->next;
1234  }
1235  buf="request items on end\n";
1236  write(scripts[i].out_fd,buf,strlen(buf));
1237  }
1238  if ( strncmp(c,"cont",4)==0 ) {
1239  char *buf;
1240 
1241  item *it=cpl.container->inv;
1242  while (it) {
1243  script_send_item(i,"request items cont ",it);
1244  it=it->next;
1245  }
1246  buf="request items cont end\n";
1247  write(scripts[i].out_fd,buf,strlen(buf));
1248  }
1249  }
1250  else if ( strncmp(c,"map ",4)==0 ) {
1251  int x,y;
1252 
1253  c+=3;
1254  while ( *c && *c!=' ' ) ++c;
1255  while ( *c==' ' ) ++c;
1256  if ( !*c ) return; /* bad request */
1257  /*
1258  * map pos Return the players x,y within the current map
1259  * map near Return the 3x3 grid of the map centered on the player
1260  * map all Return all the known map information
1261  * map <x> <y> Return the information about square x,y in the current map
1262  */
1263  if ( strncmp(c,"pos",3)==0 ) {
1264  char buf[1024];
1265 
1266  snprintf(buf, sizeof(buf), "request map pos %d %d\n",pl_pos.x,pl_pos.y);
1267  write(scripts[i].out_fd,buf,strlen(buf));
1268  }
1269  else if ( strncmp(c,"near",4)==0 ) {
1270  for(y=0;y<3;++y)
1271  for(x=0;x<3;++x)
1272  send_map(i,
1275  );
1276  }
1277  else if ( strncmp(c,"all",3)==0 ) {
1278  char buf[1024];
1279 
1280  for(y=0;y<the_map.y;++y)
1281  for(x=0;x<the_map.x;++x)
1282  send_map(i,x,y);
1283  snprintf(buf, sizeof(buf), "request map end\n");
1284  write(scripts[i].out_fd,buf,strlen(buf));
1285  }
1286  else {
1287  while ( *c && !isdigit(*c) ) ++c;
1288  if ( !*c ) return; /* No x specified */
1289  x=atoi(c);
1290  while ( *c && *c!=' ' ) ++c;
1291  while ( *c && !isdigit(*c) ) ++c;
1292  if ( !*c ) return; /* No y specified */
1293  y=atoi(c);
1294  send_map(i,x,y);
1295  }
1296  }
1297  else if (strncmp(c, "skills", 6) == 0) {
1298  char buf[1024];
1299  int s;
1300 
1301  for (s = 0; s < CS_NUM_SKILLS; s++) {
1302  if (skill_names[s]) {
1303  sprintf(buf, "request skills %d %s\n", CS_STAT_SKILLINFO + s, skill_names[s]);
1304  write(scripts[i].out_fd, buf, strlen(buf));
1305  }
1306  }
1307  sprintf(buf, "request skills end\n");
1308  write(scripts[i].out_fd, buf, strlen(buf));
1309  }
1310  else if (strncmp(c, "spells", 6) == 0) {
1311  char buf[1024];
1312  Spell *spell;
1313 
1314  for (spell = cpl.spelldata; spell; spell = spell->next) {
1315  sprintf(buf, "request spells %d %d %d %d %d %d %d %d %s\n",
1316  spell->tag, spell->level, spell->sp, spell->grace,
1317  spell->skill_number, spell->path, spell->time,
1318  spell->dam, spell->name);
1319  write(scripts[i].out_fd, buf, strlen(buf));
1320  }
1321  sprintf(buf, "request spells end\n");
1322  write(scripts[i].out_fd, buf, strlen(buf));
1323  }
1324  else {
1325  char buf[1024];
1326 
1327  snprintf(buf, sizeof(buf), "Script %d %s malfunction; unimplemented request:",i+1,scripts[i].name);
1328  draw_info(buf,NDI_RED);
1329  draw_info(cmd,NDI_RED);
1330  }
1331  }
1332  else if ( strncmp(cmd,"issue",5)==0 ) {
1333  int repeat;
1334  int must_send;
1335 
1336  c=cmd+5;
1337  while ( *c && *c==' ' ) ++c;
1338  if ( *c && (isdigit(*c) || *c=='-') ) { /* repeat specified; use send_command() */
1339  repeat=atoi(c);
1340  while ( *c && *c!=' ' ) ++c;
1341  while ( *c && !isdigit(*c) && *c!='-' ) ++c;
1342  if ( !*c ) return; /* No must_send specified */
1343  must_send=atoi(c);
1344  while ( *c && *c!=' ' ) ++c;
1345  if ( !*c ) return; /* No command specified */
1346  while ( *c==' ' ) ++c;
1347  if ( repeat != -1 )
1348  {
1349  int r;
1350 
1351  r=send_command(c,repeat,must_send);
1352  if ( r!=1 ) {
1353  char buf[1024];
1354 
1355  snprintf(buf, sizeof(buf), "Script %d %s malfunction; command not sent",i+1,scripts[i].name);
1356  draw_info(buf,NDI_RED);
1357  draw_info(cmd,NDI_RED);
1358  }
1359  }
1360  }
1361  else
1362  {
1363  c=cmd+5;
1364  while ( *c && *c!=' ' ) ++c;
1365  while ( *c==' ' ) ++c;
1366 
1367  /*
1368  * Check special cases: "mark <tag>" or "lock <new state> <tag>"
1369  */
1370  if ( strncmp(c,"mark",4)==0 ) {
1371  int tag;
1372  SockList sl;
1373  uint8 buf[MAX_BUF];
1374 
1375  c+=4;
1376 
1377  while ( *c && !isdigit(*c) ) ++c;
1378  if ( !*c ) return; /* No tag specified */
1379  tag=atoi(c);
1380 
1381  SockList_Init(&sl, buf);
1382  SockList_AddString(&sl, "mark ");
1383  SockList_AddInt(&sl, tag);
1384  SockList_Send(&sl, csocket.fd);
1385  }
1386  else if ( strncmp(c,"lock",4)==0 ) {
1387  int tag,locked;
1388  SockList sl;
1389  uint8 buf[MAX_BUF];
1390 
1391  c+=4;
1392 
1393  while ( *c && !isdigit(*c) ) ++c;
1394  if ( !*c ) return; /* No state specified */
1395  locked=atoi(c);
1396  while ( *c && *c!=' ' ) ++c;
1397  while ( *c && !isdigit(*c) ) ++c;
1398  if ( !*c ) return; /* No tag specified */
1399  tag=atoi(c);
1400 
1401  SockList_Init(&sl, buf);
1402  SockList_AddString(&sl, "lock ");
1403  SockList_AddChar(&sl, locked);
1404  SockList_AddInt(&sl, tag);
1405  SockList_Send(&sl, csocket.fd);
1406  }
1407  else {
1408  cs_print_string(csocket.fd, "%s", c);
1409  }
1410  }
1411  }
1412  else if ( strncmp(cmd,"localcmd",8)==0){
1413  char* param;
1414  c=cmd+8;
1415  while (*c==' ') c++;
1416  param=c;
1417  while ( (*param!='\0') && (*param!=' ')) param++;
1418  if (*param==' '){
1419  *param='\0';
1420  param++;
1421  } else
1422  param=NULL;
1423 
1424  if (!handle_local_command(c, param)){
1425  char buf[1024];
1426  snprintf(buf, sizeof(buf), "Script %s malfunction; localcmd not understood",scripts[i].name);
1427  draw_info(buf,NDI_RED);
1428  snprintf(buf, sizeof(buf), "Script <<localcmd %s %s>>",c,(param==NULL)?"":param);
1429  draw_info(buf,NDI_RED);
1430  }
1431  }
1432  else if ( strncmp(cmd,"draw",4)==0 ) {
1433  int color;
1434 
1435  c=cmd+4;
1436  while ( *c && !isdigit(*c) ) ++c;
1437  if ( !*c ) return; /* No color specified */
1438  color=atoi(c);
1439  while ( *c && *c!=' ' ) ++c;
1440  if ( !*c ) return; /* No message specified */
1441  while ( *c==' ' ) ++c;
1442  draw_info(c,color);
1443  }
1444  else if ( strncmp(cmd,"monitor",7)==0 ) scripts[i].monitor=1;
1445  else if ( strncmp(cmd,"unmonitor",9)==0 ) scripts[i].monitor=0;
1446  else {
1447  char buf[1024];
1448 
1449  snprintf(buf, sizeof(buf), "Script %d %s malfunction; invalid command:",i+1,scripts[i].name);
1450  draw_info(buf,NDI_RED);
1451  draw_info(cmd,NDI_RED);
1452  }
1453 }
1454 
1455 /*
1456  * script_send_item()
1457  *
1458  * Send one line to the script with item information.
1459  *
1460  * A header string is passed in. The format is:
1461  *
1462  * <header> tag num weight flags type name
1463  *
1464  * flags are a bitmask:
1465  * magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated
1466  * 256 128 64 32 16 8 4 2 1
1467  */
1468 static void script_send_item(int i, const char *head, const item *it)
1469 {
1470  char buf[4096];
1471  int flags;
1472 
1473  flags=it->magical;
1474  flags= (flags<<1)|it->cursed;
1475  flags= (flags<<1)|it->damned;
1476  flags= (flags<<1)|it->unpaid;
1477  flags= (flags<<1)|it->locked;
1478  flags= (flags<<1)|it->applied;
1479  flags= (flags<<1)|it->open;
1480  flags= (flags<<1)|it->was_open;
1481  flags= (flags<<1)|it->inv_updated;
1482  snprintf(buf, sizeof(buf), "%s%d %d %f %d %d %s\n",head,it->tag,it->nrof,it->weight,flags,it->type,it->d_name);
1483  write(scripts[i].out_fd,buf,strlen(buf));
1484 }
1485 
1486 #endif /* CPROTO */
#define CS_STAT_RANGE
Definition: newclient.h:125
float weight
Definition: item.h:56
void SockList_Init(SockList *sl, uint8 *buf)
Definition: newsocket.c:91
int maxfd
Definition: client.c:68
#define CS_STAT_SPEED
Definition: newclient.h:122
#define CS_STAT_ARMOUR
Definition: newclient.h:121
uint8 skill_number
Definition: client.h:262
static void script_process_cmd(int i)
Definition: script.c:992
int y
Definition: mapdata.h:93
sint8 level
Definition: client.h:215
int handle_local_command(const char *cp, const char *cpnext)
Definition: p_cmd.c:858
uint16 grace
Definition: client.h:260
sint16 hp
Definition: client.h:216
char * params
Definition: script.c:136
Definition: script.c:134
sint8 Int
Definition: client.h:211
sint32 speed
Definition: client.h:229
#define CS_STAT_MAXGRACE
Definition: newclient.h:129
void script_monitor_str(const char *command)
Definition: script.c:872
Definition: script.h:43
#define CS_STAT_SKILLEXP_PHYSIQUE
Definition: newclient.h:168
#define CS_STAT_SKILLEXP_WISDOM
Definition: newclient.h:172
sint8 Str
Definition: client.h:206
#define PF_LOCAL
Definition: script.h:31
static int data_len
Definition: png.c:56
#define MAX_SKILL
Definition: client.h:61
item * ob
Definition: client.h:272
short GetShort_String(const unsigned char *data)
Definition: newsocket.c:162
sint16 food
Definition: client.h:223
snd_pcm_hw_params_t * params
Definition: alsa9.c:111
int SockList_Send(SockList *sl, int fd)
Definition: newsocket.c:127
struct item_struct * next
Definition: item.h:46
ClientSocket csocket
Definition: client.c:78
sint16 maxhp
Definition: client.h:217
#define CS_STAT_SKILLEXP_WILEVEL
Definition: newclient.h:173
int num_watch
Definition: script.c:145
sint8 ac
Definition: client.h:214
uint32 repelled
Definition: client.h:236
static void script_dead(int i)
Definition: script.c:936
struct MapCellLayer tails[MAXLAYERS]
Definition: mapdata.h:78
Stats stats
Definition: client.h:285
#define CS_STAT_FOOD
Definition: newclient.h:123
static int script_by_name(const char *name)
Definition: script.c:908
static void send_map(int i, int x, int y)
Definition: script.c:968
int monitor
Definition: script.c:144
int sync_watch
Definition: script.c:155
uint32 no_echo
Definition: client.h:294
sint64 skill_exp[MAX_SKILL]
Definition: client.h:244
sint8 Con
Definition: client.h:208
uint16 inv_updated
Definition: item.h:70
sint8 Cha
Definition: client.h:210
uint32 tag
Definition: client.h:255
char * skill_names[MAX_SKILL]
Definition: client.c:63
uint8 have_darkness
Definition: mapdata.h:82
#define CS_STAT_MAXSP
Definition: newclient.h:109
void script_sync(int commdiff)
Definition: script.c:488
#define CS_STAT_POW
Definition: newclient.h:127
#define CS_STAT_SKILLEXP_PELEVEL
Definition: newclient.h:165
#define CS_STAT_SKILLEXP_MELEVEL
Definition: newclient.h:167
static void script_send_item(int i, const char *head, const item *it)
Definition: script.c:1468
sint8 Pow
Definition: client.h:212
uint8 need_update
Definition: mapdata.h:81
void SockList_AddInt(SockList *sl, uint32 data)
Definition: newsocket.c:109
uint16 applied
Definition: item.h:67
int in_fd
Definition: script.c:139
#define CS_STAT_SKILLEXP_MAGIC
Definition: newclient.h:170
sint16 face
Definition: mapdata.h:45
uint32 denied
Definition: client.h:239
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
#define CS_STAT_GRACE
Definition: newclient.h:128
#define NDI_RED
Definition: newclient.h:204
void script_init(const char *cparams)
Definition: script.c:213
#define TRUE
Definition: client-types.h:71
#define CS_STAT_SKILLEXP_AGILITY
Definition: newclient.h:162
#define CS_STAT_SKILLINFO
Definition: newclient.h:181
sint16 grace
Definition: client.h:220
int pid
Definition: script.c:150
static struct script * scripts
Definition: script.c:161
sint16 resists[30]
Definition: client.h:241
#define CS_STAT_DAM
Definition: newclient.h:120
int cs_print_string(int fd, const char *str,...)
Definition: newsocket.c:259
uint16 locked
Definition: item.h:66
sint8 Dex
Definition: client.h:207
int send_command(const char *command, int repeat, int must_send)
Definition: player.c:195
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
char d_name[NAME_LEN]
Definition: item.h:50
uint8 darkness
Definition: mapdata.h:80
#define CS_STAT_WIS
Definition: newclient.h:112
#define CS_STAT_WC
Definition: newclient.h:118
uint16 time
Definition: client.h:258
item * below
Definition: client.h:273
CmdFormat
Definition: script.h:37
int out_fd
Definition: script.c:138
Client_Player cpl
Definition: client.c:77
uint16 flags
Definition: client.h:240
#define CS_STAT_FLAGS
Definition: newclient.h:130
item * container
Definition: client.h:275
uint8 cleared
Definition: mapdata.h:84
sint32 tag
Definition: item.h:54
#define CS_STAT_LEVEL
Definition: newclient.h:117
const char *const rcsid_common_script_c
Definition: script.c:1
struct Map the_map
Definition: mapdata.c:121
char * name
Definition: image.c:61
struct MapCell ** cells
Definition: mapdata.h:95
#define CS_STAT_SKILLEXP_AGLEVEL
Definition: newclient.h:163
int fd
Definition: client.h:97
void SockList_AddString(SockList *sl, const char *str)
Definition: newsocket.c:117
uint16 magical
Definition: item.h:62
int x
Definition: mapdata.h:92
#define AF_LOCAL
Definition: script.h:34
static int num_scripts
Definition: script.c:162
#define CS_STAT_DEX
Definition: newclient.h:113
#define CS_STAT_EXP64
Definition: newclient.h:132
#define CS_STAT_SKILLEXP_PHLEVEL
Definition: newclient.h:169
uint32 run_on
Definition: client.h:291
int cmd_count
Definition: script.c:147
sint16 maxgrace
Definition: client.h:221
#define CS_STAT_WEAP_SP
Definition: newclient.h:124
#define CS_STAT_RESIST_END
Definition: newclient.h:139
uint32 fire_on
Definition: client.h:290
struct Spell_struct * next
Definition: client.h:249
sint16 maxsp
Definition: client.h:219
uint16 dam
Definition: client.h:261
#define MAX_BUF
Definition: client-types.h:128
uint16 type
Definition: item.h:75
uint8 need_resmooth
Definition: mapdata.h:83
void script_list(void)
Definition: script.c:504
uint16 unpaid
Definition: item.h:65
Definition: script.h:42
void script_watch(const char *cmd, const uint8 *data, const int data_len, const enum CmdFormat format)
Definition: script.c:634
void SockList_AddChar(SockList *sl, char c)
Definition: newsocket.c:97
sint16 dam
Definition: client.h:226
#define CS_STAT_TITLE
Definition: newclient.h:126
#define CS_NUM_SKILLS
Definition: newclient.h:182
Spell * spelldata
Definition: client.h:286
#define CONFIG_MAPWIDTH
Definition: client.h:170
uint16 open
Definition: item.h:68
char name[256]
Definition: client.h:250
uint16 was_open
Definition: item.h:69
sint32 weapon_sp
Definition: client.h:230
Definition: script.h:38
char ** watch
Definition: script.c:146
uint16 smooth[MAXLAYERS]
Definition: mapdata.h:79
#define CS_STAT_SKILLEXP_MENTAL
Definition: newclient.h:166
void script_tell(const char *params)
Definition: script.c:890
#define CS_STAT_SKILLEXP_PERSONAL
Definition: newclient.h:164
void script_kill(const char *params)
Definition: script.c:528
void script_fdset(int *maxfd, fd_set *set)
Definition: script.c:559
#define CONFIG_MAPHEIGHT
Definition: client.h:171
uint32 nrof
Definition: item.h:55
uint32 attuned
Definition: client.h:233
char cmd[1024]
Definition: script.c:148
sint16 sp
Definition: client.h:218
#define CS_STAT_HP
Definition: newclient.h:106
uint16 sp
Definition: client.h:259
#define CS_STAT_RESIST_START
Definition: newclient.h:138
int command_sent
Definition: client.h:103
uint16 level
Definition: client.h:257
char range[MAX_BUF]
Definition: client.h:288
int command_received
Definition: client.h:103
void draw_info(const char *str, int color)
Definition: gx11.c:1773
char title[MAX_BUF]
Definition: client.h:287
#define CS_STAT_INT
Definition: newclient.h:111
char * name
Definition: script.c:135
Definition: script.h:44
#define CS_STAT_CON
Definition: newclient.h:114
unsigned char uint8
Definition: client-types.h:81
struct MapCellLayer heads[MAXLAYERS]
Definition: mapdata.h:77
uint16 damned
Definition: item.h:64
#define CS_STAT_SP
Definition: newclient.h:108
sint8 wc
Definition: client.h:213
#define NDI_BLACK
Definition: newclient.h:201
sint64 GetInt64_String(const unsigned char *data)
Definition: newsocket.c:149
struct item_struct * inv
Definition: item.h:49
sint16 skill_level[MAX_SKILL]
Definition: client.h:243
#define CS_STAT_CHA
Definition: newclient.h:115
#define FALSE
Definition: client-types.h:68
sint8 Wis
Definition: client.h:209
sint64 exp
Definition: client.h:222
uint32 weight_limit
Definition: client.h:245
#define CS_STAT_WEIGHT_LIM
Definition: newclient.h:131
#define CS_STAT_SKILLEXP_MALEVEL
Definition: newclient.h:171
uint32 path
Definition: client.h:266
void script_process(fd_set *set)
Definition: script.c:572
int GetInt_String(const unsigned char *data)
Definition: newsocket.c:143
uint16 cursed
Definition: item.h:63
PlayerPosition pl_pos
Definition: map.c:69
#define CS_STAT_STR
Definition: newclient.h:110
#define CS_STAT_AC
Definition: newclient.h:119
void script_monitor(const char *command, int repeat, int must_send)
Definition: script.c:854
#define CS_STAT_EXP
Definition: newclient.h:116
#define CS_STAT_MAXHP
Definition: newclient.h:107