Atrinik Client 2.5
client/scripts.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00032 #ifndef WIN32
00033 #   include <sys/wait.h>
00034 #endif
00035 
00036 static void script_dead(int i);
00037 static void script_process_cmd(int i);
00038 static int script_by_name(const char *name);
00039 static void script_send_item(int i, const char *head, const object *it);
00040 
00042 static struct script *scripts = NULL;
00043 
00045 static int num_scripts = 0;
00046 
00047 #ifdef WIN32
00048 
00049 #define write(x, y, z) emulate_write(x, y, z)
00050 #define read(x, y, z) emulate_read(x, y, z)
00051 
00052 static int emulate_read(HANDLE fd, char *buf, size_t len)
00053 {
00054     DWORD dwBytesRead;
00055     BOOL rc;
00056 
00057     FlushFileBuffers(fd);
00058     rc = ReadFile(fd, buf, (DWORD) len, &dwBytesRead, NULL);
00059 
00060     if (rc == 0)
00061     {
00062         return -1;
00063     }
00064 
00065     buf[dwBytesRead] = '\0';
00066 
00067     return dwBytesRead;
00068 }
00069 
00070 static int emulate_write(HANDLE fd, const char *buf, size_t len)
00071 {
00072     DWORD dwBytesWritten;
00073     BOOL rc;
00074 
00075     rc = WriteFile(fd, buf, (DWORD) len, &dwBytesWritten, NULL);
00076     FlushFileBuffers(fd);
00077 
00078     if (rc == 0)
00079     {
00080         return -1;
00081     }
00082 
00083     return dwBytesWritten;
00084 }
00085 
00086 #endif
00087 
00092 void script_load(const char *cparams)
00093 {
00094 #ifndef WIN32
00095     pid_t pid;
00096     int fds[2], i = 1;
00097     char *argv[256];
00098     struct stat statbuf;
00099 #else
00100     SECURITY_ATTRIBUTES saAttr;
00101     PROCESS_INFORMATION piProcInfo;
00102     STARTUPINFO siStartupInfo;
00103     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd;
00104     HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout;
00105 #endif
00106     char *name, *args, params[MAX_BUF];
00107 
00108     if (!cparams)
00109     {
00110         draw_info(COLOR_RED, "Please specify a script to execute.");
00111         return;
00112     }
00113 
00114     /* cparams as passed in is a const value, so need to copy it
00115      * to data we can write over. */
00116     strncpy(params, cparams, MAX_BUF - 1);
00117     params[MAX_BUF - 1] = '\0';
00118 
00119     /* Get name and args */
00120     name = params;
00121     args = name;
00122 
00123     while (*args && *args != ' ')
00124     {
00125         args++;
00126     }
00127 
00128     while (*args && *args == ' ')
00129     {
00130         *args++ = '\0';
00131     }
00132 
00133     if (*args == '\0')
00134     {
00135         args = NULL;
00136     }
00137 
00138 #ifndef WIN32
00139     /* Fill in argv[] */
00140     argv[0] = name;
00141 
00142     while (args && *args && i < (int) (sizeof(argv) / sizeof(*argv)) - 1)
00143     {
00144         argv[i++] = args;
00145 
00146         while (*args && *args != ' ')
00147         {
00148             args++;
00149         }
00150 
00151         while (*args && *args == ' ')
00152         {
00153             *args++ = '\0';
00154         }
00155     }
00156 
00157     argv[i] = NULL;
00158 
00159     if (stat(argv[0], &statbuf) || !S_ISREG(statbuf.st_mode) || !(statbuf.st_mode & S_IXUSR))
00160     {
00161         draw_info(COLOR_RED, "The script does not exist, is not a regular file or is not executable.");
00162         return;
00163     }
00164 
00165     /* Create a pair of sockets */
00166     if (socketpair(PF_LOCAL, SOCK_STREAM, AF_LOCAL, fds))
00167     {
00168         draw_info(COLOR_RED, "Unable to start script: socketpair failed.");
00169         return;
00170     }
00171 
00172     pid = fork();
00173 
00174     if (pid == -1)
00175     {
00176         close(fds[0]);
00177         close(fds[1]);
00178 
00179         draw_info(COLOR_RED, "Unable to start script: fork failed.");
00180         return;
00181     }
00182 
00183     if (pid == 0)
00184     {
00185         int r;
00186 
00187         /* Clean up file descriptor space */
00188         r = dup2(fds[0], 0);
00189 
00190         if (r != 0)
00191         {
00192             fprintf(stderr, "Script Child: Failed to set pipe as stdin\n");
00193         }
00194 
00195         r = dup2(fds[0], 1);
00196 
00197         if (r != 1)
00198         {
00199             fprintf(stderr, "Script Child: Failed to set pipe as stdout\n");
00200         }
00201 
00202         /* Execute */
00203         r = execvp(argv[0], argv);
00204 
00205         if (r != -1)
00206         {
00207             printf("draw %s Script child: no error, but no execvp().\n", COLOR_RED);
00208         }
00209         else
00210         {
00211             printf("draw %s Script child failed to start: %s\n", COLOR_RED, strerror(errno));
00212         }
00213 
00214         exit(1);
00215     }
00216 
00217     close(fds[0]);
00218 
00219     if (fcntl(fds[1], F_SETFL, O_NDELAY) == -1)
00220     {
00221         LOG(llevDebug, "Error on fcntl.\n");
00222     }
00223 #else
00224     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
00225     saAttr.bInheritHandle = 1;
00226     saAttr.lpSecurityDescriptor = NULL;
00227 
00228     hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
00229 
00230     if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
00231     {
00232         draw_info(COLOR_RED, "script_load(): stdout CreatePipe() failed.");
00233         return;
00234     }
00235 
00236     if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
00237     {
00238         draw_info(COLOR_RED, "script_load(): Failed to redirect stdout using SetStdHandle().");
00239         return;
00240     }
00241 
00242     if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, 0, DUPLICATE_SAME_ACCESS))
00243     {
00244         draw_info(COLOR_RED, "script_load(): Failed to duplicate stdout using DuplicateHandle().");
00245         return;
00246     }
00247 
00248     CloseHandle(hChildStdoutRd);
00249 
00250     hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
00251 
00252     if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
00253     {
00254         draw_info(COLOR_RED, "script_load(): stdin CreatePipe() failed.");
00255         return;
00256     }
00257 
00258     if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
00259     {
00260         draw_info(COLOR_RED, "script_load(): Failed to redirect stdin using SetStdHandle().");
00261         return;
00262     }
00263 
00264     if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, 0, DUPLICATE_SAME_ACCESS))
00265     {
00266         draw_info(COLOR_RED, "script_load(): failed to duplicate stdin using DuplicateHandle()");
00267         return;
00268     }
00269 
00270     CloseHandle(hChildStdinWr);
00271 
00272     ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
00273     ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO));
00274     siStartupInfo.cb = sizeof(STARTUPINFO);
00275 
00276     if (args)
00277     {
00278         args[-1] = ' ';
00279     }
00280 
00281     if (!CreateProcess(NULL, name, NULL, NULL, 1, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo))
00282     {
00283         draw_info(COLOR_RED, "script_load(): CreateProcess() failed.");
00284         return;
00285     }
00286 
00287     CloseHandle(piProcInfo.hThread);
00288 
00289     if (args)
00290     {
00291         args[-1] = '\0';
00292     }
00293 
00294     if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
00295     {
00296         draw_info(COLOR_RED, "script_load(): Restoring original stdin failed.");
00297         return;
00298     }
00299 
00300     if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
00301     {
00302         draw_info(COLOR_RED, "script_load(): Restoring original stdout failed.");
00303         return;
00304     }
00305 #endif
00306 
00307     /* realloc script array to add new entry; fill in the data */
00308     scripts = realloc(scripts, sizeof(scripts[0]) * (num_scripts + 1));
00309     scripts[num_scripts].name = strdup(cparams);
00310     scripts[num_scripts].params = args ? strdup(args) : NULL;
00311 
00312 #ifndef WIN32
00313     scripts[num_scripts].out_fd = fds[1];
00314     scripts[num_scripts].in_fd = fds[1];
00315     scripts[num_scripts].pid = pid;
00316 #else
00317     scripts[num_scripts].out_fd = hChildStdinWrDup;
00318     scripts[num_scripts].in_fd = hChildStdoutRdDup;
00319     scripts[num_scripts].pid = piProcInfo.dwProcessId;
00320     scripts[num_scripts].process = piProcInfo.hProcess;
00321 #endif
00322     scripts[num_scripts].cmd_count = 0;
00323     scripts[num_scripts].events = NULL;
00324     scripts[num_scripts].events_count = 0;
00325 
00326     num_scripts++;
00327 }
00328 
00330 void script_list()
00331 {
00332     if (num_scripts == 0)
00333     {
00334         draw_info(COLOR_WHITE, "No scripts are currently running.");
00335     }
00336     else
00337     {
00338         int i;
00339 
00340         draw_info_format(COLOR_WHITE, "%d scripts are currently running:", num_scripts);
00341 
00342         for (i = 0; i < num_scripts; i++)
00343         {
00344             if (scripts[i].params)
00345             {
00346                 draw_info_format(COLOR_WHITE, "%d %s  %s", i + 1, scripts[i].name, scripts[i].params);
00347             }
00348             else
00349             {
00350                 draw_info_format(COLOR_WHITE, "%d %s", i + 1, scripts[i].name);
00351             }
00352         }
00353     }
00354 }
00355 
00358 void script_process()
00359 {
00360     int i, r;
00361 #ifdef WIN32
00362     DWORD nAvailBytes = 0, dwStatus;
00363     char cTmp;
00364     BOOL bRC, bStatus;
00365 #else
00366     fd_set tmp_read;
00367     int pollret;
00368     struct timeval timeout;
00369 #endif
00370 
00371     /* Determine which script's fd is set */
00372     for (i = 0; i < num_scripts; i++)
00373     {
00374 #ifndef WIN32
00375         FD_ZERO(&tmp_read);
00376         FD_SET(scripts[i].in_fd, &tmp_read);
00377 
00378         timeout.tv_sec = 0;
00379         timeout.tv_usec = 0;
00380 
00381         if ((pollret = select(scripts[i].in_fd + 1, &tmp_read, NULL, NULL, &timeout)) == -1)
00382         {
00383             LOG(llevDebug, "Got errno %d on select call: %s.\n", errno, strerror(errno));
00384         }
00385 
00386         if (FD_ISSET(scripts[i].in_fd, &tmp_read))
00387 #else
00388         bStatus = GetExitCodeProcess(scripts[i].process, &dwStatus);
00389         bRC = PeekNamedPipe(scripts[i].in_fd, &cTmp, 1, NULL, &nAvailBytes, NULL);
00390 
00391         if (nAvailBytes)
00392 #endif
00393         {
00394             /* Read in script[i].cmd */
00395             r = read(scripts[i].in_fd, scripts[i].cmd + scripts[i].cmd_count, sizeof(scripts[i].cmd) - scripts[i].cmd_count - 1);
00396 
00397             if (r > 0)
00398             {
00399                 scripts[i].cmd_count += r;
00400             }
00401 #ifndef WIN32
00402             else if (r == 0 || errno == EBADF)
00403 #else
00404             else if (r == 0 || GetLastError() == ERROR_BROKEN_PIPE)
00405 #endif
00406             {
00407                 /* Script has exited; delete it */
00408                 script_dead(i);
00409                 return;
00410             }
00411 
00412             scripts[i].cmd[scripts[i].cmd_count] = '\0';
00413 
00414             /* If a newline or full buffer has been reached, process it */
00415             while (scripts[i].cmd_count == sizeof(scripts[i].cmd) - 1 || strchr(scripts[i].cmd, '\n'))
00416             {
00417                 script_process_cmd(i);
00418                 scripts[i].cmd[scripts[i].cmd_count] = '\0';
00419             }
00420 
00421             /* Only process one script at a time */
00422             return;
00423         }
00424 #ifdef WIN32
00425         /* Error: assume dead */
00426         else if (!bRC || (bStatus && (dwStatus != STILL_ACTIVE)))
00427         {
00428             script_dead(i);
00429         }
00430 #endif
00431     }
00432 }
00433 
00437 static void script_process_cmd(int i)
00438 {
00439     char cmd[HUGE_BUF], *c;
00440     int l;
00441 
00442     /* Strip out just this one command */
00443     for (l = 0; l < scripts[i].cmd_count; l++)
00444     {
00445         if (scripts[i].cmd[l] == '\n')
00446         {
00447             break;
00448         }
00449     }
00450 
00451     l++;
00452     memcpy(cmd, scripts[i].cmd, l);
00453 
00454 #ifndef WIN32
00455     cmd[l - 1] = '\0';
00456 #else
00457     cmd[l - 2] = '\0';
00458 #endif
00459 
00460     if (l < scripts[i].cmd_count)
00461     {
00462         memmove(scripts[i].cmd, scripts[i].cmd + l, scripts[i].cmd_count - l);
00463         scripts[i].cmd_count -= l;
00464     }
00465     else
00466     {
00467         scripts[i].cmd_count = 0;
00468     }
00469 
00470     /* Now the data in scripts[i] is ready for the next read.
00471      * We have a complete command in cmd[].
00472      * Process it. */
00473     if (!strncmp(cmd, "draw ", 5))
00474     {
00475         char color[COLOR_BUF];
00476         const char *cp = cmd;
00477 
00478         cp += 5;
00479 
00480         snprintf(color, sizeof(color), "%s", cp);
00481         color[sizeof(color) - 1] = '\0';
00482         cp += 7;
00483 
00484         draw_info(color, cp);
00485     }
00486     else if (!strncmp(cmd, "log ", 4))
00487     {
00488         int log_level;
00489 
00490         c = cmd + 4;
00491 
00492         while (*c && !isdigit(*c))
00493         {
00494             c++;
00495         }
00496 
00497         /* No log level specified */
00498         if (!*c)
00499         {
00500             LOG(llevBug, "script_process_cmd(): Log command did not have log level set.\n");
00501             return;
00502         }
00503 
00504         log_level = atoi(c);
00505 
00506         while (*c && *c != ' ')
00507         {
00508             c++;
00509         }
00510 
00511         /* No log message specified */
00512         if (!*c)
00513         {
00514             LOG(llevBug, "script_process_cmd(): Log command did not have log message set.\n");
00515             return;
00516         }
00517 
00518         while (*c == ' ')
00519         {
00520             c++;
00521         }
00522 
00523         LOG(log_level, "%s\n", c);
00524     }
00525     else if (!strncmp(cmd, "request ", 8))
00526     {
00527         char buf[MAX_BUF];
00528         int w = 0;
00529 
00530         c = cmd + 8;
00531 
00532         if (!strncmp(c, "player", 6))
00533         {
00534             snprintf(buf, sizeof(buf), "request player %d %s:%s\n", cpl.ob->tag, cpl.name, cpl.ext_title);
00535             w = write(scripts[i].out_fd, buf, strlen(buf));
00536         }
00537         else if (!strncmp(c, "weight", 5))
00538         {
00539             snprintf(buf, sizeof(buf), "request weight %d %d %d\n", cpl.weight_limit, (int) (cpl.ob->weight * 1000), (int) (cpl.real_weight * 1000));
00540             w = write(scripts[i].out_fd, buf, strlen(buf));
00541         }
00542         else if (!strncmp(c, "stat ", 5))
00543         {
00544             c += 5;
00545 
00546             if (!strncmp(c, "stats", 5))
00547             {
00548                 snprintf(buf, sizeof(buf), "request stat stats %d %d %d %d %d %d %d\n", cpl.stats.Str, cpl.stats.Dex, cpl.stats.Con, cpl.stats.Int, cpl.stats.Wis, cpl.stats.Pow, cpl.stats.Cha);
00549                 w = write(scripts[i].out_fd, buf, strlen(buf));
00550             }
00551             else if (!strncmp(c, "combat", 6))
00552             {
00553                 snprintf(buf, sizeof(buf), "request stat combat %d %d %d %d %d\n", cpl.stats.wc, cpl.stats.ac, cpl.stats.dam, cpl.stats.speed, cpl.stats.weapon_sp);
00554                 w = write(scripts[i].out_fd, buf, strlen(buf));
00555             }
00556             else if (!strncmp(c, "hp", 2))
00557             {
00558                 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);
00559                 w = write(scripts[i].out_fd,buf,strlen(buf));
00560             }
00561             else if (!strncmp(c, "exp", 3))
00562             {
00563                 int s;
00564 
00565                 snprintf(buf, sizeof(buf), "request stat exp %d %"FMT64, cpl.stats.level, cpl.stats.exp);
00566                 w = write(scripts[i].out_fd, buf, strlen(buf));
00567 
00568                 for (s = 0; s < MAX_SKILL; s++)
00569                 {
00570                     snprintf(buf, sizeof(buf), " %d %"FMT64, cpl.stats.skill_level[s], cpl.stats.skill_exp[s]);
00571                     w = write(scripts[i].out_fd, buf, strlen(buf));
00572                 }
00573 
00574                 w = write(scripts[i].out_fd, "\n", 1);
00575             }
00576             else if (!strncmp(c, "protections", 11))
00577             {
00578                 int s;
00579 
00580                 snprintf(buf, sizeof(buf), "request stat protections");
00581                 w = write(scripts[i].out_fd, buf, strlen(buf));
00582 
00583                 for (s = CS_STAT_PROT_START; s <= CS_STAT_PROT_END; s++)
00584                 {
00585                     snprintf(buf, sizeof(buf), " %d", cpl.stats.protection[s - CS_STAT_PROT_START]);
00586                     w = write(scripts[i].out_fd, buf, strlen(buf));
00587                 }
00588 
00589                 w = write(scripts[i].out_fd, "\n", 1);
00590             }
00591         }
00592         else if (!strncmp(c, "items ", 6))
00593         {
00594             c += 6;
00595 
00596             if (!strncmp(c, "inv", 3))
00597             {
00598                 object *it = cpl.ob->inv;
00599 
00600                 while (it)
00601                 {
00602                     script_send_item(i, "request items inv ", it);
00603                     it = it->next;
00604                 }
00605 
00606                 strcpy(buf, "request items inv end\n");
00607                 w = write(scripts[i].out_fd, buf, strlen(buf));
00608             }
00609             else if (!strncmp(c, "applied", 7))
00610             {
00611                 object *it = cpl.ob->inv;
00612 
00613                 while (it)
00614                 {
00615                     if (it->flags & F_APPLIED)
00616                     {
00617                         script_send_item(i, "request items applied ", it);
00618                     }
00619 
00620                     it = it->next;
00621                 }
00622 
00623                 strcpy(buf, "request items applied end\n");
00624                 w = write(scripts[i].out_fd, buf, strlen(buf));
00625             }
00626             else if (!strncmp(c, "below", 5))
00627             {
00628                 object *it = cpl.below->inv;
00629 
00630                 while (it)
00631                 {
00632                     script_send_item(i, "request items below ", it);
00633                     it = it->next;
00634                 }
00635 
00636                 strcpy(buf, "request items below end\n");
00637                 w = write(scripts[i].out_fd, buf, strlen(buf));
00638             }
00639         }
00640         else
00641         {
00642             draw_info_format(COLOR_RED, "Script %d %s malfunction; unimplemented request: %s", i + 1, scripts[i].name, cmd);
00643         }
00644 
00645         if (w < 0)
00646         {
00647             LOG(llevBug, "script_process_cmd(): Write system call failed.\n");
00648         }
00649     }
00650     else if (!strncmp(cmd, "issue ", 6))
00651     {
00652         c = cmd + 6;
00653 
00654         if (!strncmp(c, "command ", 8))
00655         {
00656             c += 8;
00657 
00658             if (!client_command_check(c))
00659             {
00660                 send_command(c);
00661             }
00662         }
00663         else if (!strncmp(c, "string ", 7))
00664         {
00665             c += 7;
00666 
00667             cs_write_string(c, strlen(c));
00668         }
00669     }
00670     else if (!strncmp(cmd, "event ", 6))
00671     {
00672         c = cmd + 6;
00673 
00674         if (!strncmp(c, "register ", 9))
00675         {
00676             c += 9;
00677 
00678             c = strdup(c);
00679             scripts[i].events = realloc(scripts[i].events, (scripts[i].events_count + 1) * sizeof(scripts[i].events[1]));
00680             scripts[i].events[scripts[i].events_count] = c;
00681             scripts[i].events_count++;
00682         }
00683         else if (!strncmp(c, "unregister ", 11))
00684         {
00685             int j;
00686 
00687             c += 11;
00688 
00689             for (j = 0; j < scripts[i].events_count; j++)
00690             {
00691                 if (strcmp(c, scripts[i].events[j]) == 0 )
00692                 {
00693                     free(scripts[i].events[j]);
00694 
00695                     while (j + 1 < scripts[i].events_count)
00696                     {
00697                         scripts[i].events[j] = scripts[i].events[j + 1];
00698                         j++;
00699                     }
00700 
00701                     scripts[i].events_count--;
00702 
00703                     break;
00704                 }
00705             }
00706         }
00707     }
00708     else
00709     {
00710         draw_info_format(COLOR_RED, "Script %d %s malfunction; invalid command: %s", i + 1, scripts[i].name, cmd);
00711     }
00712 }
00713 
00719 static void script_send_item(int i, const char *head, const object *it)
00720 {
00721     char buf[HUGE_BUF];
00722     int w;
00723 
00724     snprintf(buf, sizeof(buf), "%s%d %d %f %d %d %s\n", head, it->tag, it->nrof, it->weight, it->flags, it->itype, it->s_name);
00725     w = write(scripts[i].out_fd, buf, strlen(buf));
00726 
00727     if (w < 0)
00728     {
00729         LOG(llevBug, "script_send_item(): Write system call failed.\n");
00730     }
00731 }
00732 
00739 int script_trigger_event(const char *cmd, const uint8 *data, const int data_len, const enum CmdFormat format)
00740 {
00741     int i, e, w, len;
00742 
00743     /* For each script... */
00744     for (i = 0; i < num_scripts; ++i)
00745     {
00746         /* For each registered event */
00747         for (e = 0; e < scripts[i].events_count; e++)
00748         {
00749             char buf[10240];
00750 
00751             if (strcmp(cmd, scripts[i].events[e]))
00752             {
00753                 continue;
00754             }
00755 
00756             len = data_len;
00757 
00758             switch (format)
00759             {
00760                 case ASCII:
00761                     snprintf(buf, sizeof(buf), "event %s %s\n", cmd, data);
00762                     break;
00763 
00764                 case SHORT_INT:
00765                     snprintf(buf, sizeof(buf), "event %s %d %d\n", cmd, GetShort_String(data), GetInt_String(data + 2));
00766                     break;
00767 
00768                 case SHORT_ARRAY:
00769                 {
00770                     int be, p;
00771 
00772                     be = snprintf(buf, sizeof(buf), "event %s", cmd);
00773 
00774                     for (p = 0; p * 2 < len && p < 100; ++p)
00775                     {
00776                         be += snprintf(buf + be, sizeof(buf) - be, " %d", GetShort_String(data + p * 2));
00777                     }
00778 
00779                     be += snprintf(buf + be, sizeof(buf) - be, "\n");
00780                     break;
00781                 }
00782 
00783                 case INT_ARRAY:
00784                 {
00785                     int be, p;
00786 
00787                     be = snprintf(buf, sizeof(buf), "event %s", cmd);
00788 
00789                     for (p = 0; p * 4 < len; ++p)
00790                     {
00791                         be += snprintf(buf + be, sizeof(buf) - be, " %d", GetInt_String(data + p * 4));
00792                     }
00793 
00794                     be += snprintf(buf + be, sizeof(buf) - be, "\n");
00795                     break;
00796                 }
00797 
00798                 case STATS:
00799                 {
00800                     int j = 0, c, be = 0;
00801 
00802                     while (j < len)
00803                     {
00804                         c = data[j++];
00805 
00806                         be += snprintf(buf + be, sizeof(buf) - be, "event %s", cmd);
00807 
00808                         if (c >= CS_STAT_PROT_START && c <= CS_STAT_PROT_END)
00809                         {
00810                             be += snprintf(buf + be, sizeof(buf) - be, " protects %d %d\n", c - CS_STAT_PROT_START, (sint16) *(data + j++));
00811                         }
00812                         else
00813                         {
00814                             switch (c)
00815                             {
00816                                 case CS_STAT_TARGET_HP:
00817                                     be += snprintf(buf + be, sizeof(buf) - be, " target_hp %d\n", (int)*(data + j++));
00818                                     cpl.target_hp = (int)*(data + j++);
00819                                     break;
00820 
00821                                 case CS_STAT_REG_HP:
00822                                     be += snprintf(buf + be, sizeof(buf) - be, " regen_hp %f\n", GetShort_String(data + j) / 10.0f);
00823                                     j += 2;
00824                                     break;
00825 
00826                                 case CS_STAT_REG_MANA:
00827                                     be += snprintf(buf + be, sizeof(buf) - be, " regen_mana %f\n", GetShort_String(data + j) / 10.0f);
00828                                     j += 2;
00829                                     break;
00830 
00831                                 case CS_STAT_REG_GRACE:
00832                                     be += snprintf(buf + be, sizeof(buf) - be, " regen_grace %f\n", GetShort_String(data + j) / 10.0f);
00833                                     j += 2;
00834                                     break;
00835 
00836                                 case CS_STAT_HP:
00837                                     be += snprintf(buf + be, sizeof(buf) - be, " hp %d\n", GetInt_String(data + j));
00838                                     j += 4;
00839                                     break;
00840 
00841                                 case CS_STAT_MAXHP:
00842                                     be += snprintf(buf + be, sizeof(buf) - be, " maxhp %d\n", GetInt_String(data + j));
00843                                     j += 4;
00844                                     break;
00845 
00846                                 case CS_STAT_SP:
00847                                     be += snprintf(buf + be, sizeof(buf) - be, " sp %d\n", GetShort_String(data + j));
00848                                     j += 2;
00849                                     break;
00850 
00851                                 case CS_STAT_MAXSP:
00852                                     be += snprintf(buf + be, sizeof(buf) - be, " maxsp %d\n", GetShort_String(data + j));
00853                                     j += 2;
00854                                     break;
00855 
00856                                 case CS_STAT_GRACE:
00857                                     be += snprintf(buf + be, sizeof(buf) - be, " grace %d\n", GetShort_String(data + j));
00858                                     j += 2;
00859                                     break;
00860 
00861                                 case CS_STAT_MAXGRACE:
00862                                     be += snprintf(buf + be, sizeof(buf) - be, " maxgrace %d\n", GetShort_String(data + j));
00863                                     j += 2;
00864                                     break;
00865 
00866                                 case CS_STAT_STR:
00867                                     be += snprintf(buf + be, sizeof(buf) - be, " str %d\n", (int) *(data + j++));
00868                                     break;
00869 
00870                                 case CS_STAT_INT:
00871                                     be += snprintf(buf + be, sizeof(buf) - be, " int %d\n", (int) *(data + j++));
00872                                     break;
00873 
00874                                 case CS_STAT_POW:
00875                                     be += snprintf(buf + be, sizeof(buf) - be, " pow %d\n", (int) *(data + j++));
00876                                     break;
00877 
00878                                 case CS_STAT_WIS:
00879                                     be += snprintf(buf + be, sizeof(buf) - be, " wis %d\n", (int) *(data + j++));
00880                                     break;
00881 
00882                                 case CS_STAT_DEX:
00883                                     be += snprintf(buf + be, sizeof(buf) - be, " dex %d\n", (int) *(data + j++));
00884                                     break;
00885 
00886                                 case CS_STAT_CON:
00887                                     be += snprintf(buf + be, sizeof(buf) - be, " con %d\n", (int) *(data + j++));
00888                                     break;
00889 
00890                                 case CS_STAT_CHA:
00891                                     be += snprintf(buf + be, sizeof(buf) - be, " cha %d\n", (int) *(data + j++));
00892                                     break;
00893 
00894                                 case CS_STAT_EXP:
00895                                     be += snprintf(buf + be, sizeof(buf) - be, " exp %d\n", GetInt_String(data + j));
00896                                     j += 4;
00897                                     break;
00898 
00899                                 case CS_STAT_LEVEL:
00900                                     be += snprintf(buf + be, sizeof(buf) - be, " level %d\n", (char) *(data + j++));
00901                                     break;
00902 
00903                                 case CS_STAT_WC:
00904                                     be += snprintf(buf + be, sizeof(buf) - be, " wc %d\n", (char) GetShort_String(data + j));
00905                                     j += 2;
00906                                     break;
00907 
00908                                 case CS_STAT_AC:
00909                                     be += snprintf(buf + be, sizeof(buf) - be, " ac %d\n", (char) GetShort_String(data + j));
00910                                     j += 2;
00911                                     break;
00912 
00913                                 case CS_STAT_DAM:
00914                                     be += snprintf(buf + be, sizeof(buf) - be, " dam %d\n", GetShort_String(data + j));
00915                                     j += 2;
00916                                     break;
00917 
00918                                 case CS_STAT_SPEED:
00919                                     be += snprintf(buf + be, sizeof(buf) - be, " speed %d\n", GetInt_String(data + j));
00920                                     j += 4;
00921                                     break;
00922 
00923                                 case CS_STAT_FOOD:
00924                                     be += snprintf(buf + be, sizeof(buf) - be, " food %d\n", GetShort_String(data + j));
00925                                     j += 2;
00926                                     break;
00927 
00928                                 case CS_STAT_WEAP_SP:
00929                                     be += snprintf(buf + be, sizeof(buf) - be, " weapon_speed %d\n", (int)*(data + j++));
00930                                     break;
00931 
00932                                 case CS_STAT_FLAGS:
00933                                     be += snprintf(buf + be, sizeof(buf) - be, " flags %d\n", GetShort_String(data + j));
00934                                     j += 2;
00935                                     break;
00936 
00937                                 case CS_STAT_WEIGHT_LIM:
00938                                     be += snprintf(buf + be, sizeof(buf) - be, " weight_limit %d\n", GetInt_String(data + j));
00939                                     j += 4;
00940                                     break;
00941 
00942                                 case CS_STAT_ACTION_TIME:
00943                                     be += snprintf(buf + be, sizeof(buf) - be, " action_time %f\n", abs(GetInt_String(data + j)) / 1000.0f);
00944                                     j += 4;
00945                                     break;
00946 
00947                                 case CS_STAT_SKILLEXP_AGILITY:
00948                                 case CS_STAT_SKILLEXP_PERSONAL:
00949                                 case CS_STAT_SKILLEXP_MENTAL:
00950                                 case CS_STAT_SKILLEXP_PHYSIQUE:
00951                                 case CS_STAT_SKILLEXP_MAGIC:
00952                                 case CS_STAT_SKILLEXP_WISDOM:
00953                                     be += snprintf(buf + be, sizeof(buf) - be, " skill_exp %d %"FMT64"\n", (c - CS_STAT_SKILLEXP_START) / 2, GetInt64_String(data + j));
00954                                     j += 8;
00955                                     break;
00956 
00957                                 case CS_STAT_SKILLEXP_AGLEVEL:
00958                                 case CS_STAT_SKILLEXP_PELEVEL:
00959                                 case CS_STAT_SKILLEXP_MELEVEL:
00960                                 case CS_STAT_SKILLEXP_PHLEVEL:
00961                                 case CS_STAT_SKILLEXP_MALEVEL:
00962                                 case CS_STAT_SKILLEXP_WILEVEL:
00963                                     be += snprintf(buf + be, sizeof(buf) - be, " skill_level %d %d\n", (c - CS_STAT_SKILLEXP_START - 1) / 2, (sint16)*(data + j++));
00964                                     break;
00965 
00966                                 case CS_STAT_RANGE:
00967                                 {
00968                                     int rlen = data[j++];
00969 
00970                                     be += snprintf(buf + be, sizeof(buf) - be, " range %s\n", cpl.range);
00971                                     j += rlen;
00972                                     break;
00973                                 }
00974 
00975                                 case CS_STAT_EXT_TITLE:
00976                                 {
00977                                     int rlen = data[j++];
00978 
00979                                     be += snprintf(buf + be, sizeof(buf) - be, " ext_title %s\n", cpl.ext_title);
00980                                     j += rlen;
00981                                     break;
00982                                 }
00983 
00984                                 default:
00985                                     j = len;
00986                                     break;
00987                             }
00988                         }
00989                     }
00990 
00991                     break;
00992                 }
00993 
00994                 case MIXED:
00995                 case NODATA:
00996                 default:
00997                 {
00998                     int be, p;
00999 
01000                     /* We may receive null data, in which case len has no meaning */
01001                     if (!data)
01002                     {
01003                         len = 0;
01004                     }
01005 
01006                     be = snprintf(buf, sizeof(buf), "event %s %d bytes unparsed:", cmd, len);
01007 
01008                     for (p = 0; p < len && p < 100; ++p)
01009                     {
01010                         be += snprintf(buf + be, sizeof(buf) - be, " %02x", data[p]);
01011                     }
01012 
01013                     be += snprintf(buf + be, sizeof(buf) - be, "\n");
01014                     break;
01015                 }
01016             }
01017 
01018             w = write(scripts[i].out_fd, buf, strlen(buf));
01019 
01020             if (w < 0)
01021             {
01022                 LOG(llevBug, "script_trigger_event(): Write system call failed.\n");
01023             }
01024         }
01025     }
01026 
01027     return 0;
01028 }
01029 
01033 void script_send(const char *params)
01034 {
01035     int i = 0, w;
01036     char *p;
01037 
01038     if (!params)
01039     {
01040         return;
01041     }
01042 
01043     p = strchr(params, ' ');
01044 
01045     if (!p)
01046     {
01047         draw_info(COLOR_RED, "No message to send specified.");
01048         return;
01049     }
01050 
01051     while (*p == ' ')
01052     {
01053         *p++ = '\0';
01054     }
01055 
01056     i = script_by_name(params);
01057 
01058     if (i < 0)
01059     {
01060         draw_info(COLOR_RED, "No such running script.");
01061         return;
01062     }
01063 
01064     /* Send the message */
01065     w = write(scripts[i].out_fd, "scriptsend ", 11);
01066     w = write(scripts[i].out_fd, p, strlen(p));
01067     w = write(scripts[i].out_fd, "\n", 1);
01068 
01069     if (w < 0)
01070     {
01071         LOG(llevBug, "script_send(): Write system call failed.\n");
01072     }
01073 }
01074 
01078 void script_killall()
01079 {
01080 #ifdef WIN32
01081     while (num_scripts > 0)
01082     {
01083         GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid);
01084         script_dead(0);
01085     }
01086 #endif
01087 }
01088 
01093 static void script_dead(int i)
01094 {
01095     /* Release resources */
01096 #ifndef WIN32
01097     close(scripts[i].in_fd);
01098     close(scripts[i].out_fd);
01099 #else
01100     CloseHandle(scripts[i].in_fd);
01101     CloseHandle(scripts[i].out_fd);
01102     CloseHandle(scripts[i].process);
01103 #endif
01104 
01105     free(scripts[i].name);
01106     free(scripts[i].params);
01107 
01108 #ifndef WIN32
01109     waitpid(-1, NULL, WNOHANG);
01110 #endif
01111 
01112     /* Move scripts with higher index numbers down one slot */
01113     if (i < (num_scripts - 1))
01114     {
01115         memmove(&scripts[i], &scripts[i + 1], sizeof(scripts[i]) * (num_scripts - i - 1));
01116     }
01117 
01118     /* Update our count */
01119     num_scripts--;
01120 }
01121 
01127 static int script_by_name(const char *name)
01128 {
01129     int i, l = 0;
01130 
01131     if (!name)
01132     {
01133         return (num_scripts == 1 ? 0 : -1);
01134     }
01135 
01136     /* Parse script number */
01137     if (isdigit(*name))
01138     {
01139         i = atoi(name);
01140         i--;
01141 
01142         if (i >= 0 && i < num_scripts)
01143         {
01144             return i;
01145         }
01146     }
01147 
01148     while (name[l] && name[l] != ' ')
01149     {
01150         l++;
01151     }
01152 
01153     for (i = 0; i < num_scripts; i++)
01154     {
01155         if (strncmp(name, scripts[i].name, l) == 0)
01156         {
01157             return i;
01158         }
01159     }
01160 
01161     return -1;
01162 }
01163 
01167 void script_autoload()
01168 {
01169     FILE *fp;
01170     char line[MAX_BUF];
01171 
01172     if (!(fp = fopen_wrapper(SCRIPTS_AUTOLOAD, "r+")))
01173     {
01174         LOG(llevInfo, "Can't find file %s. Will not load any scripts.\n", SCRIPTS_AUTOLOAD);
01175         return;
01176     }
01177 
01178     while (fgets(line, sizeof(line) - 1, fp))
01179     {
01180         if (line[0] == '#' || line[0] == '\n')
01181         {
01182             continue;
01183         }
01184 
01185 #ifndef WIN32
01186         line[strlen(line) - 1] = '\0';
01187 #else
01188         line[strlen(line) - 2] = '\0';
01189 #endif
01190 
01191         script_load(line);
01192     }
01193 }
01194 
01199 void script_unload(const char *params)
01200 {
01201     int i = script_by_name(params);
01202 
01203     if (i < 0 || i >= num_scripts)
01204     {
01205         draw_info(COLOR_RED, "No such running script.");
01206         return;
01207     }
01208 
01209 #ifndef WIN32
01210     kill(scripts[i].pid, SIGHUP);
01211 #else
01212     GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid);
01213 #endif
01214 
01215     draw_info_format(COLOR_GREEN, "Unloaded script #%d.", i + 1);
01216     script_dead(i);
01217 }