Crossfire Client, Trunk  R19307
script.c
Go to the documentation of this file.
00001 /*
00002  * Crossfire -- cooperative multi-player graphical RPG and adventure game
00003  *
00004  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
00005  * Copyright (c) 1992 Frank Tore Johansen
00006  *
00007  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
00008  * welcome to redistribute it under certain conditions. For details, please
00009  * see COPYING and LICENSE.
00010  *
00011  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
00012  */
00013 
00014 /*
00015  * This file has its own script.h for prototypes, so don't want to include
00016  * this when doing a 'make proto'.
00017  */
00018 #ifndef CPROTO
00019 
00097 /*
00098  * Include files
00099  */
00100 
00101 /*
00102  * This does not work under Windows for now.  Someday this will be fixed :)
00103  */
00104 
00105 #ifndef WIN32
00106 #include <errno.h>
00107 #include <sys/types.h>
00108 #include <sys/socket.h>
00109 #include <sys/wait.h>
00110 /* for SIGHUP */
00111 #include <signal.h>
00112 #endif
00113 #include <ctype.h>
00114 
00115 #include <client.h>
00116 #include <external.h>
00117 #include <script.h>
00118 #include <p_cmd.h>
00119 #include "mapdata.h"
00120 
00121 /*
00122  * Data structures
00123  */
00124 struct script {
00125     char *name; /* the script name */
00126     char *params; /* the script parameters, if any */
00127 #ifndef WIN32
00128     int out_fd; /* the file descriptor to which the client writes to the script */
00129     int in_fd; /* the file descriptor from which we read commands from the script */
00130 #else
00131     HANDLE out_fd; /* the file descriptor to which the client writes to the script */
00132     HANDLE in_fd; /* the file descriptor from which we read commands from the script */
00133 #endif /* WIN32 */
00134     int monitor; /* true if this script is monitoring commands sent to the server */
00135     int num_watch; /* number of commands we're watching */
00136     char **watch; /* array of commands that we're watching */
00137     int cmd_count; /* bytes already read in */
00138     char cmd[1024]; /* command from the script */
00139 #ifndef WIN32
00140     int pid;
00141 #else
00142     DWORD pid;   /* Handle to Win32 process ID */
00143     HANDLE process; /* Handle of Win32 process */
00144 #endif
00145     int sync_watch;
00146 };
00147 
00148 /*
00149  * Global variables
00150  */
00151 static struct script *scripts = NULL;
00152 
00153 static int num_scripts = 0;
00154 
00155 /*
00156  * Prototypes
00157  */
00158 static int script_by_name(const char *name);
00159 
00160 static void script_dead(int i);
00161 
00162 static void script_process_cmd(int i);
00163 
00164 static void send_map(int i, int x, int y);
00165 
00166 static void script_send_item(int i, const char *head, const item *it);
00167 
00168 
00169 /*
00170  * Functions
00171  */
00172 
00173 #ifdef WIN32
00174 
00175 #define write(x, y, z) emulate_write(x, y, z)
00176 #define read(x, y, z) emulate_read(x, y, z)
00177 
00178 static int emulate_read(HANDLE fd, char *buf, int len)
00179 {
00180     DWORD dwBytesRead;
00181     BOOL rc;
00182 
00183     FlushFileBuffers(fd);
00184     rc = ReadFile(fd, buf, len, &dwBytesRead, NULL);
00185     if (rc == FALSE) {
00186         return(-1);
00187     }
00188     buf[dwBytesRead] = '\0';
00189 
00190     return(dwBytesRead);
00191 }
00192 
00193 static int emulate_write(HANDLE fd, const char *buf, int len)
00194 {
00195     DWORD dwBytesWritten;
00196     BOOL rc;
00197 
00198     rc = WriteFile(fd, buf, len, &dwBytesWritten, NULL);
00199     FlushFileBuffers(fd);
00200     if (rc == FALSE) {
00201         return(-1);
00202     }
00203 
00204     return(dwBytesWritten);
00205 }
00206 
00207 #endif /* WIN32 */
00208 
00209 void script_init(const char *cparams) {
00210 #ifndef WIN32
00211     int pipe1[2], pipe2[2];
00212     int pid;
00213     char *name, *args, params[MAX_BUF];
00214 
00215     if (!cparams) {
00216         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00217                       "Please specify a script to start. For help, type "
00218                       "'help script'.");
00219         return;
00220     }
00221 
00222     /* cparams is a const value, so copy the data into a buffer */
00223     strncpy(params, cparams, MAX_BUF - 1);
00224     params[MAX_BUF - 1] = '\0';
00225 
00226     /* Get name and args */
00227     name = params;
00228     args = name;
00229     while (*args && *args != ' ') {
00230         ++args;
00231     }
00232     while (*args && *args == ' ') {
00233         *args++ = '\0';
00234     }
00235     if (*args == 0) {
00236         args = NULL;
00237     }
00238 
00239     /* Open two pipes, one for stdin and the other for stdout. */
00240     if (pipe(pipe1) != 0) {
00241         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00242                       "Unable to start script--pipe failed");
00243         return;
00244     }
00245     if (pipe(pipe2) != 0) {
00246         close(pipe1[0]);
00247         close(pipe1[1]);
00248 
00249         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00250                       "Unable to start script--pipe failed");
00251         return;
00252     }
00253 
00254     /* Fork */
00255     pid = fork();
00256     if (pid == -1) {
00257         close(pipe1[0]);
00258         close(pipe1[1]);
00259         close(pipe2[0]);
00260         close(pipe2[1]);
00261         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00262                       "Unable to start script--fork failed");
00263         return;
00264     }
00265 
00266     /* Child--set stdin/stdout to the pipes, then exec */
00267     if (pid == 0) {
00268         int i;
00269         int r;
00270         char *argv[256];
00271 
00272         /* Fill in argv[] */
00273         argv[0] = name;
00274         i = 1;
00275         while (args && *args && i < sizeof(argv)/sizeof(*argv)-1) {
00276             argv[i++] = args;
00277             while (*args && *args != ' ') {
00278                 ++args;
00279             }
00280             while (*args && *args == ' ') {
00281                 *args++ = '\0';
00282             }
00283         }
00284         argv[i] = NULL;
00285 
00286         /* Clean up file descriptor space */
00287         r = dup2(pipe1[0], 0);
00288         if (r != 0) {
00289             fprintf(stderr, "Script Child: Failed to set pipe1 as stdin\n");
00290         }
00291         r = dup2(pipe2[1], 1);
00292         if (r != 1) {
00293             fprintf(stderr, "Script Child: Failed to set pipe2 as stdout\n");
00294         }
00295         for (i = 3; i < 100; ++i) {
00296             close(i);
00297         }
00298 
00299         /* EXEC */
00300         r = execvp(argv[0], argv);
00301 
00302         /* If we get here, then there's been an failure of some sort.
00303          * In my case, it's often that I don't know what script name to
00304          * give to /script, so exec() can't find the script.
00305          *
00306          * Forward the error back to the client, using the script pipes.
00307          */
00308 
00309         if (r != -1) {
00310             printf("draw %d Script child: no error, but no execvp().\n", NDI_RED);
00311         } else {
00312             printf("draw %d Script child failed to start: %s\n", NDI_RED, strerror(errno));
00313         }
00314 
00315         exit(1);
00316     }
00317 
00318     /* Close the child's pipe ends */
00319     close(pipe1[0]);
00320     close(pipe2[1]);
00321 
00322     if (fcntl(pipe1[1], F_SETFL, O_NDELAY) == -1) {
00323         LOG(LOG_WARNING, "common::script_init", "Error on fcntl.");
00324     }
00325 
00326     /* realloc script array to add new entry; fill in the data */
00327     scripts = realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
00328 
00329     if (scripts == NULL) {
00330         LOG(LOG_ERROR, "script_init",
00331                 "Could not allocate memory: %s", strerror(errno));
00332         exit(EXIT_FAILURE);
00333     }
00334 
00335     scripts[num_scripts].name = strdup(name);
00336     scripts[num_scripts].params = args ? strdup(args) : NULL;
00337     scripts[num_scripts].out_fd = pipe1[1];
00338     scripts[num_scripts].in_fd = pipe2[0];
00339     scripts[num_scripts].monitor = 0;
00340     scripts[num_scripts].num_watch = 0;
00341     scripts[num_scripts].watch = NULL;
00342     scripts[num_scripts].cmd_count = 0;
00343     scripts[num_scripts].pid = pid;
00344     scripts[num_scripts].sync_watch = -1;
00345     ++num_scripts;
00346 #else /* WIN32 */
00347 
00348     char *name, *args;
00349     char params[ MAX_BUF ];
00350     SECURITY_ATTRIBUTES saAttr;
00351     PROCESS_INFORMATION piProcInfo;
00352     STARTUPINFO siStartupInfo;
00353     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd;
00354     HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout;
00355 
00356     if (!cparams) {
00357         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00358                       "Please specifiy a script to launch!");
00359         return;
00360     }
00361 
00362     strncpy(params, cparams, MAX_BUF-1);
00363     params[MAX_BUF-1] = '\0';
00364 
00365     /* Get name and args */
00366     name = params;
00367     args = name;
00368     while (*args && *args != ' ') {
00369         ++args;
00370     }
00371     while (*args && *args == ' ') {
00372         *args++ = '\0';
00373     }
00374     if (*args == 0) {
00375         args = NULL;
00376     }
00377 
00378     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
00379     saAttr.bInheritHandle = TRUE;
00380     saAttr.lpSecurityDescriptor = NULL;
00381 
00382     hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
00383     if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
00384         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00385                       "Script support: stdout CreatePipe() failed");
00386         return;
00387     }
00388 
00389     if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
00390         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00391                       "Script support: failed to redirect stdout using SetStdHandle()");
00392         return;
00393     }
00394 
00395     if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
00396         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00397                       "Script support: failed to duplicate stdout using DuplicateHandle()");
00398         return;
00399     }
00400 
00401     CloseHandle(hChildStdoutRd);
00402 
00403     hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
00404     if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
00405         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00406                       "Script support: stdin CreatePipe() failed");
00407         return;
00408     }
00409 
00410     if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) {
00411         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00412                       "Script support: failed to redirect stdin using SetStdHandle()");
00413         return;
00414     }
00415 
00416     if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
00417         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00418                       "Script support: failed to duplicate stdin using DuplicateHandle()");
00419         return;
00420     }
00421 
00422     CloseHandle(hChildStdinWr);
00423 
00424     ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
00425     ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO));
00426     siStartupInfo.cb = sizeof(STARTUPINFO);
00427 
00428     if (args) {
00429         args[-1] = ' ';
00430     }
00431 
00432     if (!CreateProcess(NULL, name, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo)) {
00433         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00434                       "Script support: CreateProcess() failed");
00435         return;
00436     }
00437 
00438     CloseHandle(piProcInfo.hThread);
00439 
00440     if (args) {
00441         args[-1] = '\0';
00442     }
00443 
00444     if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) {
00445         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00446                       "Script support: restoring original stdin failed");
00447         return;
00448     }
00449 
00450     if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
00451         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00452                       "Script support: restoring original stdout failed");
00453         return;
00454     }
00455 
00456     /* realloc script array to add new entry; fill in the data */
00457     scripts = realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
00458 
00459     if (scripts == NULL) {
00460         LOG(LOG_ERROR, "script_init",
00461                 "Could not allocate memory: %s", strerror(errno));
00462         exit(EXIT_FAILURE);
00463     }
00464 
00465     scripts[num_scripts].name = strdup(name);
00466     scripts[num_scripts].params = args ? strdup(args) : NULL;
00467     scripts[num_scripts].out_fd = hChildStdinWrDup;
00468     scripts[num_scripts].in_fd = hChildStdoutRdDup;
00469     scripts[num_scripts].monitor = 0;
00470     scripts[num_scripts].num_watch = 0;
00471     scripts[num_scripts].watch = NULL;
00472     scripts[num_scripts].cmd_count = 0;
00473     scripts[num_scripts].pid = piProcInfo.dwProcessId;
00474     scripts[num_scripts].process = piProcInfo.hProcess;
00475     scripts[num_scripts].sync_watch = -1;
00476     ++num_scripts;
00477 #endif /* WIN32 */
00478 }
00479 
00480 void script_sync(int commdiff)
00481 {
00482     int i;
00483 
00484     if (commdiff < 0) {
00485         commdiff +=256;
00486     }
00487     for (i = 0; i < num_scripts; ++i) {
00488         if (commdiff <= scripts[i].sync_watch && scripts[i].sync_watch >= 0) {
00489             char buf[1024];
00490 
00491             snprintf(buf, sizeof(buf), "sync %d\n", commdiff);
00492             write(scripts[i].out_fd, buf, strlen(buf));
00493             scripts[i].sync_watch = -1;
00494         }
00495     }
00496 }
00497 
00498 void script_list(void)
00499 {
00500     if (num_scripts == 0) {
00501         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00502                       "No scripts are currently running");
00503     } else {
00504         int i;
00505         char buf[1024];
00506 
00507         snprintf(buf, sizeof(buf), "%d scripts currently running:", num_scripts);
00508         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
00509         for (i = 0; i < num_scripts; ++i) {
00510             if (scripts[i].params) {
00511                 snprintf(buf, sizeof(buf), "%d %s  %s", i+1, scripts[i].name, scripts[i].params);
00512             } else {
00513                 snprintf(buf, sizeof(buf), "%d %s", i+1, scripts[i].name);
00514             }
00515             draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
00516         }
00517     }
00518 }
00519 
00520 void script_kill(const char *params)
00521 {
00522     int i;
00523 
00524     /* Verify that the number is a valid array entry */
00525     i = script_by_name(params);
00526     if (i < 0 || i >= num_scripts) {
00527         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00528                       "No such running script");
00529         return;
00530     }
00531 #ifndef WIN32
00532     kill(scripts[i].pid, SIGHUP);
00533 #else
00534     GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid);
00535 #endif /* WIN32 */
00536     draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00537                   "Killed script.");
00538     script_dead(i);
00539 }
00540 
00541 #ifdef WIN32
00542 void script_killall(void)
00543 {
00544     while (num_scripts > 0) {
00545         GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid);
00546         script_dead(0);
00547     }
00548 }
00549 #endif /* WIN32 */
00550 
00551 void script_fdset(int *maxfd, fd_set *set)
00552 {
00553 #ifndef WIN32
00554     int i;
00555 
00556     for (i = 0; i < num_scripts; ++i) {
00557         FD_SET(scripts[i].in_fd, set);
00558         if (scripts[i].in_fd >= *maxfd) {
00559             *maxfd = scripts[i].in_fd+1;
00560         }
00561     }
00562 #endif /* WIN32 */
00563 }
00564 
00565 void script_process(fd_set *set)
00566 {
00567     int i;
00568     int r;
00569 #ifdef WIN32
00570     DWORD nAvailBytes = 0;
00571     char cTmp;
00572     BOOL bRC;
00573     DWORD dwStatus;
00574     BOOL bStatus;
00575 #endif
00576 
00577     /* Determine which script's fd is set */
00578     for (i = 0; i < num_scripts; ++i) {
00579 #ifndef WIN32
00580         if (FD_ISSET(scripts[i].in_fd, set))
00581 #else
00582         bStatus = GetExitCodeProcess(scripts[i].process, &dwStatus);
00583         bRC = PeekNamedPipe(scripts[i].in_fd, &cTmp, 1, NULL, &nAvailBytes, NULL);
00584         if (nAvailBytes)
00585 #endif /* WIN32 */
00586         {
00587             /* Read in script[i].cmd */
00588             r = read(scripts[i].in_fd, scripts[i].cmd+scripts[i].cmd_count, sizeof(scripts[i].cmd)-scripts[i].cmd_count-1);
00589             if (r > 0) {
00590                 scripts[i].cmd_count += r;
00591             }
00592 #ifndef WIN32
00593             else if (r == 0 || errno == EBADF)
00594 #else
00595             else if (r == 0 || GetLastError() == ERROR_BROKEN_PIPE)
00596 #endif
00597             {
00598                 /* Script has exited; delete it */
00599                 script_dead(i);
00600                 return;
00601             }
00602             /* If a newline or full buffer has been reached, process it */
00603             scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
00604             while (scripts[i].cmd_count == sizeof(scripts[i].cmd)-1
00605 #ifndef WIN32
00606                     || strchr(scripts[i].cmd, '\n'))
00607 #else
00608                     || strchr(scripts[i].cmd, '\r\n'))
00609 #endif /* WIN32 */
00610             {
00611                 script_process_cmd(i);
00612                 scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
00613             }
00614             return; /* Only process one script at a time */
00615         }
00616 #ifdef WIN32
00617         else if (!bRC || (bStatus && (dwStatus != STILL_ACTIVE))) { /* Error: assume dead */
00618             script_dead(i);
00619         }
00620 #endif /* WIN32 */
00621     }
00622 }
00623 
00624 void script_watch(const char *cmd, const uint8 *data_initial, const int data_len, const enum CmdFormat format)
00625 {
00626     int i;
00627     int w;
00628     int l, len;
00629     const uint8 *data;
00630 
00631     /* For each script... */
00632     for (i = 0; i < num_scripts; ++i) {
00633         /* For each watch... */
00634         for (w = 0; w < scripts[i].num_watch; ++w) {
00635             len = data_len;
00636             /* Does this command match our watch? */
00637             l = strlen(scripts[i].watch[w]);
00638             if (!l || strncmp(cmd, scripts[i].watch[w], l) == 0) {
00639                 char buf[10240];
00640 
00641                 data = data_initial;
00642                 if (!len) {
00643                     snprintf(buf, sizeof(buf), "watch %s\n", cmd);
00644                 } else
00645                     switch (format) {
00646                     case ASCII:
00647                         snprintf(buf, sizeof(buf), "watch %s %s\n", cmd, data);
00648                         break;
00649 
00650                     case SHORT_INT:
00651                         snprintf(buf, sizeof(buf), "watch %s %d %d\n", cmd, GetShort_String(data), GetInt_String(data+2));
00652                         break;
00653 
00654                     case SHORT_ARRAY: {
00655                         int be;
00656                         int p;
00657 
00658                         be = snprintf(buf, sizeof(buf), "watch %s", cmd);
00659                         for (p = 0; p*2 < len && p < 100; ++p) {
00660                             be += snprintf(buf+be, sizeof(buf)-be, " %d", GetShort_String(data+p*2));
00661                         }
00662                         be += snprintf(buf+be, sizeof(buf)-be, "\n");
00663                     }
00664                     break;
00665 
00666                     case INT_ARRAY: {
00667                         int be;
00668                         int p;
00669 
00670                         be = snprintf(buf, sizeof(buf), "watch %s", cmd);
00671                         for (p = 0; p*4 < len; ++p) {
00672                             be += snprintf(buf+be, sizeof(buf)-be, " %d", GetInt_String(data+p*4));
00673                         }
00674                         be += snprintf(buf+be, sizeof(buf)-be, "\n");
00675                     }
00676                     break;
00677 
00678                     case STATS: {
00679                         /*
00680                          * We cheat here and log each stat as a separate command, even
00681                          * if the server sent a bunch of updates as a single message;
00682                          * most scripts will be easier to write if they only parse a fixed
00683                          * format.
00684                          */
00685                         int be = 0;
00686                         while (len) {
00687                             int c; /* which stat */
00688 
00689                             be += snprintf(buf+be, sizeof(buf)-be, "watch %s", cmd);
00690                             c = *data;
00691                             ++data;
00692                             --len;
00693                             if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
00694                                 be += snprintf(buf+be, sizeof(buf)-be, " resists %d %d\n", c, GetShort_String(data));
00695                                 data += 2;
00696                                 len -= 2;
00697                             } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
00698                                 be += snprintf(buf+be, sizeof(buf)-be, " skill %d %d %" FMT64 "\n", c, *data, GetInt64_String(data+1));
00699                                 data += 9;
00700                                 len -= 9;
00701                             } else
00702                                 switch (c) {
00703                                 case CS_STAT_HP:
00704                                     be += snprintf(buf+be, sizeof(buf)-be, " hp %d\n", GetShort_String(data));
00705                                     data += 2;
00706                                     len -= 2;
00707                                     break;
00708 
00709                                 case CS_STAT_MAXHP:
00710                                     be += snprintf(buf+be, sizeof(buf)-be, " maxhp %d\n", GetShort_String(data));
00711                                     data += 2;
00712                                     len -= 2;
00713                                     break;
00714 
00715                                 case CS_STAT_SP:
00716                                     be += snprintf(buf+be, sizeof(buf)-be, " sp %d\n", GetShort_String(data));
00717                                     data += 2;
00718                                     len -= 2;
00719                                     break;
00720 
00721                                 case CS_STAT_MAXSP:
00722                                     be += snprintf(buf+be, sizeof(buf)-be, " maxsp %d\n", GetShort_String(data));
00723                                     data += 2;
00724                                     len -= 2;
00725                                     break;
00726 
00727                                 case CS_STAT_GRACE:
00728                                     be += snprintf(buf+be, sizeof(buf)-be, " grace %d\n", GetShort_String(data));
00729                                     data += 2;
00730                                     len -= 2;
00731                                     break;
00732 
00733                                 case CS_STAT_MAXGRACE:
00734                                     be += snprintf(buf+be, sizeof(buf)-be, " maxgrace %d\n", GetShort_String(data));
00735                                     data += 2;
00736                                     len -= 2;
00737                                     break;
00738 
00739                                 case CS_STAT_STR:
00740                                     be += snprintf(buf+be, sizeof(buf)-be, " str %d\n", GetShort_String(data));
00741                                     data += 2;
00742                                     len -= 2;
00743                                     break;
00744 
00745                                 case CS_STAT_INT:
00746                                     be += snprintf(buf+be, sizeof(buf)-be, " int %d\n", GetShort_String(data));
00747                                     data += 2;
00748                                     len -= 2;
00749                                     break;
00750 
00751                                 case CS_STAT_POW:
00752                                     be += snprintf(buf+be, sizeof(buf)-be, " pow %d\n", GetShort_String(data));
00753                                     data += 2;
00754                                     len -= 2;
00755                                     break;
00756 
00757                                 case CS_STAT_WIS:
00758                                     be += snprintf(buf+be, sizeof(buf)-be, " wis %d\n", GetShort_String(data));
00759                                     data += 2;
00760                                     len -= 2;
00761                                     break;
00762 
00763                                 case CS_STAT_DEX:
00764                                     be += snprintf(buf+be, sizeof(buf)-be, " dex %d\n", GetShort_String(data));
00765                                     data += 2;
00766                                     len -= 2;
00767                                     break;
00768 
00769                                 case CS_STAT_CON:
00770                                     be += snprintf(buf+be, sizeof(buf)-be, " con %d\n", GetShort_String(data));
00771                                     data += 2;
00772                                     len -= 2;
00773                                     break;
00774 
00775                                 case CS_STAT_CHA:
00776                                     be += snprintf(buf+be, sizeof(buf)-be, " cha %d\n", GetShort_String(data));
00777                                     data += 2;
00778                                     len -= 2;
00779                                     break;
00780 
00781                                 case CS_STAT_EXP:
00782                                     be += snprintf(buf+be, sizeof(buf)-be, " exp %d\n", GetInt_String(data));
00783                                     data += 4;
00784                                     len -= 4;
00785                                     break;
00786 
00787                                 case CS_STAT_EXP64:
00788                                     be += snprintf(buf+be, sizeof(buf)-be, " exp %" FMT64 "\n", GetInt64_String(data));
00789                                     data += 8;
00790                                     len -= 8;
00791                                     break;
00792 
00793                                 case CS_STAT_LEVEL:
00794                                     be += snprintf(buf+be, sizeof(buf)-be, " level %d\n", GetShort_String(data));
00795                                     data += 2;
00796                                     len -= 2;
00797                                     break;
00798 
00799                                 case CS_STAT_WC:
00800                                     be += snprintf(buf+be, sizeof(buf)-be, " wc %d\n", GetShort_String(data));
00801                                     data += 2;
00802                                     len -= 2;
00803                                     break;
00804 
00805                                 case CS_STAT_AC:
00806                                     be += snprintf(buf+be, sizeof(buf)-be, " ac %d\n", GetShort_String(data));
00807                                     data += 2;
00808                                     len -= 2;
00809                                     break;
00810 
00811                                 case CS_STAT_DAM:
00812                                     be += snprintf(buf+be, sizeof(buf)-be, " dam %d\n", GetShort_String(data));
00813                                     data += 2;
00814                                     len -= 2;
00815                                     break;
00816 
00817                                 case CS_STAT_ARMOUR:
00818                                     be += snprintf(buf+be, sizeof(buf)-be, " armour %d\n", GetShort_String(data));
00819                                     data += 2;
00820                                     len -= 2;
00821                                     break;
00822 
00823                                 case CS_STAT_SPEED:
00824                                     be += snprintf(buf+be, sizeof(buf)-be, " speed %d\n", GetInt_String(data));
00825                                     data += 4;
00826                                     len -= 4;
00827                                     break;
00828 
00829                                 case CS_STAT_FOOD:
00830                                     be += snprintf(buf+be, sizeof(buf)-be, " food %d\n", GetShort_String(data));
00831                                     data += 2;
00832                                     len -= 2;
00833                                     break;
00834 
00835                                 case CS_STAT_WEAP_SP:
00836                                     be += snprintf(buf+be, sizeof(buf)-be, " weap_sp %d\n", GetInt_String(data));
00837                                     data += 4;
00838                                     len -= 4;
00839                                     break;
00840 
00841                                 case CS_STAT_FLAGS:
00842                                     be += snprintf(buf+be, sizeof(buf)-be, " flags %d\n", GetShort_String(data));
00843                                     data += 2;
00844                                     len -= 2;
00845                                     break;
00846 
00847                                 case CS_STAT_WEIGHT_LIM:
00848                                     be += snprintf(buf+be, sizeof(buf)-be, " weight_lim %d\n", GetInt_String(data));
00849                                     data += 4;
00850                                     len -= 4;
00851                                     break;
00852 
00853                                 case CS_STAT_RANGE: {
00854                                     int rlen = *data;
00855                                     ++data;
00856                                     --len;
00857                                     be += snprintf(buf+be, sizeof(buf)-be, " range %*.*s\n", rlen, rlen, data);
00858                                     data += rlen;
00859                                     len -= rlen;
00860                                     break;
00861                                 }
00862 
00863                                 case CS_STAT_TITLE: {
00864                                     int rlen = *data;
00865                                     ++data;
00866                                     --len;
00867                                     be += snprintf(buf+be, sizeof(buf)-be, " title %*.*s\n", rlen, rlen, data);
00868                                     data += rlen;
00869                                     len -= rlen;
00870                                     break;
00871                                 }
00872 
00873                                 default:
00874                                     be += snprintf(buf+be, sizeof(buf)-be, " unknown %d %d bytes left\n", c, len);
00875                                     len = 0;
00876                                 }
00877                         }
00878                     }
00879                     break;
00880 
00881                     case MIXED:
00882                         /* magicmap */
00883                         /* mapextended */
00884                         /* item1 item2 */
00885                         /* upditem */
00886                         /* image image2 */
00887                         /* face face1 face2 */
00888                         /* sound */
00889                         /* player */
00890                         /*
00891                          * If we find that scripts need data from any of the above, we can
00892                          * write special-case code as with stats.  In the meantime, fall
00893                          * through and just give a hex dump.  Script writers should not
00894                          * depend on that data format.
00895                          */
00896                     case NODATA:
00897                     default: {
00898                         int be;
00899                         int p;
00900 
00901                         /*we may receive an null data, in which case len has no meaning*/
00902                         if (!data) {
00903                             len = 0;
00904                         }
00905                         be = snprintf(buf, sizeof(buf), "watch %s %d bytes unparsed:", cmd, len);
00906                         for (p = 0; p < len && p < 100; ++p) {
00907                             be += snprintf(buf+be, sizeof(buf)-be, " %02x", data[p]);
00908                         }
00909                         be += snprintf(buf+be, sizeof(buf)-be, "\n");
00910                     }
00911                     break;
00912                     }
00913                 write(scripts[i].out_fd, buf, strlen(buf));
00914             }
00915         }
00916     }
00917 }
00918 
00919 void script_monitor(const char *command, int repeat, int must_send)
00920 {
00921     int i;
00922 
00923     /* For each script... */
00924     for (i = 0; i < num_scripts; ++i) {
00925         /* Do we send the command? */
00926         if (scripts[i].monitor) {
00927             char buf[1024];
00928 
00929             snprintf(buf, sizeof(buf), "monitor %d %d %s\n", repeat, must_send, command);
00930             write(scripts[i].out_fd, buf, strlen(buf));
00931         }
00932     }
00933 }
00934 
00935 void script_monitor_str(const char *command)
00936 {
00937     int i;
00938 
00939     /* For each script... */
00940     for (i = 0; i < num_scripts; ++i) {
00941         /* Do we send the command? */
00942         if (scripts[i].monitor) {
00943             char buf[1024];
00944 
00945             snprintf(buf, sizeof(buf), "monitor %s\n", command);
00946             write(scripts[i].out_fd, buf, strlen(buf));
00947         }
00948     }
00949 }
00950 
00951 void script_tell(const char *params)
00952 {
00953     int i;
00954     char *p;
00955 
00956     if (params == NULL) {
00957         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00958                       "Which script do you want to talk to?");
00959         return;
00960     }
00961 
00962     /* Local copy for modifications */
00963     char params_cpy[MAX_BUF]; 
00964     snprintf(params_cpy, MAX_BUF-1, "%s", params);
00965     p = strchr(params_cpy, ' ');
00966     if (p == NULL) {
00967         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00968                       "What do you want to tell the script?");
00969         return;
00970     }
00971     while (*p == ' ') {
00972         *p++ = '\0';
00973     }
00974 
00975     /* Find the script */
00976     i = script_by_name(params_cpy);
00977     if (i < 0) {
00978         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00979                       "No such running script");
00980         return;
00981     }
00982 
00983     /* Send the message */
00984     write(scripts[i].out_fd, "scripttell ", 11);
00985     write(scripts[i].out_fd, p, strlen(p));
00986     write(scripts[i].out_fd, "\n", 1);
00987 }
00988 
00989 static int script_by_name(const char *name)
00990 {
00991     int i;
00992     int l;
00993 
00994     if (name == NULL) {
00995         return(num_scripts == 1 ? 0 : -1);
00996     }
00997 
00998     /* Parse script number */
00999     if (isdigit(*name)) {
01000         i = atoi(name);
01001         --i;
01002         if (i >= 0 && i < num_scripts) {
01003             return(i);
01004         }
01005     }
01006 
01007     /* Parse script name */
01008     l = 0;
01009     while (name[l] && name[l] != ' ') {
01010         ++l;
01011     }
01012     for (i = 0; i < num_scripts; ++i) {
01013         if (strncmp(name, scripts[i].name, l) == 0) {
01014             return(i);
01015         }
01016     }
01017     return(-1);
01018 }
01019 
01020 static void script_dead(int i)
01021 {
01022     int w;
01023 
01024     /* Release resources */
01025 #ifndef WIN32
01026     close(scripts[i].in_fd);
01027     close(scripts[i].out_fd);
01028 #else
01029     CloseHandle(scripts[i].in_fd);
01030     CloseHandle(scripts[i].out_fd);
01031     CloseHandle(scripts[i].process);
01032 #endif
01033     free(scripts[i].name);
01034     free(scripts[i].params);
01035     for (w = 0; w < scripts[i].num_watch; ++w) {
01036         free(scripts[i].watch[w]);
01037     }
01038     free(scripts[i].watch);
01039 
01040 #ifndef WIN32
01041     waitpid(-1, NULL, WNOHANG);
01042 #endif
01043 
01044     /* Move scripts with higher index numbers down one slot */
01045     if (i < (num_scripts-1)) {
01046         memmove(&scripts[i], &scripts[i+1], sizeof(scripts[i])*(num_scripts-i-1));
01047     }
01048 
01049     /* Update our count */
01050     --num_scripts;
01051 }
01052 
01053 static void send_map(int i, int x, int y)
01054 {
01055     char buf[1024];
01056 
01057     if (x < 0 || y < 0 || the_map.x <= x || the_map.y <= y) {
01058         snprintf(buf, sizeof(buf), "request map %d %d unknown\n", x, y);
01059         write(scripts[i].out_fd, buf, strlen(buf));
01060     }
01061     /*** FIXME *** send more relevant data ***/
01062     snprintf(buf, sizeof(buf), "request map %d %d  %d %c %c %c %c"
01063              " smooth %d %d %d heads %d %d %d tails %d %d %d\n",
01064              x, y, the_map.cells[x][y].darkness,
01065              the_map.cells[x][y].need_update ? 'y' : 'n',
01066              the_map.cells[x][y].have_darkness ? 'y' : 'n',
01067              the_map.cells[x][y].need_resmooth ? 'y' : 'n',
01068              the_map.cells[x][y].cleared ? 'y' : 'n',
01069              the_map.cells[x][y].smooth[0], the_map.cells[x][y].smooth[1], the_map.cells[x][y].smooth[2],
01070              the_map.cells[x][y].heads[0].face, the_map.cells[x][y].heads[1].face, the_map.cells[x][y].heads[2].face,
01071              the_map.cells[x][y].tails[0].face, the_map.cells[x][y].tails[1].face, the_map.cells[x][y].tails[2].face
01072             );
01073     write(scripts[i].out_fd, buf, strlen(buf));
01074 }
01075 
01076 static void script_process_cmd(int i)
01077 {
01078     char cmd[1024];
01079     char *c;
01080     int l;
01081 
01082     /*
01083      * Strip out just this one command
01084      */
01085     for (l = 0; l < scripts[i].cmd_count; ++l) {
01086         if (scripts[i].cmd[l] == '\n') {
01087             break;
01088         }
01089     }
01090     ++l;
01091     memcpy(cmd, scripts[i].cmd, l);
01092 #ifndef WIN32
01093     cmd[l-1] = 0;
01094 #else
01095     cmd[l-2] = 0;
01096 #endif
01097     if (l < scripts[i].cmd_count) {
01098         memmove(scripts[i].cmd, scripts[i].cmd+l, scripts[i].cmd_count-l);
01099         scripts[i].cmd_count -= l;
01100     } else {
01101         scripts[i].cmd_count = 0;
01102     }
01103 
01104     /*
01105      * Now the data in scripts[i] is ready for the next read.
01106      * We have a complete command in cmd[].
01107      * Process it.
01108      */
01109     /*
01110      * Script commands
01111      *
01112      * watch <command type>
01113      * unwatch <command type>
01114      * request <data type>
01115      * issue <repeat> <must_send> <command>
01116      * localcmd <command> [<params>]
01117      * draw <color> <text>
01118      * monitor
01119      * unmonitor
01120      */
01121     if (strncmp(cmd, "sync", 4) == 0) {
01122         c = cmd+4;
01123         while (*c && *c != ' ') {
01124             ++c;
01125         }
01126         while (*c == ' ') {
01127             ++c;
01128         }
01129         scripts[i].sync_watch = -1;
01130         if (isdigit(*c)) {
01131             scripts[i].sync_watch = atoi(c);
01132         }
01133         script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */
01134     } else if (strncmp(cmd, "watch", 5) == 0) {
01135         c = cmd+5;
01136         while (*c && *c != ' ') {
01137             ++c;
01138         }
01139         while (*c == ' ') {
01140             ++c;
01141         }
01142         c = strdup(c);
01143         scripts[i].watch = realloc(scripts[i].watch, (scripts[i].num_watch+1)*sizeof(scripts[i].watch[1]));
01144         scripts[i].watch[scripts[i].num_watch] = c;
01145         ++scripts[i].num_watch;
01146     } else if (strncmp(cmd, "unwatch", 7) == 0) {
01147         int w;
01148 
01149         c = cmd+7;
01150         while (*c && *c != ' ') {
01151             ++c;
01152         }
01153         while (*c == ' ') {
01154             ++c;
01155         }
01156         for (w = 0; w < scripts[i].num_watch; ++w) {
01157             if (strcmp(c, scripts[i].watch[w]) == 0) {
01158                 free(scripts[i].watch[w]);
01159                 while (w+1 < scripts[i].num_watch) {
01160                     scripts[i].watch[w] = scripts[i].watch[w+1];
01161                     ++w;
01162                 }
01163                 --scripts[i].num_watch;
01164                 break;
01165             }
01166         }
01167     } else if (strncmp(cmd, "request", 7) == 0) {
01168         c = cmd+7;
01169         while (*c && *c != ' ') {
01170             ++c;
01171         }
01172         while (*c == ' ') {
01173             ++c;
01174         }
01175         if (!*c) {
01176             return;    /* bad request */
01177         }
01178         /*
01179          * Request information from the client's view of the world
01180          * (mostly defined in client.h)
01181          *
01182          * Valid requests:
01183          *
01184          *   player       Return the player's tag and title
01185          *   range        Return the type and name of the currently selected range attack
01186          *   stat <type>  Return the specified stats
01187          *   stat stats   Return Str,Con,Dex,Int,Wis,Pow,Cha
01188          *   stat cmbt    Return wc,ac,dam,speed,weapon_sp
01189          *   stat hp      Return hp,maxhp,sp,maxsp,grace,maxgrace,food
01190          *   stat xp      Return level,xp,skill-1 level,skill-1 xp,...
01191          *   stat resists Return resistances
01192          *   stat paths   Return spell paths: attuned, repelled, denied.
01193          *   weight       Return maxweight, weight
01194          *   flags        Return flags (fire, run)
01195          *   items inv    Return a list of items in the inventory, one per line
01196          *   items actv   Return a list of inventory items that are active, one per line
01197          *   items on     Return a list of items under the player, one per line
01198          *   items cont   Return a list of items in the open container, one per line
01199          *   map pos      Return the players x,y within the current map
01200          *   map near     Return the 3x3 grid of the map centered on the player
01201          *   map all      Return all the known map information
01202          *   map <x> <y>  Return the information about square x,y in the current map
01203          *   skills       Return a list of all skill names, one per line (see also stat xp)
01204          *   spells       Return a list of known spells, one per line
01205          */
01206         if (strncmp(c, "player", 6) == 0) {
01207             char buf[1024];
01208 
01209             snprintf(buf, sizeof(buf), "request player %d %s\n", cpl.ob->tag, cpl.title);
01210             write(scripts[i].out_fd, buf, strlen(buf));
01211         } else if (strncmp(c, "range", 5) == 0) {
01212             char buf[1024];
01213 
01214             snprintf(buf, sizeof(buf), "request range %s\n", cpl.range);
01215             write(scripts[i].out_fd, buf, strlen(buf));
01216         } else if (strncmp(c, "weight", 5) == 0) {
01217             char buf[1024];
01218 
01219             snprintf(buf, sizeof(buf), "request weight %d %d\n", cpl.stats.weight_limit, (int)(cpl.ob->weight*1000));
01220             write(scripts[i].out_fd, buf, strlen(buf));
01221         } else if (strncmp(c, "stat ", 5) == 0) {
01222             c += 4;
01223             while (*c && *c != ' ') {
01224                 ++c;
01225             }
01226             while (*c == ' ') {
01227                 ++c;
01228             }
01229             if (!*c) {
01230                 return;    /* bad request */
01231             }
01232             /*
01233              *   stat stats   Return Str,Con,Dex,Int,Wis,Pow,Cha
01234              *   stat cmbt    Return wc,ac,dam,speed,weapon_sp
01235              *   stat hp      Return hp,maxhp,sp,maxsp,grace,maxgrace,food
01236              *   stat xp      Return level,xp,skill-1 level,skill-1 xp,...
01237              *   stat resists Return resistances
01238              */
01239             if (strncmp(c, "stats", 5) == 0) {
01240                 char buf[1024];
01241 
01242                 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);
01243                 write(scripts[i].out_fd, buf, strlen(buf));
01244             } else if (strncmp(c, "cmbt", 4) == 0) {
01245                 char buf[1024];
01246 
01247                 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);
01248                 write(scripts[i].out_fd, buf, strlen(buf));
01249             } else if (strncmp(c, "hp", 2) == 0) {
01250                 char buf[1024];
01251 
01252                 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);
01253                 write(scripts[i].out_fd, buf, strlen(buf));
01254             } else if (strncmp(c, "xp", 2) == 0) {
01255                 char buf[1024];
01256                 int s;
01257 
01258                 snprintf(buf, sizeof(buf), "request stat xp %d %" FMT64, cpl.stats.level, cpl.stats.exp);
01259                 write(scripts[i].out_fd, buf, strlen(buf));
01260                 for (s = 0; s < MAX_SKILL; ++s) {
01261                     snprintf(buf, sizeof(buf), " %d %" FMT64, cpl.stats.skill_level[s], cpl.stats.skill_exp[s]);
01262                     write(scripts[i].out_fd, buf, strlen(buf));
01263                 }
01264                 write(scripts[i].out_fd, "\n", 1);
01265             } else if (strncmp(c, "resists", 7) == 0) {
01266                 char buf[1024];
01267                 int s;
01268 
01269                 snprintf(buf, sizeof(buf), "request stat resists");
01270                 write(scripts[i].out_fd, buf, strlen(buf));
01271                 for (s = 0; s < 30; ++s) {
01272                     snprintf(buf, sizeof(buf), " %d", cpl.stats.resists[s]);
01273                     write(scripts[i].out_fd, buf, strlen(buf));
01274                 }
01275                 write(scripts[i].out_fd, "\n", 1);
01276             } else if (strncmp(c, "paths", 2) == 0) {
01277                 char buf[1024];
01278 
01279                 snprintf(buf, sizeof(buf), "request stat paths %d %d %d\n", cpl.stats.attuned, cpl.stats.repelled, cpl.stats.denied);
01280                 write(scripts[i].out_fd, buf, strlen(buf));
01281             }
01282         } else if (strncmp(c, "flags", 5) == 0) {
01283             char buf[1024];
01284 
01285             snprintf(buf, sizeof(buf), "request flags %d %d %d %d\n", cpl.stats.flags, cpl.fire_on, cpl.run_on, cpl.no_echo);
01286             write(scripts[i].out_fd, buf, strlen(buf));
01287         } else if (strncmp(c, "items ", 6) == 0) {
01288             c += 5;
01289             while (*c && *c != ' ') {
01290                 ++c;
01291             }
01292             while (*c == ' ') {
01293                 ++c;
01294             }
01295             if (!*c) {
01296                 return;    /* bad request */
01297             }
01298             /*
01299              *   items inv    Return a list of items in the inventory, one per line
01300              *   items actv   Return a list of inventory items that are active, one per line
01301              *   items on     Return a list of items under the player, one per line
01302              *   items cont   Return a list of items in the open container, one per line
01303              */
01304             if (strncmp(c, "inv", 3) == 0) {
01305                 char *buf;
01306                 item *it;
01307 
01308                 for (it = cpl.ob->inv; it; it = it->next) {
01309                     script_send_item(i, "request items inv ", it);
01310                 }
01311                 buf = "request items inv end\n";
01312                 write(scripts[i].out_fd, buf, strlen(buf));
01313             }
01314             if (strncmp(c, "actv", 4) == 0) {
01315                 char *buf;
01316                 item *it;
01317 
01318                 for (it = cpl.ob->inv; it; it = it->next) {
01319                     if (it->applied) {
01320                         script_send_item(i, "request items actv ", it);
01321                     }
01322                 }
01323                 buf = "request items actv end\n";
01324                 write(scripts[i].out_fd, buf, strlen(buf));
01325             }
01326             if (strncmp(c, "on", 2) == 0) {
01327                 char *buf;
01328                 item *it;
01329 
01330                 for (it = cpl.below->inv; it; it = it->next) {
01331                     script_send_item(i, "request items on ", it);
01332                 }
01333                 buf = "request items on end\n";
01334                 write(scripts[i].out_fd, buf, strlen(buf));
01335             }
01336             if (strncmp(c, "cont", 4) == 0) {
01337                 char *buf;
01338                 item *it;
01339 
01340                 if (cpl.container) {
01341                     for (it = cpl.container->inv; it; it = it->next) {
01342                         script_send_item(i, "request items cont ", it);
01343                     }
01344                 }
01345                 buf = "request items cont end\n";
01346                 write(scripts[i].out_fd, buf, strlen(buf));
01347             }
01348         } else if (strncmp(c, "map ", 4) == 0) {
01349             int x, y;
01350 
01351             c += 3;
01352             while (*c && *c != ' ') {
01353                 ++c;
01354             }
01355             while (*c == ' ') {
01356                 ++c;
01357             }
01358             if (!*c) {
01359                 return;    /* bad request */
01360             }
01361             /*
01362              *   map pos      Return the players x,y within the current map
01363              *   map near     Return the 3x3 grid of the map centered on the player
01364              *   map all      Return all the known map information
01365              *   map <x> <y>  Return the information about square x,y in the current map
01366              */
01367             if (strncmp(c, "pos", 3) == 0) {
01368                 char buf[1024];
01369 
01370                 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);
01371                 write(scripts[i].out_fd, buf, strlen(buf));
01372             } else if (strncmp(c, "near", 4) == 0) {
01373                 for (y = 0; y < 3; ++y)
01374                     for (x = 0; x < 3; ++x)
01375                         send_map(i,
01376                                  x+pl_pos.x+use_config[CONFIG_MAPWIDTH]/2-1,
01377                                  y+pl_pos.y+use_config[CONFIG_MAPHEIGHT]/2-1
01378                                 );
01379             } else if (strncmp(c, "all", 3) == 0) {
01380                 char buf[1024];
01381 
01382                 for (y = 0; y < the_map.y; ++y)
01383                     for (x = 0; x < the_map.x; ++x) {
01384                         send_map(i, x, y);
01385                     }
01386                 snprintf(buf, sizeof(buf), "request map end\n");
01387                 write(scripts[i].out_fd, buf, strlen(buf));
01388             } else {
01389                 while (*c && !isdigit(*c)) {
01390                     ++c;
01391                 }
01392                 if (!*c) {
01393                     return;    /* No x specified */
01394                 }
01395                 x = atoi(c);
01396                 while (*c && *c != ' ') {
01397                     ++c;
01398                 }
01399                 while (*c && !isdigit(*c)) {
01400                     ++c;
01401                 }
01402                 if (!*c) {
01403                     return;    /* No y specified */
01404                 }
01405                 y = atoi(c);
01406                 send_map(i, x, y);
01407             }
01408         } else if (strncmp(c, "skills", 6) == 0) {
01409             char buf[1024];
01410             int s;
01411 
01412             for (s = 0; s < CS_NUM_SKILLS; s++) {
01413                 if (skill_names[s]) {
01414                     sprintf(buf, "request skills %d %s\n", CS_STAT_SKILLINFO + s, skill_names[s]);
01415                     write(scripts[i].out_fd, buf, strlen(buf));
01416                 }
01417             }
01418             sprintf(buf, "request skills end\n");
01419             write(scripts[i].out_fd, buf, strlen(buf));
01420         } else if (strncmp(c, "spells", 6) == 0) {
01421             char buf[1024];
01422             Spell *spell;
01423 
01424             for (spell = cpl.spelldata; spell; spell = spell->next) {
01425                 sprintf(buf, "request spells %d %d %d %d %d %d %d %d %s\n",
01426                         spell->tag, spell->level, spell->sp, spell->grace,
01427                         spell->skill_number, spell->path, spell->time,
01428                         spell->dam, spell->name);
01429                 write(scripts[i].out_fd, buf, strlen(buf));
01430             }
01431             sprintf(buf, "request spells end\n");
01432             write(scripts[i].out_fd, buf, strlen(buf));
01433         } else {
01434             char buf[1024];
01435 
01436             snprintf(buf, sizeof(buf), "Script %d %s malfunction; unimplemented request:", i+1, scripts[i].name);
01437             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01438             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01439         }
01440     } else if (strncmp(cmd, "issue", 5) == 0) {
01441         int repeat;
01442         int must_send;
01443 
01444         c = cmd+5;
01445         while (*c && *c == ' ') {
01446             ++c;
01447         }
01448         if (*c && (isdigit(*c) || *c == '-')) { /* repeat specified; use send_command() */
01449             repeat = atoi(c);
01450             while (*c && *c != ' ') {
01451                 ++c;
01452             }
01453             while (*c && !isdigit(*c) && *c != '-') {
01454                 ++c;
01455             }
01456             if (!*c) {
01457                 return;    /* No must_send specified */
01458             }
01459             must_send = atoi(c);
01460             while (*c && *c != ' ') {
01461                 ++c;
01462             }
01463             if (!*c) {
01464                 return;    /* No command specified */
01465             }
01466             while (*c == ' ') {
01467                 ++c;
01468             }
01469             if (repeat != -1) {
01470                 int r;
01471 
01472                 r = send_command(c, repeat, must_send);
01473                 if (r != 1) {
01474                     char buf[1024];
01475 
01476                     snprintf(buf, sizeof(buf), "Script %d %s malfunction; command not sent", i+1, scripts[i].name);
01477                     draw_ext_info(
01478                         NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01479                     draw_ext_info(
01480                         NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01481                 }
01482             }
01483         } else {
01484             c = cmd+5;
01485             while (*c && *c != ' ') {
01486                 ++c;
01487             }
01488             while (*c == ' ') {
01489                 ++c;
01490             }
01491 
01492             /*
01493              * Check special cases: "mark <tag>" or "lock <new state> <tag>"
01494              */
01495             if (strncmp(c, "mark", 4) == 0) {
01496                 int tag;
01497                 SockList sl;
01498                 uint8 buf[MAX_BUF];
01499 
01500                 c += 4;
01501 
01502                 while (*c && !isdigit(*c)) {
01503                     ++c;
01504                 }
01505                 if (!*c) {
01506                     return;    /* No tag specified */
01507                 }
01508                 tag = atoi(c);
01509 
01510                 SockList_Init(&sl, buf);
01511                 SockList_AddString(&sl, "mark ");
01512                 SockList_AddInt(&sl, tag);
01513                 SockList_Send(&sl, csocket.fd);
01514             } else if (strncmp(c, "lock", 4) == 0) {
01515                 int tag, locked;
01516                 SockList sl;
01517                 uint8 buf[MAX_BUF];
01518 
01519                 c += 4;
01520 
01521                 while (*c && !isdigit(*c)) {
01522                     ++c;
01523                 }
01524                 if (!*c) {
01525                     return;    /* No state specified */
01526                 }
01527                 locked = atoi(c);
01528                 while (*c && *c != ' ') {
01529                     ++c;
01530                 }
01531                 while (*c && !isdigit(*c)) {
01532                     ++c;
01533                 }
01534                 if (!*c) {
01535                     return;    /* No tag specified */
01536                 }
01537                 tag = atoi(c);
01538 
01539                 SockList_Init(&sl, buf);
01540                 SockList_AddString(&sl, "lock ");
01541                 SockList_AddChar(&sl, locked);
01542                 SockList_AddInt(&sl, tag);
01543                 SockList_Send(&sl, csocket.fd);
01544             } else {
01545                 cs_print_string(csocket.fd, "%s", c);
01546             }
01547         }
01548     } else if (strncmp(cmd, "localcmd", 8) == 0) {
01549         char *param;
01550 
01551         c = cmd+8;
01552         while (*c == ' ') {
01553             c++;
01554         }
01555         param = c;
01556         while ((*param != '\0') && (*param != ' ')) {
01557             param++;
01558         }
01559         if (*param == ' ') {
01560             *param = '\0';
01561             param++;
01562         } else {
01563             param = NULL;
01564         }
01565 
01566         if (!handle_local_command(c, param)) {
01567             char buf[1024];
01568 
01569             snprintf(buf, sizeof(buf), "Script %s malfunction; localcmd not understood", scripts[i].name);
01570             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01571             snprintf(buf, sizeof(buf), "Script <<localcmd %s %s>>", c, (param == NULL) ? "" : param);
01572             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01573         }
01574     } else if (strncmp(cmd, "draw", 4) == 0) {
01575         int color;
01576 
01577         c = cmd+4;
01578         while (*c && !isdigit(*c)) {
01579             ++c;
01580         }
01581         if (!*c) {
01582             return;    /* No color specified */
01583         }
01584         color = atoi(c);
01585         while (*c && *c != ' ') {
01586             ++c;
01587         }
01588         if (!*c) {
01589             return;    /* No message specified */
01590         }
01591         while (*c == ' ') {
01592             ++c;
01593         }
01594         draw_ext_info(color, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, c);
01595     } else if (strncmp(cmd, "monitor", 7) == 0) {
01596         scripts[i].monitor = 1;
01597     } else if (strncmp(cmd, "unmonitor", 9) == 0) {
01598         scripts[i].monitor = 0;
01599     } else {
01600         char buf[1024];
01601 
01602         snprintf(buf, sizeof(buf), "Script %d %s malfunction; invalid command:", i+1, scripts[i].name);
01603         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01604         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01605     }
01606 }
01607 
01608 /*
01609  * script_send_item()
01610  *
01611  * Send one line to the script with item information.
01612  *
01613  * A header string is passed in.  The format is:
01614  *
01615  * <header>  tag num weight flags type name
01616  *
01617  * flags are a bitmask:
01618  * unidentified, magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated
01619  *      512        256     128     64      32       16      8       4      2         1
01620  */
01621 static void script_send_item(int i, const char *head, const item *it)
01622 {
01623     char buf[4096];
01624     int flags;
01625 
01626     flags = it->flagsval&F_UNIDENTIFIED?1:0;
01627     flags = (flags<<1)|it->magical;
01628     flags = (flags<<1)|it->cursed;
01629     flags = (flags<<1)|it->damned;
01630     flags = (flags<<1)|it->unpaid;
01631     flags = (flags<<1)|it->locked;
01632     flags = (flags<<1)|it->applied;
01633     flags = (flags<<1)|it->open;
01634     flags = (flags<<1)|it->was_open;
01635     flags = (flags<<1)|it->inv_updated;
01636     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);
01637     write(scripts[i].out_fd, buf, strlen(buf));
01638 }
01639 
01640 #endif /* CPROTO */