Crossfire Client, Trunk  R19733
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 (!mapdata_contains(x, 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, mapdata_cell(x, y)->darkness,
1065  mapdata_cell(x, y)->need_update ? 'y' : 'n',
1066  mapdata_cell(x, y)->have_darkness ? 'y' : 'n',
1067  mapdata_cell(x, y)->need_resmooth ? 'y' : 'n',
1068  mapdata_cell(x, y)->cleared ? 'y' : 'n',
1069  mapdata_cell(x, y)->smooth[0], mapdata_cell(x, y)->smooth[1], mapdata_cell(x, y)->smooth[2],
1070  mapdata_cell(x, y)->heads[0].face, mapdata_cell(x, y)->heads[1].face, mapdata_cell(x, y)->heads[2].face,
1071  mapdata_cell(x, y)->tails[0].face, mapdata_cell(x, y)->tails[1].face, mapdata_cell(x, y)->tails[2].face
1072  );
1073  write(scripts[i].out_fd, buf, strlen(buf));
1074 }
1075 
1081 static void script_process_cmd(int i) {
1082  char cmd[1024];
1083  char *c;
1084 
1085  // Find the length of the command up to the trailing newline.
1086  int l = strcspn(scripts[i].cmd, "\n") + 1;
1087 
1088  // Copy a single command up until the newline into a buffer.
1089  g_strlcpy(cmd, scripts[i].cmd, l);
1090 
1091  // If a carriage return is present, trim it out as well.
1092  char *cr = strchr(cmd, '\r');
1093  if (cr != NULL) {
1094  *cr = '\0';
1095  }
1096 
1097  // Remove a single command from the script command buffer.
1098  if (l < scripts[i].cmd_count) {
1099  memmove(scripts[i].cmd, scripts[i].cmd + l, scripts[i].cmd_count - l);
1100  scripts[i].cmd_count -= l;
1101  } else {
1102  scripts[i].cmd_count = 0;
1103  }
1104 
1105  /*
1106  * Now the data in scripts[i] is ready for the next read.
1107  * We have a complete command in cmd[].
1108  * Process it.
1109  */
1110  /*
1111  * Script commands
1112  *
1113  * watch <command type>
1114  * unwatch <command type>
1115  * request <data type>
1116  * issue <repeat> <must_send> <command>
1117  * localcmd <command> [<params>]
1118  * draw <color> <text>
1119  * monitor
1120  * unmonitor
1121  */
1122  if (strncmp(cmd, "sync", 4) == 0) {
1123  c = cmd+4;
1124  while (*c && *c != ' ') {
1125  ++c;
1126  }
1127  while (*c == ' ') {
1128  ++c;
1129  }
1130  scripts[i].sync_watch = -1;
1131  if (isdigit(*c)) {
1132  scripts[i].sync_watch = atoi(c);
1133  }
1134  script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */
1135  } else if (strncmp(cmd, "watch", 5) == 0) {
1136  c = cmd+5;
1137  while (*c && *c != ' ') {
1138  ++c;
1139  }
1140  while (*c == ' ') {
1141  ++c;
1142  }
1143  c = g_strdup(c);
1144  scripts[i].watch = g_realloc(scripts[i].watch, (scripts[i].num_watch+1)*sizeof(scripts[i].watch[1]));
1145  scripts[i].watch[scripts[i].num_watch] = c;
1146  ++scripts[i].num_watch;
1147  } else if (strncmp(cmd, "unwatch", 7) == 0) {
1148  int w;
1149 
1150  c = cmd+7;
1151  while (*c && *c != ' ') {
1152  ++c;
1153  }
1154  while (*c == ' ') {
1155  ++c;
1156  }
1157  for (w = 0; w < scripts[i].num_watch; ++w) {
1158  if (strcmp(c, scripts[i].watch[w]) == 0) {
1159  free(scripts[i].watch[w]);
1160  while (w+1 < scripts[i].num_watch) {
1161  scripts[i].watch[w] = scripts[i].watch[w+1];
1162  ++w;
1163  }
1164  --scripts[i].num_watch;
1165  break;
1166  }
1167  }
1168  } else if (strncmp(cmd, "request", 7) == 0) {
1169  c = cmd+7;
1170  while (*c && *c != ' ') {
1171  ++c;
1172  }
1173  while (*c == ' ') {
1174  ++c;
1175  }
1176  if (!*c) {
1177  return; /* bad request */
1178  }
1179  /*
1180  * Request information from the client's view of the world
1181  * (mostly defined in client.h)
1182  *
1183  * Valid requests:
1184  *
1185  * player Return the player's tag and title
1186  * range Return the type and name of the currently selected range attack
1187  * stat <type> Return the specified stats
1188  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1189  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1190  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1191  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1192  * stat resists Return resistances
1193  * stat paths Return spell paths: attuned, repelled, denied.
1194  * weight Return maxweight, weight
1195  * flags Return flags (fire, run)
1196  * items inv Return a list of items in the inventory, one per line
1197  * items actv Return a list of inventory items that are active, one per line
1198  * items on Return a list of items under the player, one per line
1199  * items cont Return a list of items in the open container, one per line
1200  * map pos Return the players x,y within the current map
1201  * map near Return the 3x3 grid of the map centered on the player
1202  * map all Return all the known map information
1203  * map <x> <y> Return the information about square x,y in the current map
1204  * skills Return a list of all skill names, one per line (see also stat xp)
1205  * spells Return a list of known spells, one per line
1206  */
1207  if (strncmp(c, "player", 6) == 0) {
1208  char buf[1024];
1209 
1210  snprintf(buf, sizeof(buf), "request player %d %s\n", cpl.ob->tag, cpl.title);
1211  write(scripts[i].out_fd, buf, strlen(buf));
1212  } else if (strncmp(c, "range", 5) == 0) {
1213  char buf[1024];
1214 
1215  snprintf(buf, sizeof(buf), "request range %s\n", cpl.range);
1216  write(scripts[i].out_fd, buf, strlen(buf));
1217  } else if (strncmp(c, "weight", 5) == 0) {
1218  char buf[1024];
1219 
1220  snprintf(buf, sizeof(buf), "request weight %d %d\n", cpl.stats.weight_limit, (int)(cpl.ob->weight*1000));
1221  write(scripts[i].out_fd, buf, strlen(buf));
1222  } else if (strncmp(c, "stat ", 5) == 0) {
1223  c += 4;
1224  while (*c && *c != ' ') {
1225  ++c;
1226  }
1227  while (*c == ' ') {
1228  ++c;
1229  }
1230  if (!*c) {
1231  return; /* bad request */
1232  }
1233  /*
1234  * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha
1235  * stat cmbt Return wc,ac,dam,speed,weapon_sp
1236  * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food
1237  * stat xp Return level,xp,skill-1 level,skill-1 xp,...
1238  * stat resists Return resistances
1239  */
1240  if (strncmp(c, "stats", 5) == 0) {
1241  char buf[1024];
1242 
1243  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);
1244  write(scripts[i].out_fd, buf, strlen(buf));
1245  } else if (strncmp(c, "cmbt", 4) == 0) {
1246  char buf[1024];
1247 
1248  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);
1249  write(scripts[i].out_fd, buf, strlen(buf));
1250  } else if (strncmp(c, "hp", 2) == 0) {
1251  char buf[1024];
1252 
1253  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);
1254  write(scripts[i].out_fd, buf, strlen(buf));
1255  } else if (strncmp(c, "xp", 2) == 0) {
1256  char buf[1024];
1257  int s;
1258 
1259  snprintf(buf, sizeof(buf), "request stat xp %d %" G_GINT64_FORMAT, cpl.stats.level, cpl.stats.exp);
1260  write(scripts[i].out_fd, buf, strlen(buf));
1261  for (s = 0; s < MAX_SKILL; ++s) {
1262  snprintf(buf, sizeof(buf), " %d %" G_GINT64_FORMAT, cpl.stats.skill_level[s], cpl.stats.skill_exp[s]);
1263  write(scripts[i].out_fd, buf, strlen(buf));
1264  }
1265  write(scripts[i].out_fd, "\n", 1);
1266  } else if (strncmp(c, "resists", 7) == 0) {
1267  char buf[1024];
1268  int s;
1269 
1270  snprintf(buf, sizeof(buf), "request stat resists");
1271  write(scripts[i].out_fd, buf, strlen(buf));
1272  for (s = 0; s < 30; ++s) {
1273  snprintf(buf, sizeof(buf), " %d", cpl.stats.resists[s]);
1274  write(scripts[i].out_fd, buf, strlen(buf));
1275  }
1276  write(scripts[i].out_fd, "\n", 1);
1277  } else if (strncmp(c, "paths", 2) == 0) {
1278  char buf[1024];
1279 
1280  snprintf(buf, sizeof(buf), "request stat paths %d %d %d\n", cpl.stats.attuned, cpl.stats.repelled, cpl.stats.denied);
1281  write(scripts[i].out_fd, buf, strlen(buf));
1282  }
1283  } else if (strncmp(c, "flags", 5) == 0) {
1284  char buf[1024];
1285 
1286  snprintf(buf, sizeof(buf), "request flags %d %d %d %d\n", cpl.stats.flags, cpl.fire_on, cpl.run_on, cpl.no_echo);
1287  write(scripts[i].out_fd, buf, strlen(buf));
1288  } else if (strncmp(c, "items ", 6) == 0) {
1289  c += 5;
1290  while (*c && *c != ' ') {
1291  ++c;
1292  }
1293  while (*c == ' ') {
1294  ++c;
1295  }
1296  if (!*c) {
1297  return; /* bad request */
1298  }
1299  /*
1300  * items inv Return a list of items in the inventory, one per line
1301  * items actv Return a list of inventory items that are active, one per line
1302  * items on Return a list of items under the player, one per line
1303  * items cont Return a list of items in the open container, one per line
1304  */
1305  if (strncmp(c, "inv", 3) == 0) {
1306  char *buf;
1307  item *it;
1308 
1309  for (it = cpl.ob->inv; it; it = it->next) {
1310  script_send_item(i, "request items inv ", it);
1311  }
1312  buf = "request items inv end\n";
1313  write(scripts[i].out_fd, buf, strlen(buf));
1314  }
1315  if (strncmp(c, "actv", 4) == 0) {
1316  char *buf;
1317  item *it;
1318 
1319  for (it = cpl.ob->inv; it; it = it->next) {
1320  if (it->applied) {
1321  script_send_item(i, "request items actv ", it);
1322  }
1323  }
1324  buf = "request items actv end\n";
1325  write(scripts[i].out_fd, buf, strlen(buf));
1326  }
1327  if (strncmp(c, "on", 2) == 0) {
1328  char *buf;
1329  item *it;
1330 
1331  for (it = cpl.below->inv; it; it = it->next) {
1332  script_send_item(i, "request items on ", it);
1333  }
1334  buf = "request items on end\n";
1335  write(scripts[i].out_fd, buf, strlen(buf));
1336  }
1337  if (strncmp(c, "cont", 4) == 0) {
1338  char *buf;
1339  item *it;
1340 
1341  if (cpl.container) {
1342  for (it = cpl.container->inv; it; it = it->next) {
1343  script_send_item(i, "request items cont ", it);
1344  }
1345  }
1346  buf = "request items cont end\n";
1347  write(scripts[i].out_fd, buf, strlen(buf));
1348  }
1349  } else if (strncmp(c, "map ", 4) == 0) {
1350  int x, y;
1351 
1352  c += 3;
1353  while (*c && *c != ' ') {
1354  ++c;
1355  }
1356  while (*c == ' ') {
1357  ++c;
1358  }
1359  if (!*c) {
1360  return; /* bad request */
1361  }
1362  /*
1363  * map pos Return the players x,y within the current map
1364  * map near Return the 3x3 grid of the map centered on the player
1365  * map all Return all the known map information
1366  * map <x> <y> Return the information about square x,y in the current map
1367  */
1368  if (strncmp(c, "pos", 3) == 0) {
1369  char buf[1024];
1370 
1371  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);
1372  write(scripts[i].out_fd, buf, strlen(buf));
1373  } else if (strncmp(c, "near", 4) == 0) {
1374  for (y = 0; y < 3; ++y)
1375  for (x = 0; x < 3; ++x)
1376  send_map(i,
1379  );
1380  } else if (strncmp(c, "all", 3) == 0) {
1381  char *endmsg = "request map end\n";
1382  int sizex, sizey;
1383 
1384  mapdata_size(&sizex, &sizey);
1385 
1386  for (y = 0; y < sizey; y++) {
1387  for (x = 0; x < sizex; x++) {
1388  send_map(i, x, y);
1389  }
1390  }
1391 
1392  write(scripts[i].out_fd, endmsg, strlen(endmsg));
1393  } else {
1394  while (*c && !isdigit(*c)) {
1395  ++c;
1396  }
1397  if (!*c) {
1398  return; /* No x specified */
1399  }
1400  x = atoi(c);
1401  while (*c && *c != ' ') {
1402  ++c;
1403  }
1404  while (*c && !isdigit(*c)) {
1405  ++c;
1406  }
1407  if (!*c) {
1408  return; /* No y specified */
1409  }
1410  y = atoi(c);
1411  send_map(i, x, y);
1412  }
1413  } else if (strncmp(c, "skills", 6) == 0) {
1414  char buf[1024];
1415  int s;
1416 
1417  for (s = 0; s < CS_NUM_SKILLS; s++) {
1418  if (skill_names[s]) {
1419  sprintf(buf, "request skills %d %s\n", CS_STAT_SKILLINFO + s, skill_names[s]);
1420  write(scripts[i].out_fd, buf, strlen(buf));
1421  }
1422  }
1423  sprintf(buf, "request skills end\n");
1424  write(scripts[i].out_fd, buf, strlen(buf));
1425  } else if (strncmp(c, "spells", 6) == 0) {
1426  char buf[1024];
1427  Spell *spell;
1428 
1429  for (spell = cpl.spelldata; spell; spell = spell->next) {
1430  sprintf(buf, "request spells %d %d %d %d %d %d %d %d %s\n",
1431  spell->tag, spell->level, spell->sp, spell->grace,
1432  spell->skill_number, spell->path, spell->time,
1433  spell->dam, spell->name);
1434  write(scripts[i].out_fd, buf, strlen(buf));
1435  }
1436  sprintf(buf, "request spells end\n");
1437  write(scripts[i].out_fd, buf, strlen(buf));
1438  } else {
1439  char buf[1024];
1440 
1441  snprintf(buf, sizeof(buf), "Script %d %s malfunction; unimplemented request:", i+1, scripts[i].name);
1444  }
1445  } else if (strncmp(cmd, "issue", 5) == 0) {
1446  int repeat;
1447  int must_send;
1448 
1449  c = cmd+5;
1450  while (*c && *c == ' ') {
1451  ++c;
1452  }
1453  if (*c && (isdigit(*c) || *c == '-')) { /* repeat specified; use send_command() */
1454  repeat = atoi(c);
1455  while (*c && *c != ' ') {
1456  ++c;
1457  }
1458  while (*c && !isdigit(*c) && *c != '-') {
1459  ++c;
1460  }
1461  if (!*c) {
1462  return; /* No must_send specified */
1463  }
1464  must_send = atoi(c);
1465  while (*c && *c != ' ') {
1466  ++c;
1467  }
1468  if (!*c) {
1469  return; /* No command specified */
1470  }
1471  while (*c == ' ') {
1472  ++c;
1473  }
1474  if (repeat != -1) {
1475  int r;
1476 
1477  r = send_command(c, repeat, must_send);
1478  if (r != 1) {
1479  char buf[1024];
1480 
1481  snprintf(buf, sizeof(buf), "Script %d %s malfunction; command not sent", i+1, scripts[i].name);
1482  draw_ext_info(
1484  draw_ext_info(
1486  }
1487  }
1488  } else {
1489  c = cmd+5;
1490  while (*c && *c != ' ') {
1491  ++c;
1492  }
1493  while (*c == ' ') {
1494  ++c;
1495  }
1496 
1497  /*
1498  * Check special cases: "mark <tag>" or "lock <new state> <tag>"
1499  */
1500  if (strncmp(c, "mark", 4) == 0) {
1501  int tag;
1502  SockList sl;
1503  guint8 buf[MAX_BUF];
1504 
1505  c += 4;
1506 
1507  while (*c && !isdigit(*c)) {
1508  ++c;
1509  }
1510  if (!*c) {
1511  return; /* No tag specified */
1512  }
1513  tag = atoi(c);
1514 
1515  SockList_Init(&sl, buf);
1516  SockList_AddString(&sl, "mark ");
1517  SockList_AddInt(&sl, tag);
1518  SockList_Send(&sl, csocket.fd);
1519  } else if (strncmp(c, "lock", 4) == 0) {
1520  int tag, locked;
1521  SockList sl;
1522  guint8 buf[MAX_BUF];
1523 
1524  c += 4;
1525 
1526  while (*c && !isdigit(*c)) {
1527  ++c;
1528  }
1529  if (!*c) {
1530  return; /* No state specified */
1531  }
1532  locked = atoi(c);
1533  while (*c && *c != ' ') {
1534  ++c;
1535  }
1536  while (*c && !isdigit(*c)) {
1537  ++c;
1538  }
1539  if (!*c) {
1540  return; /* No tag specified */
1541  }
1542  tag = atoi(c);
1543 
1544  SockList_Init(&sl, buf);
1545  SockList_AddString(&sl, "lock ");
1546  SockList_AddChar(&sl, locked);
1547  SockList_AddInt(&sl, tag);
1548  SockList_Send(&sl, csocket.fd);
1549  } else {
1550  cs_print_string(csocket.fd, "%s", c);
1551  }
1552  }
1553  } else if (strncmp(cmd, "localcmd", 8) == 0) {
1554  char *param;
1555 
1556  c = cmd+8;
1557  while (*c == ' ') {
1558  c++;
1559  }
1560  param = c;
1561  while ((*param != '\0') && (*param != ' ')) {
1562  param++;
1563  }
1564  if (*param == ' ') {
1565  *param = '\0';
1566  param++;
1567  } else {
1568  param = NULL;
1569  }
1570 
1571  if (!handle_local_command(c, param)) {
1572  char buf[1024];
1573 
1574  snprintf(buf, sizeof(buf), "Script %s malfunction; localcmd not understood", scripts[i].name);
1576  snprintf(buf, sizeof(buf), "Script <<localcmd %s %s>>", c, (param == NULL) ? "" : param);
1578  }
1579  } else if (strncmp(cmd, "draw", 4) == 0) {
1580  int color;
1581 
1582  c = cmd+4;
1583  while (*c && !isdigit(*c)) {
1584  ++c;
1585  }
1586  if (!*c) {
1587  return; /* No color specified */
1588  }
1589  color = atoi(c);
1590  while (*c && *c != ' ') {
1591  ++c;
1592  }
1593  if (!*c) {
1594  return; /* No message specified */
1595  }
1596  while (*c == ' ') {
1597  ++c;
1598  }
1600  } else if (strncmp(cmd, "monitor", 7) == 0) {
1601  scripts[i].monitor = 1;
1602  } else if (strncmp(cmd, "unmonitor", 9) == 0) {
1603  scripts[i].monitor = 0;
1604  } else {
1605  char buf[1024];
1606 
1607  snprintf(buf, sizeof(buf), "Script %d %s malfunction; invalid command:", i+1, scripts[i].name);
1610  }
1611 }
1612 
1613 /*
1614  * script_send_item()
1615  *
1616  * Send one line to the script with item information.
1617  *
1618  * A header string is passed in. The format is:
1619  *
1620  * <header> tag num weight flags type name
1621  *
1622  * flags are a bitmask:
1623  * unidentified, magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated
1624  * 512 256 128 64 32 16 8 4 2 1
1625  */
1626 static void script_send_item(int i, const char *head, const item *it)
1627 {
1628  char buf[4096];
1629  int flags;
1630 
1631  flags = it->flagsval&F_UNIDENTIFIED?1:0;
1632  flags = (flags<<1)|it->magical;
1633  flags = (flags<<1)|it->cursed;
1634  flags = (flags<<1)|it->damned;
1635  flags = (flags<<1)|it->unpaid;
1636  flags = (flags<<1)|it->locked;
1637  flags = (flags<<1)|it->applied;
1638  flags = (flags<<1)|it->open;
1639  flags = (flags<<1)|it->was_open;
1640  flags = (flags<<1)|it->inv_updated;
1641  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);
1642  write(scripts[i].out_fd, buf, strlen(buf));
1643 }
1644 
1645 #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:232
gint8 wc
Weapon Class.
Definition: client.h:225
int maxfd
Definition: client.c:58
static void script_process_cmd(int i)
Process a single script command from the given script.
Definition: script.c:1081
gint32 weapon_sp
Weapon speed (displayed in client as a float)
Definition: client.h:242
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:1009
guint16 level
The casting level of the spell.
Definition: client.h:267
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:256
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:231
item * ob
Player object.
Definition: client.h:302
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:271
#define CS_STAT_SPEED
Definition: newclient.h:131
Warning that something might not work.
Definition: client.h:409
guint16 sp
Mana per cast; may be zero.
Definition: client.h:269
struct MapCell * mapdata_cell(int x, int y)
Get the stored map cell at the given map coordinate.
Definition: mapdata.c:119
gint32 speed
Speed (is displayed as a float)
Definition: client.h:241
#define CS_STAT_DEX
Definition: newclient.h:122
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:69
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
guint16 time
Casting time in server ticks.
Definition: client.h:268
Stats stats
Player stats.
Definition: client.h:315
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:220
#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:50
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:1626
guint32 path
The bitmask of paths this spell belongs to.
Definition: client.h:281
#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:115
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:248
guint16 grace
Grace per cast; may be zero.
Definition: client.h:270
gint8 ac
Armour Class.
Definition: client.h:226
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'.
gint8 Str
Strength.
Definition: client.h:218
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:221
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:303
void mapdata_size(int *x, int *y)
Determine the size of the internal fog-of-war map.
Definition: mapdata.c:142
gint8 level
Experience level.
Definition: client.h:227
CmdFormat
Definition: script.h:43
int out_fd
Definition: script.c:128
Client_Player cpl
Player object.
Definition: client.c:68
item * container
open container
Definition: client.h:305
guint16 was_open
Definition: item.h:74
#define CONFIG_MAPHEIGHT
Definition: client.h:170
guint32 flagsval
Definition: item.h:79
int fd
Definition: client.h:84
gint8 Pow
Power.
Definition: client.h:224
void SockList_AddString(SockList *sl, const char *str)
Definition: newsocket.c:140
guint8 skill_number
The index in the skill arrays, plus CS_STAT_SKILLINFO.
Definition: client.h:275
gint16 food
Quantity food in stomach.
Definition: client.h:235
static int num_scripts
Definition: script.c:153
#define CS_STAT_CHA
Definition: newclient.h:124
gint8 Dex
Dexterity.
Definition: client.h:219
guint32 tag
Unique ID number for a spell so updspell etc can operate on it.
Definition: client.h:265
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:253
#define CS_STAT_EXP
No longer used.
Definition: newclient.h:125
bool mapdata_contains(int x, int y)
Determine whether the map data contains the given cell.
Definition: mapdata.c:126
struct Spell_struct * next
Definition: client.h:261
guint16 flags
Contains fire on/run on flags.
Definition: client.h:252
gint64 exp
Experience.
Definition: client.h:234
#define MAX_BUF
Definition: client-types.h:39
void script_list(void)
Definition: script.c:498
gint16 skill_level[MAX_SKILL]
Level of known skills.
Definition: client.h:255
Warning that something definitely didn't work.
Definition: client.h:410
guint32 weight_limit
Carrying weight limit.
Definition: client.h:257
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:316
gint16 dam
How much damage this object does for each hit.
Definition: client.h:238
guint32 run_on
True if run key is on.
Definition: client.h:321
char name[256]
One length byte plus data.
Definition: client.h:262
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:223
void script_kill(const char *params)
Definition: script.c:520
gint16 sp
Spell points for casting spells.
Definition: client.h:230
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:88
#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:318
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:88
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:229
char title[MAX_BUF]
Title of character.
Definition: client.h:317
static int data_len
Definition: png.c:51
guint16 applied
Definition: item.h:72
Includes various dependencies header files needed by most everything.
char * name
Definition: image.c:57
#define CS_STAT_RESIST_START
Start of resistances (inclusive)
Definition: newclient.h:169
gint16 hp
Hit Points.
Definition: client.h:228
#define CS_STAT_POW
Definition: newclient.h:136
char * name
Definition: script.c:125
Definition: script.h:50
guint32 no_echo
If TRUE, don't echo keystrokes.
Definition: client.h:324
#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:222
#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:320
#define CS_STAT_SKILLINFO
CS_STAT_SKILLINFO is used as the starting index point.
Definition: newclient.h:196
PlayerPosition pl_pos
Definition: map.c:60
guint32 attuned
Spell paths to which the player is attuned.
Definition: client.h:245
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:169
gint16 maxgrace
Maximum spell points.
Definition: client.h:233
guint32 denied
Spell paths denied to the player.
Definition: client.h:251