Crossfire Client, Trunk  R19597
script.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
14 /*
15  * This file has its own script.h for prototypes, so don't want to include
16  * this when doing a 'make proto'.
17  */
18 #ifndef CPROTO
19 
97 /*
98  * Include files
99  */
100 
101 /*
102  * This does not work under Windows for now. Someday this will be fixed :)
103  */
104 
105 #ifndef WIN32
106 #include <errno.h>
107 #include <sys/types.h>
108 #include <sys/socket.h>
109 #include <sys/wait.h>
110 /* for SIGHUP */
111 #include <signal.h>
112 #endif
113 #include <ctype.h>
114 
115 #include <client.h>
116 #include <external.h>
117 #include <script.h>
118 #include <p_cmd.h>
119 #include "mapdata.h"
120 
121 /*
122  * Data structures
123  */
124 struct script {
125  char *name; /* the script name */
126  char *params; /* the script parameters, if any */
127 #ifndef WIN32
128  int out_fd; /* the file descriptor to which the client writes to the script */
129  int in_fd; /* the file descriptor from which we read commands from the script */
130 #else
131  HANDLE out_fd; /* the file descriptor to which the client writes to the script */
132  HANDLE in_fd; /* the file descriptor from which we read commands from the script */
133 #endif /* WIN32 */
134  int monitor; /* true if this script is monitoring commands sent to the server */
135  int num_watch; /* number of commands we're watching */
136  char **watch; /* array of commands that we're watching */
137  int cmd_count; /* bytes already read in */
138  char cmd[1024]; /* command from the script */
139 #ifndef WIN32
140  int pid;
141 #else
142  DWORD pid; /* Handle to Win32 process ID */
143  HANDLE process; /* Handle of Win32 process */
144 #endif
146 };
147 
148 /*
149  * Global variables
150  */
151 static struct script *scripts = NULL;
152 
153 static int num_scripts = 0;
154 
155 /*
156  * Prototypes
157  */
158 static int script_by_name(const char *name);
159 
160 static void script_dead(int i);
161 
162 static void script_process_cmd(int i);
163 
164 static void send_map(int i, int x, int y);
165 
166 static void script_send_item(int i, const char *head, const item *it);
167 
168 
169 /*
170  * Functions
171  */
172 
173 #ifdef WIN32
174 
175 #define write(x, y, z) emulate_write(x, y, z)
176 #define read(x, y, z) emulate_read(x, y, z)
177 
178 static int emulate_read(HANDLE fd, char *buf, int len)
179 {
180  DWORD dwBytesRead;
181  BOOL rc;
182 
183  FlushFileBuffers(fd);
184  rc = ReadFile(fd, buf, len, &dwBytesRead, NULL);
185  if (rc == FALSE) {
186  return(-1);
187  }
188  buf[dwBytesRead] = '\0';
189 
190  return(dwBytesRead);
191 }
192 
193 static int emulate_write(HANDLE fd, const char *buf, int len)
194 {
195  DWORD dwBytesWritten;
196  BOOL rc;
197 
198  rc = WriteFile(fd, buf, len, &dwBytesWritten, NULL);
199  FlushFileBuffers(fd);
200  if (rc == FALSE) {
201  return(-1);
202  }
203 
204  return(dwBytesWritten);
205 }
206 
207 #endif /* WIN32 */
208 
209 void script_init(const char *cparams) {
210 #ifndef WIN32
211  int pipe1[2], pipe2[2];
212  int pid;
213  char *name, *args, params[MAX_BUF];
214 
215  if (!cparams) {
217  "Please specify a script to start. For help, type "
218  "'help script'.");
219  return;
220  }
221 
222  /* cparams is a const value, so copy the data into a buffer */
223  strncpy(params, cparams, MAX_BUF - 1);
224  params[MAX_BUF - 1] = '\0';
225 
226  /* Get name and args */
227  name = params;
228  args = name;
229  while (*args && *args != ' ') {
230  ++args;
231  }
232  while (*args && *args == ' ') {
233  *args++ = '\0';
234  }
235  if (*args == 0) {
236  args = NULL;
237  }
238 
239  /* Open two pipes, one for stdin and the other for stdout. */
240  if (pipe(pipe1) != 0) {
242  "Unable to start script--pipe failed");
243  return;
244  }
245  if (pipe(pipe2) != 0) {
246  close(pipe1[0]);
247  close(pipe1[1]);
248 
250  "Unable to start script--pipe failed");
251  return;
252  }
253 
254  /* Fork */
255  pid = fork();
256  if (pid == -1) {
257  close(pipe1[0]);
258  close(pipe1[1]);
259  close(pipe2[0]);
260  close(pipe2[1]);
262  "Unable to start script--fork failed");
263  return;
264  }
265 
266  /* Child--set stdin/stdout to the pipes, then exec */
267  if (pid == 0) {
268  int i;
269  int r;
270  char *argv[256];
271 
272  /* Fill in argv[] */
273  argv[0] = name;
274  i = 1;
275  while (args && *args && i < sizeof(argv)/sizeof(*argv)-1) {
276  argv[i++] = args;
277  while (*args && *args != ' ') {
278  ++args;
279  }
280  while (*args && *args == ' ') {
281  *args++ = '\0';
282  }
283  }
284  argv[i] = NULL;
285 
286  /* Clean up file descriptor space */
287  r = dup2(pipe1[0], 0);
288  if (r != 0) {
289  fprintf(stderr, "Script Child: Failed to set pipe1 as stdin\n");
290  }
291  r = dup2(pipe2[1], 1);
292  if (r != 1) {
293  fprintf(stderr, "Script Child: Failed to set pipe2 as stdout\n");
294  }
295  for (i = 3; i < 100; ++i) {
296  close(i);
297  }
298 
299  /* EXEC */
300  r = execvp(argv[0], argv);
301 
302  /* If we get here, then there's been an failure of some sort.
303  * In my case, it's often that I don't know what script name to
304  * give to /script, so exec() can't find the script.
305  *
306  * Forward the error back to the client, using the script pipes.
307  */
308 
309  if (r != -1) {
310  printf("draw %d Script child: no error, but no execvp().\n", NDI_RED);
311  } else {
312  printf("draw %d Script child failed to start: %s\n", NDI_RED, strerror(errno));
313  }
314 
315  exit(1);
316  }
317 
318  /* Close the child's pipe ends */
319  close(pipe1[0]);
320  close(pipe2[1]);
321 
322  if (fcntl(pipe1[1], F_SETFL, O_NDELAY) == -1) {
323  LOG(LOG_WARNING, "common::script_init", "Error on fcntl.");
324  }
325 
326  /* g_realloc script array to add new entry; fill in the data */
327  scripts = g_realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
328 
329  if (scripts == NULL) {
330  LOG(LOG_ERROR, "script_init",
331  "Could not allocate memory: %s", strerror(errno));
332  exit(EXIT_FAILURE);
333  }
334 
335  scripts[num_scripts].name = g_strdup(name);
336  scripts[num_scripts].params = args ? g_strdup(args) : NULL;
337  scripts[num_scripts].out_fd = pipe1[1];
338  scripts[num_scripts].in_fd = pipe2[0];
339  scripts[num_scripts].monitor = 0;
340  scripts[num_scripts].num_watch = 0;
341  scripts[num_scripts].watch = NULL;
342  scripts[num_scripts].cmd_count = 0;
343  scripts[num_scripts].pid = pid;
344  scripts[num_scripts].sync_watch = -1;
345  ++num_scripts;
346 #else /* WIN32 */
347 
348  char *name, *args;
349  char params[ MAX_BUF ];
350  SECURITY_ATTRIBUTES saAttr;
351  PROCESS_INFORMATION piProcInfo;
352  STARTUPINFO siStartupInfo;
353  HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd;
354  HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout;
355 
356  if (!cparams) {
358  "Please specifiy a script to launch!");
359  return;
360  }
361 
362  strncpy(params, cparams, MAX_BUF-1);
363  params[MAX_BUF-1] = '\0';
364 
365  /* Get name and args */
366  name = params;
367  args = name;
368  while (*args && *args != ' ') {
369  ++args;
370  }
371  while (*args && *args == ' ') {
372  *args++ = '\0';
373  }
374  if (*args == 0) {
375  args = NULL;
376  }
377 
378  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
379  saAttr.bInheritHandle = TRUE;
380  saAttr.lpSecurityDescriptor = NULL;
381 
382  hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
383  if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
385  "Script support: stdout CreatePipe() failed");
386  return;
387  }
388 
389  if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
391  "Script support: failed to redirect stdout using SetStdHandle()");
392  return;
393  }
394 
395  if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
397  "Script support: failed to duplicate stdout using DuplicateHandle()");
398  return;
399  }
400 
401  CloseHandle(hChildStdoutRd);
402 
403  hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
404  if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
406  "Script support: stdin CreatePipe() failed");
407  return;
408  }
409 
410  if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) {
412  "Script support: failed to redirect stdin using SetStdHandle()");
413  return;
414  }
415 
416  if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
418  "Script support: failed to duplicate stdin using DuplicateHandle()");
419  return;
420  }
421 
422  CloseHandle(hChildStdinWr);
423 
424  ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
425  ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO));
426  siStartupInfo.cb = sizeof(STARTUPINFO);
427 
428  if (args) {
429  args[-1] = ' ';
430  }
431 
432  if (!CreateProcess(NULL, name, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo)) {
434  "Script support: CreateProcess() failed");
435  return;
436  }
437 
438  CloseHandle(piProcInfo.hThread);
439 
440  if (args) {
441  args[-1] = '\0';
442  }
443 
444  if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) {
446  "Script support: restoring original stdin failed");
447  return;
448  }
449 
450  if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
452  "Script support: restoring original stdout failed");
453  return;
454  }
455 
456  /* g_realloc script array to add new entry; fill in the data */
457  scripts = g_realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
458 
459  if (scripts == NULL) {
460  LOG(LOG_ERROR, "script_init",
461  "Could not allocate memory: %s", strerror(errno));
462  exit(EXIT_FAILURE);
463  }
464 
465  scripts[num_scripts].name = g_strdup(name);
466  scripts[num_scripts].params = args ? g_strdup(args) : NULL;
467  scripts[num_scripts].out_fd = hChildStdinWrDup;
468  scripts[num_scripts].in_fd = hChildStdoutRdDup;
469  scripts[num_scripts].monitor = 0;
470  scripts[num_scripts].num_watch = 0;
471  scripts[num_scripts].watch = NULL;
472  scripts[num_scripts].cmd_count = 0;
473  scripts[num_scripts].pid = piProcInfo.dwProcessId;
474  scripts[num_scripts].process = piProcInfo.hProcess;
475  scripts[num_scripts].sync_watch = -1;
476  ++num_scripts;
477 #endif /* WIN32 */
478 }
479 
480 void script_sync(int commdiff)
481 {
482  int i;
483 
484  if (commdiff < 0) {
485  commdiff +=256;
486  }
487  for (i = 0; i < num_scripts; ++i) {
488  if (commdiff <= scripts[i].sync_watch && scripts[i].sync_watch >= 0) {
489  char buf[1024];
490 
491  snprintf(buf, sizeof(buf), "sync %d\n", commdiff);
492  write(scripts[i].out_fd, buf, strlen(buf));
493  scripts[i].sync_watch = -1;
494  }
495  }
496 }
497 
498 void script_list(void)
499 {
500  if (num_scripts == 0) {
502  "No scripts are currently running");
503  } else {
504  int i;
505  char buf[1024];
506 
507  snprintf(buf, sizeof(buf), "%d scripts currently running:", num_scripts);
509  for (i = 0; i < num_scripts; ++i) {
510  if (scripts[i].params) {
511  snprintf(buf, sizeof(buf), "%d %s %s", i+1, scripts[i].name, scripts[i].params);
512  } else {
513  snprintf(buf, sizeof(buf), "%d %s", i+1, scripts[i].name);
514  }
516  }
517  }
518 }
519 
520 void script_kill(const char *params)
521 {
522  int i;
523 
524  /* Verify that the number is a valid array entry */
525  i = script_by_name(params);
526  if (i < 0 || i >= num_scripts) {
528  "No such running script");
529  return;
530  }
531 #ifndef WIN32
532  kill(scripts[i].pid, SIGHUP);
533 #else
534  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid);
535 #endif /* WIN32 */
537  "Killed script.");
538  script_dead(i);
539 }
540 
541 #ifdef WIN32
542 void script_killall(void)
543 {
544  while (num_scripts > 0) {
545  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid);
546  script_dead(0);
547  }
548 }
549 #endif /* WIN32 */
550 
551 void script_fdset(int *maxfd, fd_set *set)
552 {
553 #ifndef WIN32
554  int i;
555 
556  for (i = 0; i < num_scripts; ++i) {
557  FD_SET(scripts[i].in_fd, set);
558  if (scripts[i].in_fd >= *maxfd) {
559  *maxfd = scripts[i].in_fd+1;
560  }
561  }
562 #endif /* WIN32 */
563 }
564 
565 void script_process(fd_set *set)
566 {
567  int i;
568  int r;
569 #ifdef WIN32
570  DWORD nAvailBytes = 0;
571  char cTmp;
572  BOOL bRC;
573  DWORD dwStatus;
574  BOOL bStatus;
575 #endif
576 
577  /* Determine which script's fd is set */
578  for (i = 0; i < num_scripts; ++i) {
579 #ifndef WIN32
580  if (FD_ISSET(scripts[i].in_fd, set))
581 #else
582  bStatus = GetExitCodeProcess(scripts[i].process, &dwStatus);
583  bRC = PeekNamedPipe(scripts[i].in_fd, &cTmp, 1, NULL, &nAvailBytes, NULL);
584  if (nAvailBytes)
585 #endif /* WIN32 */
586  {
587  /* Read in script[i].cmd */
588  r = read(scripts[i].in_fd, scripts[i].cmd+scripts[i].cmd_count, sizeof(scripts[i].cmd)-scripts[i].cmd_count-1);
589  if (r > 0) {
590  scripts[i].cmd_count += r;
591  }
592 #ifndef WIN32
593  else if (r == 0 || errno == EBADF)
594 #else
595  else if (r == 0 || GetLastError() == ERROR_BROKEN_PIPE)
596 #endif
597  {
598  /* Script has exited; delete it */
599  script_dead(i);
600  return;
601  }
602  /* If a newline or full buffer has been reached, process it */
603  scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
604  while (scripts[i].cmd_count == sizeof(scripts[i].cmd)-1
605 #ifndef WIN32
606  || strchr(scripts[i].cmd, '\n'))
607 #else
608  || strchr(scripts[i].cmd, '\r\n'))
609 #endif /* WIN32 */
610  {
612  scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
613  }
614  return; /* Only process one script at a time */
615  }
616 #ifdef WIN32
617  else if (!bRC || (bStatus && (dwStatus != STILL_ACTIVE))) { /* Error: assume dead */
618  script_dead(i);
619  }
620 #endif /* WIN32 */
621  }
622 }
623 
624 void script_watch(const char *cmd, const guint8 *data_initial, const int data_len, const enum CmdFormat format)
625 {
626  int i;
627  int w;
628  int l, len;
629  const guint8 *data;
630 
631  /* For each script... */
632  for (i = 0; i < num_scripts; ++i) {
633  /* For each watch... */
634  for (w = 0; w < scripts[i].num_watch; ++w) {
635  len = data_len;
636  /* Does this command match our watch? */
637  l = strlen(scripts[i].watch[w]);
638  if (!l || strncmp(cmd, scripts[i].watch[w], l) == 0) {
639  char buf[10240];
640 
641  data = data_initial;
642  if (!len) {
643  snprintf(buf, sizeof(buf), "watch %s\n", cmd);
644  } else
645  switch (format) {
646  case ASCII:
647  snprintf(buf, sizeof(buf), "watch %s %s\n", cmd, data);
648  break;
649 
650  case SHORT_INT:
651  snprintf(buf, sizeof(buf), "watch %s %d %d\n", cmd, GetShort_String(data), GetInt_String(data+2));
652  break;
653 
654  case SHORT_ARRAY: {
655  int be;
656  int p;
657 
658  be = snprintf(buf, sizeof(buf), "watch %s", cmd);
659  for (p = 0; p*2 < len && p < 100; ++p) {
660  be += snprintf(buf+be, sizeof(buf)-be, " %d", GetShort_String(data+p*2));
661  }
662  be += snprintf(buf+be, sizeof(buf)-be, "\n");
663  }
664  break;
665 
666  case INT_ARRAY: {
667  int be;
668  int p;
669 
670  be = snprintf(buf, sizeof(buf), "watch %s", cmd);
671  for (p = 0; p*4 < len; ++p) {
672  be += snprintf(buf+be, sizeof(buf)-be, " %d", GetInt_String(data+p*4));
673  }
674  be += snprintf(buf+be, sizeof(buf)-be, "\n");
675  }
676  break;
677 
678  case STATS: {
679  /*
680  * We cheat here and log each stat as a separate command, even
681  * if the server sent a bunch of updates as a single message;
682  * most scripts will be easier to write if they only parse a fixed
683  * format.
684  */
685  int be = 0;
686  while (len) {
687  int c; /* which stat */
688 
689  be += snprintf(buf+be, sizeof(buf)-be, "watch %s", cmd);
690  c = *data;
691  ++data;
692  --len;
693  if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
694  be += snprintf(buf+be, sizeof(buf)-be, " resists %d %d\n", c, GetShort_String(data));
695  data += 2;
696  len -= 2;
697  } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
698  be += snprintf(buf+be, sizeof(buf)-be, " skill %d %d %" G_GINT64_FORMAT "\n", c, *data, GetInt64_String(data+1));
699  data += 9;
700  len -= 9;
701  } else
702  switch (c) {
703  case CS_STAT_HP:
704  be += snprintf(buf+be, sizeof(buf)-be, " hp %d\n", GetShort_String(data));
705  data += 2;
706  len -= 2;
707  break;
708 
709  case CS_STAT_MAXHP:
710  be += snprintf(buf+be, sizeof(buf)-be, " maxhp %d\n", GetShort_String(data));
711  data += 2;
712  len -= 2;
713  break;
714 
715  case CS_STAT_SP:
716  be += snprintf(buf+be, sizeof(buf)-be, " sp %d\n", GetShort_String(data));
717  data += 2;
718  len -= 2;
719  break;
720 
721  case CS_STAT_MAXSP:
722  be += snprintf(buf+be, sizeof(buf)-be, " maxsp %d\n", GetShort_String(data));
723  data += 2;
724  len -= 2;
725  break;
726 
727  case CS_STAT_GRACE:
728  be += snprintf(buf+be, sizeof(buf)-be, " grace %d\n", GetShort_String(data));
729  data += 2;
730  len -= 2;
731  break;
732 
733  case CS_STAT_MAXGRACE:
734  be += snprintf(buf+be, sizeof(buf)-be, " maxgrace %d\n", GetShort_String(data));
735  data += 2;
736  len -= 2;
737  break;
738 
739  case CS_STAT_STR:
740  be += snprintf(buf+be, sizeof(buf)-be, " str %d\n", GetShort_String(data));
741  data += 2;
742  len -= 2;
743  break;
744 
745  case CS_STAT_INT:
746  be += snprintf(buf+be, sizeof(buf)-be, " int %d\n", GetShort_String(data));
747  data += 2;
748  len -= 2;
749  break;
750 
751  case CS_STAT_POW:
752  be += snprintf(buf+be, sizeof(buf)-be, " pow %d\n", GetShort_String(data));
753  data += 2;
754  len -= 2;
755  break;
756 
757  case CS_STAT_WIS:
758  be += snprintf(buf+be, sizeof(buf)-be, " wis %d\n", GetShort_String(data));
759  data += 2;
760  len -= 2;
761  break;
762 
763  case CS_STAT_DEX:
764  be += snprintf(buf+be, sizeof(buf)-be, " dex %d\n", GetShort_String(data));
765  data += 2;
766  len -= 2;
767  break;
768 
769  case CS_STAT_CON:
770  be += snprintf(buf+be, sizeof(buf)-be, " con %d\n", GetShort_String(data));
771  data += 2;
772  len -= 2;
773  break;
774 
775  case CS_STAT_CHA:
776  be += snprintf(buf+be, sizeof(buf)-be, " cha %d\n", GetShort_String(data));
777  data += 2;
778  len -= 2;
779  break;
780 
781  case CS_STAT_EXP:
782  be += snprintf(buf+be, sizeof(buf)-be, " exp %d\n", GetInt_String(data));
783  data += 4;
784  len -= 4;
785  break;
786 
787  case CS_STAT_EXP64:
788  be += snprintf(buf+be, sizeof(buf)-be, " exp %" G_GINT64_FORMAT "\n", GetInt64_String(data));
789  data += 8;
790  len -= 8;
791  break;
792 
793  case CS_STAT_LEVEL:
794  be += snprintf(buf+be, sizeof(buf)-be, " level %d\n", GetShort_String(data));
795  data += 2;
796  len -= 2;
797  break;
798 
799  case CS_STAT_WC:
800  be += snprintf(buf+be, sizeof(buf)-be, " wc %d\n", GetShort_String(data));
801  data += 2;
802  len -= 2;
803  break;
804 
805  case CS_STAT_AC:
806  be += snprintf(buf+be, sizeof(buf)-be, " ac %d\n", GetShort_String(data));
807  data += 2;
808  len -= 2;
809  break;
810 
811  case CS_STAT_DAM:
812  be += snprintf(buf+be, sizeof(buf)-be, " dam %d\n", GetShort_String(data));
813  data += 2;
814  len -= 2;
815  break;
816 
817  case CS_STAT_ARMOUR:
818  be += snprintf(buf+be, sizeof(buf)-be, " armour %d\n", GetShort_String(data));
819  data += 2;
820  len -= 2;
821  break;
822 
823  case CS_STAT_SPEED:
824  be += snprintf(buf+be, sizeof(buf)-be, " speed %d\n", GetInt_String(data));
825  data += 4;
826  len -= 4;
827  break;
828 
829  case CS_STAT_FOOD:
830  be += snprintf(buf+be, sizeof(buf)-be, " food %d\n", GetShort_String(data));
831  data += 2;
832  len -= 2;
833  break;
834 
835  case CS_STAT_WEAP_SP:
836  be += snprintf(buf+be, sizeof(buf)-be, " weap_sp %d\n", GetInt_String(data));
837  data += 4;
838  len -= 4;
839  break;
840 
841  case CS_STAT_FLAGS:
842  be += snprintf(buf+be, sizeof(buf)-be, " flags %d\n", GetShort_String(data));
843  data += 2;
844  len -= 2;
845  break;
846 
847  case CS_STAT_WEIGHT_LIM:
848  be += snprintf(buf+be, sizeof(buf)-be, " weight_lim %d\n", GetInt_String(data));
849  data += 4;
850  len -= 4;
851  break;
852 
853  case CS_STAT_RANGE: {
854  int rlen = *data;
855  ++data;
856  --len;
857  be += snprintf(buf+be, sizeof(buf)-be, " range %*.*s\n", rlen, rlen, data);
858  data += rlen;
859  len -= rlen;
860  break;
861  }
862 
863  case CS_STAT_TITLE: {
864  int rlen = *data;
865  ++data;
866  --len;
867  be += snprintf(buf+be, sizeof(buf)-be, " title %*.*s\n", rlen, rlen, data);
868  data += rlen;
869  len -= rlen;
870  break;
871  }
872 
873  default:
874  be += snprintf(buf+be, sizeof(buf)-be, " unknown %d %d bytes left\n", c, len);
875  len = 0;
876  }
877  }
878  }
879  break;
880 
881  case MIXED:
882  /* magicmap */
883  /* mapextended */
884  /* item1 item2 */
885  /* upditem */
886  /* image image2 */
887  /* face face1 face2 */
888  /* sound */
889  /* player */
890  /*
891  * If we find that scripts need data from any of the above, we can
892  * write special-case code as with stats. In the meantime, fall
893  * through and just give a hex dump. Script writers should not
894  * depend on that data format.
895  */
896  case NODATA:
897  default: {
898  int be;
899  int p;
900 
901  /*we may receive an null data, in which case len has no meaning*/
902  if (!data) {
903  len = 0;
904  }
905  be = snprintf(buf, sizeof(buf), "watch %s %d bytes unparsed:", cmd, len);
906  for (p = 0; p < len && p < 100; ++p) {
907  be += snprintf(buf+be, sizeof(buf)-be, " %02x", data[p]);
908  }
909  be += snprintf(buf+be, sizeof(buf)-be, "\n");
910  }
911  break;
912  }
913  write(scripts[i].out_fd, buf, strlen(buf));
914  }
915  }
916  }
917 }
918 
919 void script_monitor(const char *command, int repeat, int must_send)
920 {
921  int i;
922 
923  /* For each script... */
924  for (i = 0; i < num_scripts; ++i) {
925  /* Do we send the command? */
926  if (scripts[i].monitor) {
927  char buf[1024];
928 
929  snprintf(buf, sizeof(buf), "monitor %d %d %s\n", repeat, must_send, command);
930  write(scripts[i].out_fd, buf, strlen(buf));
931  }
932  }
933 }
934 
935 void script_monitor_str(const char *command)
936 {
937  int i;
938 
939  /* For each script... */
940  for (i = 0; i < num_scripts; ++i) {
941  /* Do we send the command? */
942  if (scripts[i].monitor) {
943  char buf[1024];
944 
945  snprintf(buf, sizeof(buf), "monitor %s\n", command);
946  write(scripts[i].out_fd, buf, strlen(buf));
947  }
948  }
949 }
950 
951 void script_tell(const char *params)
952 {
953  int i;
954  char *p;
955 
956  if (params == NULL) {
958  "Which script do you want to talk to?");
959  return;
960  }
961 
962  /* Local copy for modifications */
963  char params_cpy[MAX_BUF];
964  snprintf(params_cpy, MAX_BUF-1, "%s", params);
965  p = strchr(params_cpy, ' ');
966  if (p == NULL) {
968  "What do you want to tell the script?");
969  return;
970  }
971  while (*p == ' ') {
972  *p++ = '\0';
973  }
974 
975  /* Find the script */
976  i = script_by_name(params_cpy);
977  if (i < 0) {
979  "No such running script");
980  return;
981  }
982 
983  /* Send the message */
984  write(scripts[i].out_fd, "scripttell ", 11);
985  write(scripts[i].out_fd, p, strlen(p));
986  write(scripts[i].out_fd, "\n", 1);
987 }
988 
989 static int script_by_name(const char *name)
990 {
991  int i;
992  int l;
993 
994  if (name == NULL) {
995  return(num_scripts == 1 ? 0 : -1);
996  }
997 
998  /* Parse script number */
999  if (isdigit(*name)) {
1000  i = atoi(name);
1001  --i;
1002  if (i >= 0 && i < num_scripts) {
1003  return(i);
1004  }
1005  }
1006 
1007  /* Parse script name */
1008  l = 0;
1009  while (name[l] && name[l] != ' ') {
1010  ++l;
1011  }
1012  for (i = 0; i < num_scripts; ++i) {
1013  if (strncmp(name, scripts[i].name, l) == 0) {
1014  return(i);
1015  }
1016  }
1017  return(-1);
1018 }
1019 
1020 static void script_dead(int i)
1021 {
1022  int w;
1023 
1024  /* Release resources */
1025 #ifndef WIN32
1026  close(scripts[i].in_fd);
1027  close(scripts[i].out_fd);
1028 #else
1029  CloseHandle(scripts[i].in_fd);
1030  CloseHandle(scripts[i].out_fd);
1031  CloseHandle(scripts[i].process);
1032 #endif
1033  free(scripts[i].name);
1034  free(scripts[i].params);
1035  for (w = 0; w < scripts[i].num_watch; ++w) {
1036  free(scripts[i].watch[w]);
1037  }
1038  free(scripts[i].watch);
1039 
1040 #ifndef WIN32
1041  waitpid(-1, NULL, WNOHANG);
1042 #endif
1043 
1044  /* Move scripts with higher index numbers down one slot */
1045  if (i < (num_scripts-1)) {
1046  memmove(&scripts[i], &scripts[i+1], sizeof(scripts[i])*(num_scripts-i-1));
1047  }
1048 
1049  /* Update our count */
1050  --num_scripts;
1051 }
1052 
1053 static void send_map(int i, int x, int y)
1054 {
1055  char buf[1024];
1056 
1057  if (x < 0 || y < 0 || the_map.x <= x || the_map.y <= y) {
1058  snprintf(buf, sizeof(buf), "request map %d %d unknown\n", x, y);
1059  write(scripts[i].out_fd, buf, strlen(buf));
1060  }
1061  /*** FIXME *** send more relevant data ***/
1062  snprintf(buf, sizeof(buf), "request map %d %d %d %c %c %c %c"
1063  " smooth %d %d %d heads %d %d %d tails %d %d %d\n",
1064  x, y, the_map.cells[x][y].darkness,
1065  the_map.cells[x][y].need_update ? 'y' : 'n',
1066  the_map.cells[x][y].have_darkness ? 'y' : 'n',
1067  the_map.cells[x][y].need_resmooth ? 'y' : 'n',
1068  the_map.cells[x][y].cleared ? 'y' : 'n',
1069  the_map.cells[x][y].smooth[0], the_map.cells[x][y].smooth[1], the_map.cells[x][y].smooth[2],
1070  the_map.cells[x][y].heads[0].face, the_map.cells[x][y].heads[1].face, the_map.cells[x][y].heads[2].face,
1071  the_map.cells[x][y].tails[0].face, the_map.cells[x][y].tails[1].face, the_map.cells[x][y].tails[2].face
1072  );
1073  write(scripts[i].out_fd, buf, strlen(buf));
1074 }
1075 
1076 static void script_process_cmd(int i)
1077 {
1078  char cmd[1024];
1079  char *c;
1080  int l;
1081 
1082  /*
1083  * Strip out just this one command
1084  */
1085  for (l = 0; l < scripts[i].cmd_count; ++l) {
1086  if (scripts[i].cmd[l] == '\n') {
1087  break;
1088  }
1089  }
1090  ++l;
1091  memcpy(cmd, scripts[i].cmd, l);
1092 #ifndef WIN32
1093  cmd[l-1] = 0;
1094 #else
1095  cmd[l-2] = 0;
1096 #endif
1097  if (l < scripts[i].cmd_count) {
1098  memmove(scripts[i].cmd, scripts[i].cmd+l, scripts[i].cmd_count-l);
1099  scripts[i].cmd_count -= l;
1100  } else {
1101  scripts[i].cmd_count = 0;
1102  }
1103 
1104  /*
1105  * Now the data in scripts[i] is ready for the next read.
1106  * We have a complete command in cmd[].
1107  * Process it.
1108  */
1109  /*
1110  * Script commands
1111  *
1112  * watch <command type>
1113  * unwatch <command type>
1114  * request <data type>
1115  * issue <repeat> <must_send> <command>
1116  * localcmd <command> [<params>]
1117  * draw <color> <text>
1118  * monitor
1119  * unmonitor
1120  */
1121  if (strncmp(cmd, "sync", 4) == 0) {
1122  c = cmd+4;
1123  while (*c && *c != ' ') {
1124  ++c;
1125  }
1126  while (*c == ' ') {
1127  ++c;
1128  }
1129  scripts[i].sync_watch = -1;
1130  if (isdigit(*c)) {
1131  scripts[i].sync_watch = atoi(c);
1132  }
1133  script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */
1134  } else if (strncmp(cmd, "watch", 5) == 0) {
1135  c = cmd+5;
1136  while (*c && *c != ' ') {
1137  ++c;
1138  }
1139  while (*c == ' ') {
1140  ++c;
1141  }
1142  c = g_strdup(c);
1143  scripts[i].watch = g_realloc(scripts[i].watch, (scripts[i].num_watch+1)*sizeof(scripts[i].watch[1]));
1144  scripts[i].watch[scripts[i].num_watch] = c;
1145  ++scripts[i].num_watch;
1146  } else if (strncmp(cmd, "unwatch", 7) == 0) {
1147  int w;
1148 
1149  c = cmd+7;
1150  while (*c && *c != ' ') {
1151  ++c;
1152  }
1153  while (*c == ' ') {
1154  ++c;
1155  }
1156  for (w = 0; w < scripts[i].num_watch; ++w) {
1157  if (strcmp(c, scripts[i].watch[w]) == 0) {
1158  free(scripts[i].watch[w]);
1159  while (w+1 < scripts[i].num_watch) {
1160  scripts[i].watch[w] = scripts[i].watch[w+1];
1161  ++w;
1162  }
1163  --scripts[i].num_watch;
1164  break;
1165  }
1166  }
1167  } else if (strncmp(cmd, "request", 7) == 0) {
1168  c = cmd+7;
1169  while (*c && *c != ' ') {
1170  ++c;
1171  }
1172  while (*c == ' ') {
1173  ++c;
1174  }
1175  if (!*c) {
1176  return; /* bad request */
1177  }
1178  /*
1179  * Request information from the client's view of the world
1180  * (mostly defined in client.h)
1181  *
1182  * Valid requests:
1183  *
1184  * player Return the player's tag and title
1185  * range Return the type and name of the currently selected range attack
1186  * stat <type> Return the specified stats
1187  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1188  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1189  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1190  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1191  * stat resists Return resistances
1192  * stat paths Return spell paths: attuned, repelled, denied.
1193  * weight Return maxweight, weight
1194  * flags Return flags (fire, run)
1195  * items inv Return a list of items in the inventory, one per line
1196  * items actv Return a list of inventory items that are active, one per line
1197  * items on Return a list of items under the player, one per line
1198  * items cont Return a list of items in the open container, one per line
1199  * map pos Return the players x,y within the current map
1200  * map near Return the 3x3 grid of the map centered on the player
1201  * map all Return all the known map information
1202  * map <x> <y> Return the information about square x,y in the current map
1203  * skills Return a list of all skill names, one per line (see also stat xp)
1204  * spells Return a list of known spells, one per line
1205  */
1206  if (strncmp(c, "player", 6) == 0) {
1207  char buf[1024];
1208 
1209  snprintf(buf, sizeof(buf), "request player %d %s\n", cpl.ob->tag, cpl.title);
1210  write(scripts[i].out_fd, buf, strlen(buf));
1211  } else if (strncmp(c, "range", 5) == 0) {
1212  char buf[1024];
1213 
1214  snprintf(buf, sizeof(buf), "request range %s\n", cpl.range);
1215  write(scripts[i].out_fd, buf, strlen(buf));
1216  } else if (strncmp(c, "weight", 5) == 0) {
1217  char buf[1024];
1218 
1219  snprintf(buf, sizeof(buf), "request weight %d %d\n", cpl.stats.weight_limit, (int)(cpl.ob->weight*1000));
1220  write(scripts[i].out_fd, buf, strlen(buf));
1221  } else if (strncmp(c, "stat ", 5) == 0) {
1222  c += 4;
1223  while (*c && *c != ' ') {
1224  ++c;
1225  }
1226  while (*c == ' ') {
1227  ++c;
1228  }
1229  if (!*c) {
1230  return; /* bad request */
1231  }
1232  /*
1233  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1234  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1235  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1236  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1237  * stat resists Return resistances
1238  */
1239  if (strncmp(c, "stats", 5) == 0) {
1240  char buf[1024];
1241 
1242  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);
1243  write(scripts[i].out_fd, buf, strlen(buf));
1244  } else if (strncmp(c, "cmbt", 4) == 0) {
1245  char buf[1024];
1246 
1247  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);
1248  write(scripts[i].out_fd, buf, strlen(buf));
1249  } else if (strncmp(c, "hp", 2) == 0) {
1250  char buf[1024];
1251 
1252  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);
1253  write(scripts[i].out_fd, buf, strlen(buf));
1254  } else if (strncmp(c, "xp", 2) == 0) {
1255  char buf[1024];
1256  int s;
1257 
1258  snprintf(buf, sizeof(buf), "request stat xp %d %" G_GINT64_FORMAT, cpl.stats.level, cpl.stats.exp);
1259  write(scripts[i].out_fd, buf, strlen(buf));
1260  for (s = 0; s < MAX_SKILL; ++s) {
1261  snprintf(buf, sizeof(buf), " %d %" G_GINT64_FORMAT, cpl.stats.skill_level[s], cpl.stats.skill_exp[s]);
1262  write(scripts[i].out_fd, buf, strlen(buf));
1263  }
1264  write(scripts[i].out_fd, "\n", 1);
1265  } else if (strncmp(c, "resists", 7) == 0) {
1266  char buf[1024];
1267  int s;
1268 
1269  snprintf(buf, sizeof(buf), "request stat resists");
1270  write(scripts[i].out_fd, buf, strlen(buf));
1271  for (s = 0; s < 30; ++s) {
1272  snprintf(buf, sizeof(buf), " %d", cpl.stats.resists[s]);
1273  write(scripts[i].out_fd, buf, strlen(buf));
1274  }
1275  write(scripts[i].out_fd, "\n", 1);
1276  } else if (strncmp(c, "paths", 2) == 0) {
1277  char buf[1024];
1278 
1279  snprintf(buf, sizeof(buf), "request stat paths %d %d %d\n", cpl.stats.attuned, cpl.stats.repelled, cpl.stats.denied);
1280  write(scripts[i].out_fd, buf, strlen(buf));
1281  }
1282  } else if (strncmp(c, "flags", 5) == 0) {
1283  char buf[1024];
1284 
1285  snprintf(buf, sizeof(buf), "request flags %d %d %d %d\n", cpl.stats.flags, cpl.fire_on, cpl.run_on, cpl.no_echo);
1286  write(scripts[i].out_fd, buf, strlen(buf));
1287  } else if (strncmp(c, "items ", 6) == 0) {
1288  c += 5;
1289  while (*c && *c != ' ') {
1290  ++c;
1291  }
1292  while (*c == ' ') {
1293  ++c;
1294  }
1295  if (!*c) {
1296  return; /* bad request */
1297  }
1298  /*
1299  * items inv Return a list of items in the inventory, one per line
1300  * items actv Return a list of inventory items that are active, one per line
1301  * items on Return a list of items under the player, one per line
1302  * items cont Return a list of items in the open container, one per line
1303  */
1304  if (strncmp(c, "inv", 3) == 0) {
1305  char *buf;
1306  item *it;
1307 
1308  for (it = cpl.ob->inv; it; it = it->next) {
1309  script_send_item(i, "request items inv ", it);
1310  }
1311  buf = "request items inv end\n";
1312  write(scripts[i].out_fd, buf, strlen(buf));
1313  }
1314  if (strncmp(c, "actv", 4) == 0) {
1315  char *buf;
1316  item *it;
1317 
1318  for (it = cpl.ob->inv; it; it = it->next) {
1319  if (it->applied) {
1320  script_send_item(i, "request items actv ", it);
1321  }
1322  }
1323  buf = "request items actv end\n";
1324  write(scripts[i].out_fd, buf, strlen(buf));
1325  }
1326  if (strncmp(c, "on", 2) == 0) {
1327  char *buf;
1328  item *it;
1329 
1330  for (it = cpl.below->inv; it; it = it->next) {
1331  script_send_item(i, "request items on ", it);
1332  }
1333  buf = "request items on end\n";
1334  write(scripts[i].out_fd, buf, strlen(buf));
1335  }
1336  if (strncmp(c, "cont", 4) == 0) {
1337  char *buf;
1338  item *it;
1339 
1340  if (cpl.container) {
1341  for (it = cpl.container->inv; it; it = it->next) {
1342  script_send_item(i, "request items cont ", it);
1343  }
1344  }
1345  buf = "request items cont end\n";
1346  write(scripts[i].out_fd, buf, strlen(buf));
1347  }
1348  } else if (strncmp(c, "map ", 4) == 0) {
1349  int x, y;
1350 
1351  c += 3;
1352  while (*c && *c != ' ') {
1353  ++c;
1354  }
1355  while (*c == ' ') {
1356  ++c;
1357  }
1358  if (!*c) {
1359  return; /* bad request */
1360  }
1361  /*
1362  * map pos Return the players x,y within the current map
1363  * map near Return the 3x3 grid of the map centered on the player
1364  * map all Return all the known map information
1365  * map <x> <y> Return the information about square x,y in the current map
1366  */
1367  if (strncmp(c, "pos", 3) == 0) {
1368  char buf[1024];
1369 
1370  snprintf(buf, sizeof(buf), "request map pos %d %d\n", pl_pos.x+use_config[CONFIG_MAPWIDTH]/2, pl_pos.y+use_config[CONFIG_MAPHEIGHT]/2);
1371  write(scripts[i].out_fd, buf, strlen(buf));
1372  } else if (strncmp(c, "near", 4) == 0) {
1373  for (y = 0; y < 3; ++y)
1374  for (x = 0; x < 3; ++x)
1375  send_map(i,
1378  );
1379  } else if (strncmp(c, "all", 3) == 0) {
1380  char buf[1024];
1381 
1382  for (y = 0; y < the_map.y; ++y)
1383  for (x = 0; x < the_map.x; ++x) {
1384  send_map(i, x, y);
1385  }
1386  snprintf(buf, sizeof(buf), "request map end\n");
1387  write(scripts[i].out_fd, buf, strlen(buf));
1388  } else {
1389  while (*c && !isdigit(*c)) {
1390  ++c;
1391  }
1392  if (!*c) {
1393  return; /* No x specified */
1394  }
1395  x = atoi(c);
1396  while (*c && *c != ' ') {
1397  ++c;
1398  }
1399  while (*c && !isdigit(*c)) {
1400  ++c;
1401  }
1402  if (!*c) {
1403  return; /* No y specified */
1404  }
1405  y = atoi(c);
1406  send_map(i, x, y);
1407  }
1408  } else if (strncmp(c, "skills", 6) == 0) {
1409  char buf[1024];
1410  int s;
1411 
1412  for (s = 0; s < CS_NUM_SKILLS; s++) {
1413  if (skill_names[s]) {
1414  sprintf(buf, "request skills %d %s\n", CS_STAT_SKILLINFO + s, skill_names[s]);
1415  write(scripts[i].out_fd, buf, strlen(buf));
1416  }
1417  }
1418  sprintf(buf, "request skills end\n");
1419  write(scripts[i].out_fd, buf, strlen(buf));
1420  } else if (strncmp(c, "spells", 6) == 0) {
1421  char buf[1024];
1422  Spell *spell;
1423 
1424  for (spell = cpl.spelldata; spell; spell = spell->next) {
1425  sprintf(buf, "request spells %d %d %d %d %d %d %d %d %s\n",
1426  spell->tag, spell->level, spell->sp, spell->grace,
1427  spell->skill_number, spell->path, spell->time,
1428  spell->dam, spell->name);
1429  write(scripts[i].out_fd, buf, strlen(buf));
1430  }
1431  sprintf(buf, "request spells end\n");
1432  write(scripts[i].out_fd, buf, strlen(buf));
1433  } else {
1434  char buf[1024];
1435 
1436  snprintf(buf, sizeof(buf), "Script %d %s malfunction; unimplemented request:", i+1, scripts[i].name);
1439  }
1440  } else if (strncmp(cmd, "issue", 5) == 0) {
1441  int repeat;
1442  int must_send;
1443 
1444  c = cmd+5;
1445  while (*c && *c == ' ') {
1446  ++c;
1447  }
1448  if (*c && (isdigit(*c) || *c == '-')) { /* repeat specified; use send_command() */
1449  repeat = atoi(c);
1450  while (*c && *c != ' ') {
1451  ++c;
1452  }
1453  while (*c && !isdigit(*c) && *c != '-') {
1454  ++c;
1455  }
1456  if (!*c) {
1457  return; /* No must_send specified */
1458  }
1459  must_send = atoi(c);
1460  while (*c && *c != ' ') {
1461  ++c;
1462  }
1463  if (!*c) {
1464  return; /* No command specified */
1465  }
1466  while (*c == ' ') {
1467  ++c;
1468  }
1469  if (repeat != -1) {
1470  int r;
1471 
1472  r = send_command(c, repeat, must_send);
1473  if (r != 1) {
1474  char buf[1024];
1475 
1476  snprintf(buf, sizeof(buf), "Script %d %s malfunction; command not sent", i+1, scripts[i].name);
1477  draw_ext_info(
1479  draw_ext_info(
1481  }
1482  }
1483  } else {
1484  c = cmd+5;
1485  while (*c && *c != ' ') {
1486  ++c;
1487  }
1488  while (*c == ' ') {
1489  ++c;
1490  }
1491 
1492  /*
1493  * Check special cases: "mark <tag>" or "lock <new state> <tag>"
1494  */
1495  if (strncmp(c, "mark", 4) == 0) {
1496  int tag;
1497  SockList sl;
1498  guint8 buf[MAX_BUF];
1499 
1500  c += 4;
1501 
1502  while (*c && !isdigit(*c)) {
1503  ++c;
1504  }
1505  if (!*c) {
1506  return; /* No tag specified */
1507  }
1508  tag = atoi(c);
1509 
1510  SockList_Init(&sl, buf);
1511  SockList_AddString(&sl, "mark ");
1512  SockList_AddInt(&sl, tag);
1513  SockList_Send(&sl, csocket.fd);
1514  } else if (strncmp(c, "lock", 4) == 0) {
1515  int tag, locked;
1516  SockList sl;
1517  guint8 buf[MAX_BUF];
1518 
1519  c += 4;
1520 
1521  while (*c && !isdigit(*c)) {
1522  ++c;
1523  }
1524  if (!*c) {
1525  return; /* No state specified */
1526  }
1527  locked = atoi(c);
1528  while (*c && *c != ' ') {
1529  ++c;
1530  }
1531  while (*c && !isdigit(*c)) {
1532  ++c;
1533  }
1534  if (!*c) {
1535  return; /* No tag specified */
1536  }
1537  tag = atoi(c);
1538 
1539  SockList_Init(&sl, buf);
1540  SockList_AddString(&sl, "lock ");
1541  SockList_AddChar(&sl, locked);
1542  SockList_AddInt(&sl, tag);
1543  SockList_Send(&sl, csocket.fd);
1544  } else {
1545  cs_print_string(csocket.fd, "%s", c);
1546  }
1547  }
1548  } else if (strncmp(cmd, "localcmd", 8) == 0) {
1549  char *param;
1550 
1551  c = cmd+8;
1552  while (*c == ' ') {
1553  c++;
1554  }
1555  param = c;
1556  while ((*param != '\0') && (*param != ' ')) {
1557  param++;
1558  }
1559  if (*param == ' ') {
1560  *param = '\0';
1561  param++;
1562  } else {
1563  param = NULL;
1564  }
1565 
1566  if (!handle_local_command(c, param)) {
1567  char buf[1024];
1568 
1569  snprintf(buf, sizeof(buf), "Script %s malfunction; localcmd not understood", scripts[i].name);
1571  snprintf(buf, sizeof(buf), "Script <<localcmd %s %s>>", c, (param == NULL) ? "" : param);
1573  }
1574  } else if (strncmp(cmd, "draw", 4) == 0) {
1575  int color;
1576 
1577  c = cmd+4;
1578  while (*c && !isdigit(*c)) {
1579  ++c;
1580  }
1581  if (!*c) {
1582  return; /* No color specified */
1583  }
1584  color = atoi(c);
1585  while (*c && *c != ' ') {
1586  ++c;
1587  }
1588  if (!*c) {
1589  return; /* No message specified */
1590  }
1591  while (*c == ' ') {
1592  ++c;
1593  }
1595  } else if (strncmp(cmd, "monitor", 7) == 0) {
1596  scripts[i].monitor = 1;
1597  } else if (strncmp(cmd, "unmonitor", 9) == 0) {
1598  scripts[i].monitor = 0;
1599  } else {
1600  char buf[1024];
1601 
1602  snprintf(buf, sizeof(buf), "Script %d %s malfunction; invalid command:", i+1, scripts[i].name);
1605  }
1606 }
1607 
1608 /*
1609  * script_send_item()
1610  *
1611  * Send one line to the script with item information.
1612  *
1613  * A header string is passed in. The format is:
1614  *
1615  * <header> tag num weight flags type name
1616  *
1617  * flags are a bitmask:
1618  * unidentified, magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated
1619  * 512 256 128 64 32 16 8 4 2 1
1620  */
1621 static void script_send_item(int i, const char *head, const item *it)
1622 {
1623  char buf[4096];
1624  int flags;
1625 
1626  flags = it->flagsval&F_UNIDENTIFIED?1:0;
1627  flags = (flags<<1)|it->magical;
1628  flags = (flags<<1)|it->cursed;
1629  flags = (flags<<1)|it->damned;
1630  flags = (flags<<1)|it->unpaid;
1631  flags = (flags<<1)|it->locked;
1632  flags = (flags<<1)|it->applied;
1633  flags = (flags<<1)|it->open;
1634  flags = (flags<<1)|it->was_open;
1635  flags = (flags<<1)|it->inv_updated;
1636  snprintf(buf, sizeof(buf), "%s%d %d %d %d %d %s\n", head, it->tag, it->nrof, (int)(it->weight*1000+0.5), flags, it->type, it->d_name);
1637  write(scripts[i].out_fd, buf, strlen(buf));
1638 }
1639 
1640 #endif /* CPROTO */
float weight
Definition: item.h:61
#define CS_STAT_AC
Definition: newclient.h:128
gint16 grace
Spell points for using prayers.
Definition: client.h:239
gint8 wc
Weapon Class.
Definition: client.h:232
int maxfd
Definition: client.c:59
static void script_process_cmd(int i)
Definition: script.c:1076
int y
Definition: mapdata.h:97
gint32 weapon_sp
Weapon speed (displayed in client as a float)
Definition: client.h:249
int handle_local_command(const char *cp, const char *cpnext)
Tries to handle command cp (with optional params in cpnext, which may be null) as a local command...
Definition: p_cmd.c:1053
guint8 have_darkness
Definition: mapdata.h:86
guint16 level
The casting level of the spell.
Definition: client.h:274
char * params
Definition: script.c:126
gint16 use_config[CONFIG_NUMS]
Definition: init.c:39
#define CS_STAT_INT
Definition: newclient.h:120
Definition: script.c:124
gint64 skill_exp[MAX_SKILL]
Experience points for skills.
Definition: client.h:263
void script_monitor_str(const char *command)
Definition: script.c:935
Definition: script.h:49
guint16 inv_updated
Definition: item.h:75
#define MAX_SKILL
How many skill types server supports/client will get sent to it.
Definition: client.h:48
gint16 maxsp
Maximum spell points.
Definition: client.h:238
item * ob
Player object.
Definition: client.h:309
short GetShort_String(const unsigned char *data)
Definition: newsocket.c:211
guint16 dam
Damage done by spell though the meaning is spell dependent and actual damage may depend on how the sp...
Definition: client.h:278
#define CS_STAT_SPEED
Definition: newclient.h:131
guint16 sp
Mana per cast; may be zero.
Definition: client.h:276
gint32 speed
Speed (is displayed as a float)
Definition: client.h:248
#define CS_STAT_DEX
Definition: newclient.h:122
guint16 smooth[MAXLAYERS]
Definition: mapdata.h:83
int SockList_Send(SockList *sl, int fd)
Send data from a socklist to the socket.
Definition: newsocket.c:157
struct item_struct * next
Definition: item.h:51
ClientSocket csocket
Definition: client.c:71
int num_watch
Definition: script.c:135
#define MSG_TYPE_CLIENT_SCRIPT
Script related messages.
Definition: newclient.h:691
static void script_dead(int i)
Definition: script.c:1020
#define MSG_TYPE_CLIENT
Client originated Messages.
Definition: newclient.h:420
struct MapCellLayer tails[MAXLAYERS]
Definition: mapdata.h:82
guint16 time
Casting time in server ticks.
Definition: client.h:275
Stats stats
Player stats.
Definition: client.h:322
static int script_by_name(const char *name)
Definition: script.c:989
static void send_map(int i, int x, int y)
Definition: script.c:1053
int monitor
Definition: script.c:134
int sync_watch
Definition: script.c:145
Contains external calls that the common area makes callbacks to.
#define CS_STAT_MAXSP
Definition: newclient.h:118
#define CS_STAT_WIS
Definition: newclient.h:121
#define NDI_BLACK
Definition: newclient.h:249
gint8 Con
Constitution.
Definition: client.h:227
#define CS_STAT_FLAGS
Definition: newclient.h:139
gint32 tag
Definition: item.h:59
guint32 nrof
Definition: item.h:60
void script_watch(const char *cmd, const guint8 *data_initial, const int data_len, const enum CmdFormat format)
Definition: script.c:624
guint16 locked
Definition: item.h:71
char * skill_names[MAX_SKILL]
Definition: client.c:51
void script_sync(int commdiff)
Definition: script.c:480
#define CS_STAT_WEIGHT_LIM
Definition: newclient.h:140
static void script_send_item(int i, const char *head, const item *it)
Definition: script.c:1621
guint32 path
The bitmask of paths this spell belongs to.
Definition: client.h:288
#define CS_STAT_EXP64
Definition: newclient.h:141
guint16 type
Definition: item.h:80
int in_fd
Definition: script.c:129
#define CS_STAT_WC
Definition: newclient.h:127
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
void script_init(const char *cparams)
Definition: script.c:209
#define NDI_RED
Definition: newclient.h:252
guint32 repelled
Spell paths to which the player is repelled.
Definition: client.h:255
guint16 grace
Grace per cast; may be zero.
Definition: client.h:277
gint8 ac
Armour Class.
Definition: client.h:233
int pid
Definition: script.c:140
static struct script * scripts
Definition: script.c:151
Includes and prototypes for p_cmd.c for player-commands like '/magicmap'.
guint8 cleared
Definition: mapdata.h:88
gint8 Str
Strength.
Definition: client.h:225
int cs_print_string(int fd, const char *str,...)
Send a printf-formatted packet to the socket.
Definition: newsocket.c:334
void SockList_AddInt(SockList *sl, guint32 data)
Definition: newsocket.c:122
int send_command(const char *command, int repeat, int must_send)
Definition: player.c:200
#define CS_STAT_RESIST_END
End of resistances (inclusive)
Definition: newclient.h:170
#define CS_STAT_LEVEL
Definition: newclient.h:126
gint8 Wis
Wisdom.
Definition: client.h:228
char d_name[NAME_LEN]
Definition: item.h:55
#define CS_STAT_WEAP_SP
Definition: newclient.h:133
item * below
Items below the player (pl.below->inv)
Definition: client.h:310
gint8 level
Experience level.
Definition: client.h:234
CmdFormat
Definition: script.h:43
int out_fd
Definition: script.c:128
Client_Player cpl
Player object.
Definition: client.c:70
item * container
open container
Definition: client.h:312
guint16 was_open
Definition: item.h:74
#define CONFIG_MAPHEIGHT
Definition: client.h:177
struct Map the_map
Definition: mapdata.c:115
struct MapCell ** cells
Definition: mapdata.h:99
guint8 need_resmooth
Definition: mapdata.h:87
guint32 flagsval
Definition: item.h:79
int fd
Definition: client.h:91
gint8 Pow
Power.
Definition: client.h:231
void SockList_AddString(SockList *sl, const char *str)
Definition: newsocket.c:140
int x
Definition: mapdata.h:96
guint8 skill_number
The index in the skill arrays, plus CS_STAT_SKILLINFO.
Definition: client.h:282
gint16 food
Quantity food in stomach.
Definition: client.h:242
static int num_scripts
Definition: script.c:153
#define CS_STAT_CHA
Definition: newclient.h:124
gint8 Dex
Dexterity.
Definition: client.h:226
guint32 tag
Unique ID number for a spell so updspell etc can operate on it.
Definition: client.h:272
gint64 GetInt64_String(const unsigned char *data)
The reverse of SockList_AddInt, but on strings instead.
Definition: newsocket.c:193
#define CS_STAT_STR
Definition: newclient.h:119
int cmd_count
Definition: script.c:137
#define CS_STAT_FOOD
Definition: newclient.h:132
guint16 unpaid
Definition: item.h:70
gint16 resists[30]
Resistant values.
Definition: client.h:260
#define CS_STAT_EXP
No longer used.
Definition: newclient.h:125
struct Spell_struct * next
Definition: client.h:268
guint16 flags
Contains fire on/run on flags.
Definition: client.h:259
gint64 exp
Experience.
Definition: client.h:241
#define MAX_BUF
Definition: client-types.h:43
void script_list(void)
Definition: script.c:498
gint16 skill_level[MAX_SKILL]
Level of known skills.
Definition: client.h:262
guint32 weight_limit
Carrying weight limit.
Definition: client.h:264
Definition: script.h:48
#define F_UNIDENTIFIED
Definition: newclient.h:287
void SockList_AddChar(SockList *sl, char c)
Definition: newsocket.c:85
guint16 open
Definition: item.h:73
Spell * spelldata
List of spells known.
Definition: client.h:323
gint16 dam
How much damage this object does for each hit.
Definition: client.h:245
guint32 run_on
True if run key is on.
Definition: client.h:328
char name[256]
One length byte plus data.
Definition: client.h:269
guint8 need_update
Definition: mapdata.h:85
Definition: script.h:44
#define CS_STAT_SP
Definition: newclient.h:117
char ** watch
Definition: script.c:136
void script_tell(const char *params)
Definition: script.c:951
gint8 Int
Intelligence.
Definition: client.h:230
void script_kill(const char *params)
Definition: script.c:520
gint16 sp
Spell points for casting spells.
Definition: client.h:237
void script_fdset(int *maxfd, fd_set *set)
Definition: script.c:551
void draw_ext_info(int orig_color, int type, int subtype, const char *message)
A message processor that accepts messages along with meta information color and type.
Definition: info.c:934
char cmd[1024]
Definition: script.c:138
#define CS_STAT_ARMOUR
Definition: newclient.h:130
void SockList_Init(SockList *sl, guint8 *buf)
Definition: newsocket.c:74
int command_sent
Definition: client.h:95
#define CS_STAT_DAM
Definition: newclient.h:129
#define CS_NUM_SKILLS
CS_NUM_SKILLS does not match how many skills there really are - instead, it is used as a range of val...
Definition: newclient.h:203
guint16 damned
Definition: item.h:69
#define CS_STAT_TITLE
Definition: newclient.h:135
char range[MAX_BUF]
Range attack chosen.
Definition: client.h:325
guint16 cursed
Definition: item.h:68
int command_received
These are used for the newer 'windowing' method of commands - number of last command sent...
Definition: client.h:95
static const char format[]
cfsndserv recognizes sound commands by seeing the numeric parameters at the beginning of the command ...
Definition: sound.c:40
guint16 magical
Definition: item.h:67
#define CS_STAT_HP
Definition: newclient.h:115
gint16 maxhp
Maximum hit points.
Definition: client.h:236
char title[MAX_BUF]
Title of character.
Definition: client.h:324
static int data_len
Definition: png.c:50
guint16 applied
Definition: item.h:72
Includes various dependencies header files needed by most everything.
char * name
Definition: image.c:56
#define CS_STAT_RESIST_START
Start of resistances (inclusive)
Definition: newclient.h:169
gint16 hp
Hit Points.
Definition: client.h:235
#define CS_STAT_POW
Definition: newclient.h:136
char * name
Definition: script.c:125
Definition: script.h:50
gint16 face
Definition: mapdata.h:49
struct MapCellLayer heads[MAXLAYERS]
Definition: mapdata.h:81
guint32 no_echo
If TRUE, don't echo keystrokes.
Definition: client.h:331
#define CS_STAT_RANGE
Definition: newclient.h:134
struct item_struct * inv
Definition: item.h:54
#define CS_STAT_MAXHP
Definition: newclient.h:116
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:705
gint8 Cha
Charisma.
Definition: client.h:229
#define CS_STAT_CON
Definition: newclient.h:123
void script_process(fd_set *set)
Definition: script.c:565
int GetInt_String(const unsigned char *data)
The reverse of SockList_AddInt, but on strings instead.
Definition: newsocket.c:181
#define CS_STAT_MAXGRACE
Definition: newclient.h:138
guint32 fire_on
True if fire key is pressed.
Definition: client.h:327
#define CS_STAT_SKILLINFO
CS_STAT_SKILLINFO is used as the starting index point.
Definition: newclient.h:196
guint8 darkness
Definition: mapdata.h:84
PlayerPosition pl_pos
Definition: map.c:55
guint32 attuned
Spell paths to which the player is attuned.
Definition: client.h:252
void script_monitor(const char *command, int repeat, int must_send)
Definition: script.c:919
#define CS_STAT_GRACE
Definition: newclient.h:137
#define CONFIG_MAPWIDTH
Definition: client.h:176
gint16 maxgrace
Maximum spell points.
Definition: client.h:240
guint32 denied
Spell paths denied to the player.
Definition: client.h:258