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