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