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