char *rcsid_common_script_c = "$Id: script.c,v 1.13 2005/07/18 20:10:17 akirschbaum Exp $"; /* Crossfire client, a client program for the crossfire program. Copyright (C) 2003 Mark Wedel & Crossfire Development Team This source file also Copyright (C) 2003 Preston Crow This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author can be reached via e-mail to crossfire-devel@real-time.com */ /* This file has its own script.h for prototypes, so don't want to include * this when doing a 'make proto' */ #ifndef CPROTO /* * This file handles the client-side scripting interface. * * Each script is an external process that keeps two pipes open between the * client and the script (one in each direction). When the script starts, * it defaults to receiving no data from the client. Normally, the first * command it sends to the client will be a request to have certain types * of data sent to the script as the client receives them from the server * (such as drawinfo commands). The script can also request current * information from the client, such as the contents of the inventory or * the map data (either live or last viewed "fog-of-war" data). The script * can also send commands for the client to pass to the server. * * Script Commands: * * watch * whenever the server sends the given command type to the client, also send * a copy to the script. * Note that this checked before the client processes the command, so it will * automatically handle new options that may be added in the future. * If the command type is NULL, all commands are watched. * * unwatch * turn off a previous watch command. There may be a slight delay in * response before the command is processed, so some unwanted data may * still be sent to the script. * * request * have the client send the given data to the script. * * issue [ ] * issue the specified command to the server. * if isn't numeric then the command is sent directly * For "lock" and "mark" only, the parameters are converted to binary. * * draw * display the text in the specified color as if the server had sent * a drawinfo command. * * monitor * send the script a copy of every command that is sent to the server. * * unmonitor * turn off monitoring. * * sync <#> * wait until the server has acknowledged all but <#> commands have been received * * * To implement this: * * Processing script commands: gtk/gx11.c:do_network() and * x11/x11.c:event_loop() are modified to also watch for input from scripts * in the select() call, in which case script_process(fd) in this file is * called. * * Handling watches: common/client.c:DoClient() is modified to pass a copy * of each command to script_watch() before checking for it in the table. * * Handling of monitor: common/player.c:send_command() is modified to pass * a copy of each command to script_monitor() before sending to the server. * * Handling of requests: global variables are directly accessed from within * this file. * * Handling of issues: send_command() is called directly. Note that this * command will be sent to any scripts that are monitoring output. * * Launching new scripts: common/player.c:extended_command() is extended to * add a check for "script " as an additional command, calling * script_init(). Also added is the "scripts" command to list all running * scripts, the "scriptkill" command to terminate a script (close the pipes * and assume it takes the hint), and the "scripttell" command to send a * message to a running script. */ /* * Include files */ /* This does not work under Windows for now. Someday this will be fixed :) */ #ifndef WIN32 #include #include #include #include #include #include #include #endif #include #include #include #include /* * Data structures */ struct script { char *name; /* the script name */ char *params; /* the script parameters, if any */ #ifndef WIN32 int out_fd; /* the file descriptor to which the client writes to the script */ int in_fd; /* the file descriptor from which we read commands from the script */ #else HANDLE out_fd; /* the file descriptor to which the client writes to the script */ HANDLE in_fd; /* the file descriptor from which we read commands from the script */ #endif /* WIN32 */ int monitor; /* true if this script is monitoring commands sent to the server */ int num_watch; /* number of commands we're watching */ char **watch; /* array of commands that we're watching */ int cmd_count; /* bytes already read in */ char cmd[1024]; /* command from the script */ #ifndef WIN32 int pid; #else DWORD pid; /* Handle to Win32 process ID */ #endif int sync_watch; }; /* * Global variables */ static struct script *scripts = NULL; static int num_scripts = 0; /* * Prototypes */ static int script_by_name(const char *name); static void script_dead(int i); static void script_process_cmd(int i); static void send_map(int i,int x,int y); static void script_send_item(int i,char *head,item *it); /* * Functions */ #ifdef WIN32 #define write(x,y,z) emulate_write(x,y,z) #define read(x,y,z) emulate_read(x,y,z) static int emulate_read(HANDLE fd, char *buf, int len) { DWORD dwBytesRead; BOOL rc; rc = ReadFile(fd, buf, len, &dwBytesRead, NULL); if (rc == FALSE) return(-1); buf[dwBytesRead] = '\0'; return(dwBytesRead); } static int emulate_write(HANDLE fd, const char *buf, int len) { DWORD dwBytesWritten; BOOL rc; rc = WriteFile(fd, buf, len, &dwBytesWritten, NULL); if (rc == FALSE) return(-1); return(dwBytesWritten); } #endif /* WIN32 */ void script_init(const char *cparams) { #ifndef WIN32 int pipe1[2]; #ifdef USE_PIPE int pipe2[2]; #endif int pid; char *name, *args, params[MAX_BUF]; if ( !cparams ) { draw_info( "Please specifiy a script to launch!", NDI_RED ); return; } /* cparams as passed in is a const value, so need to copy it * to data we can write over. */ strncpy(params, cparams, MAX_BUF-1); params[MAX_BUF-1]=0; /* Get name and args */ name=params; args=name; while ( *args && *args!=' ' ) ++args; while ( *args && *args==' ' ) ++args; if ( *args==0 ) { args=NULL; } #if 0 else { args[-1]=0; } #endif #ifdef USE_PIPE /* Create two pipes */ if ( pipe(pipe1) ) { draw_info("Unable to start script--pipe failed",NDI_RED); return; } if ( pipe(pipe2) ) { close(pipe1[0]); close(pipe1[1]); draw_info("Unable to start script--pipe failed",NDI_RED); return; } #else /* Create a pair of sockets */ if ( socketpair(PF_LOCAL,SOCK_STREAM,AF_LOCAL,pipe1) ) { draw_info("Unable to start script--socketpair failed",NDI_RED); return; } #endif /* Fork */ pid=fork(); if (pid==-1) { close(pipe1[0]); close(pipe1[1]); #ifdef USE_PIPE close(pipe2[0]); close(pipe2[1]); #endif draw_info("Unable to start script--fork failed",NDI_RED); return; } /* Child--set stdin/stdout to the pipes, then exec */ if ( pid==0 ) { int i; int r; char *argv[256]; /* Fill in argv[] */ argv[0]=name; i=1; while (args && *args) { argv[i]=args; ++i; while ( *args && *args!=' ' ) ++args; if ( *args ) { *args=0; ++args; } while ( *args && *args==' ' ) ++args; } argv[i]=NULL; /* Clean up file descriptor space */ r=dup2(pipe1[0],0); if ( r != 0 ) { fprintf(stderr,"Script Child: Failed to set pipe1 as stdin\n"); } #ifdef USE_PIPE r=dup2(pipe2[1],1); #else r=dup2(pipe1[0],1); #endif if ( r != 1 ) { fprintf(stderr,"Script Child: Failed to set pipe2 as stdout\n"); } for (i=3;i<100;++i) close(i); /* EXEC */ r = execvp(argv[0],argv); /* If we get here, then there's been an failure of some sort. * In my case, it's often that I don't know what script name to * give to /script, so exec() can't find the script. * * Forward the error back to the client, using the script pipes. */ if (r != -1) { printf("draw %d Script child: no error, but no execvp().\n", NDI_RED); } else { printf("draw %d Script child failed to start: %s\n", NDI_RED, strerror(errno)); } exit(1); } /* Close the child's pipe ends */ close(pipe1[0]); #ifdef USE_PIPE close(pipe2[1]); #endif if (fcntl(pipe1[1], F_SETFL, O_NDELAY)==-1) { LOG(LOG_WARNING,"common::script_init","Error on fcntl."); } /* realloc script array to add new entry; fill in the data */ scripts=realloc(scripts,sizeof(scripts[0])*(num_scripts+1)); scripts[num_scripts].name=strdup(name); scripts[num_scripts].params=args?strdup(args):NULL; scripts[num_scripts].out_fd=pipe1[1]; #ifdef USE_PIPE scripts[num_scripts].in_fd=pipe2[0]; #else scripts[num_scripts].in_fd=pipe1[1]; #endif scripts[num_scripts].monitor=0; scripts[num_scripts].num_watch=0; scripts[num_scripts].watch=NULL; scripts[num_scripts].cmd_count=0; scripts[num_scripts].pid=pid; scripts[num_scripts].sync_watch = -1; ++num_scripts; #else /* WIN32 */ char *name,*args; char params[ MAX_BUF ]; SECURITY_ATTRIBUTES saAttr; PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartupInfo; HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd; HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout; if ( !cparams ) { draw_info( "Please specifiy a script to launch!", NDI_RED ); return; } strncpy(params, cparams, MAX_BUF-1); params[MAX_BUF-1] = '\0'; /* Get name and args */ name=params; args=name; while ( *args && *args!=' ' ) ++args; while ( *args && *args==' ' ) ++args; if ( *args==0 ) { args=NULL; } else { args[-1]=0; } saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) { draw_info("Script support: stdout CreatePipe() failed", NDI_RED); return; } if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) { draw_info("Script support: failed to redirect stdout using SetStdHandle()", NDI_RED); return; } if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { draw_info("Script support: failed to duplicate stdout using DuplicateHandle()", NDI_RED); return; } CloseHandle(hChildStdoutRd); hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) { draw_info("Script support: stdin CreatePipe() failed", NDI_RED); return; } if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) { draw_info("Script support: failed to redirect stdin using SetStdHandle()", NDI_RED); return; } if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { draw_info("Script support: failed to duplicate stdin using DuplicateHandle()", NDI_RED); return; } CloseHandle(hChildStdinWr); ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO)); siStartupInfo.cb = sizeof(STARTUPINFO); if (args) args[-1] = ' '; if (!CreateProcess(NULL, name, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo)) { draw_info("Script support: CreateProcess() failed", NDI_RED); return; } CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); if (args) args[-1] = '\0'; if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) { draw_info("Script support: restoring original stdin failed", NDI_RED); return; } if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) { draw_info("Script support: restoring original stdout failed", NDI_RED); return; } /* realloc script array to add new entry; fill in the data */ scripts=realloc(scripts,sizeof(scripts[0])*(num_scripts+1)); scripts[num_scripts].name=strdup(name); scripts[num_scripts].params=args?strdup(args):NULL; scripts[num_scripts].out_fd=hChildStdinWrDup; scripts[num_scripts].in_fd=hChildStdoutRdDup; scripts[num_scripts].monitor=0; scripts[num_scripts].num_watch=0; scripts[num_scripts].watch=NULL; scripts[num_scripts].cmd_count=0; scripts[num_scripts].pid=piProcInfo.dwProcessId; scripts[num_scripts].sync_watch = -1; ++num_scripts; #endif /* WIN32 */ } void script_sync(int commdiff) { int i; if (commdiff<0) commdiff +=256; for (i=0;i= 0 ) { char buf[1024]; sprintf(buf,"sync %d\n",commdiff); write(scripts[i].out_fd,buf,strlen(buf)); scripts[i].sync_watch = -1; } } } void script_list(void) { if ( num_scripts == 0 ) { draw_info("No scripts are currently running",NDI_BLACK); } else { int i; char buf[1024]; sprintf(buf,"%d scripts currently running:",num_scripts); draw_info(buf,NDI_BLACK); for ( i=0;i=num_scripts) { draw_info("No such running script",NDI_BLACK); return; } #ifndef WIN32 kill(scripts[i].pid,SIGHUP); #else GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid); #endif /* WIN32 */ draw_info( "Killed script.", NDI_RED ); script_dead(i); } #ifdef WIN32 void script_killall(void) { while (num_scripts > 0) { GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid); script_dead(0); } } #endif /* WIN32 */ void script_fdset(int *maxfd,fd_set *set) { #ifndef WIN32 int i; for ( i=0;i= *maxfd ) *maxfd = scripts[i].in_fd+1; } #endif /* WIN32 */ } void script_process(fd_set *set) { int i; int r; #ifdef WIN32 DWORD nAvailBytes = 0; char cTmp; BOOL bRC; #endif /* Determine which script's fd is set */ for(i=0;i0 ) { scripts[i].cmd_count+=r; } #ifndef WIN32 else if ( r==0 || errno==EBADF ) #else else if ( r==0 || GetLastError() == ERROR_BROKEN_PIPE ) #endif { /* Script has exited; delete it */ script_dead(i); return; } /* If a newline or full buffer has been reached, process it */ scripts[i].cmd[scripts[i].cmd_count]=0; /* terminate string */ while ( scripts[i].cmd_count == sizeof(scripts[i].cmd)-1 #ifndef WIN32 || strchr(scripts[i].cmd,'\n') ) #else || strchr(scripts[i].cmd,'\r\n') ) #endif /* WIN32 */ { script_process_cmd(i); scripts[i].cmd[scripts[i].cmd_count]=0; /* terminate string */ } return; /* Only process one script at a time */ } #ifdef WIN32 else if (!bRC) /* Error: assume dead */ script_dead(i); #endif /* WIN32 */ } } void script_watch(char *cmd,char *data, int len, enum CmdFormat format) { int i; int w; int l; int cl; cl=strlen(cmd); /* For each script... */ for (i=0;i=CS_STAT_RESIST_START && c<=CS_STAT_RESIST_END) { sprintf(buf+be," resists %d %d\n",c,GetShort_String(data)); data+=2; len-=2; } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) { sprintf(buf+be," skill %d %d %lld\n",c,*data,GetInt64_String(data+1)); data+=9; len-=9; } else switch (c) { case CS_STAT_HP: sprintf(buf+be," hp %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_MAXHP: sprintf(buf+be," maxhp %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_SP: sprintf(buf+be," sp %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_MAXSP: sprintf(buf+be," maxspp %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_GRACE: sprintf(buf+be," grace %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_MAXGRACE: sprintf(buf+be," maxgrace %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_STR: sprintf(buf+be," str %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_INT: sprintf(buf+be," int %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_POW: sprintf(buf+be," pow %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_WIS: sprintf(buf+be," wis %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_DEX: sprintf(buf+be," dex %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_CON: sprintf(buf+be," con %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_CHA: sprintf(buf+be," cha %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_EXP: sprintf(buf+be," exp %d\n",GetInt_String(data)); data+=4; len-=4; break; case CS_STAT_EXP64: sprintf(buf+be," exp %lld\n",GetInt64_String(data)); data+=8; len-=8; break; case CS_STAT_LEVEL: sprintf(buf+be," level %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_WC: sprintf(buf+be," wc %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_AC: sprintf(buf+be," ac %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_DAM: sprintf(buf+be," dam %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_ARMOUR: sprintf(buf+be," armour %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_SPEED: sprintf(buf+be," speed %d\n",GetInt_String(data)); data+=4; len-=4; break; case CS_STAT_FOOD: sprintf(buf+be," food %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_WEAP_SP: sprintf(buf+be," weap_sp %d\n",GetInt_String(data)); data+=4; len-=4; break; case CS_STAT_FLAGS: sprintf(buf+be," flags %d\n",GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_WEIGHT_LIM: sprintf(buf+be," weight_lim %d\n",GetInt_String(data)); data+=4; len-=4; break; case CS_STAT_SKILLEXP_AGILITY: case CS_STAT_SKILLEXP_PERSONAL: case CS_STAT_SKILLEXP_MENTAL: case CS_STAT_SKILLEXP_PHYSIQUE: case CS_STAT_SKILLEXP_MAGIC: case CS_STAT_SKILLEXP_WISDOM: sprintf(buf+be," skillexp %d %d\n",c,GetInt_String(data)); data+=4; len-=4; break; case CS_STAT_SKILLEXP_AGLEVEL: case CS_STAT_SKILLEXP_PELEVEL: case CS_STAT_SKILLEXP_MELEVEL: case CS_STAT_SKILLEXP_PHLEVEL: case CS_STAT_SKILLEXP_MALEVEL: case CS_STAT_SKILLEXP_WILEVEL: sprintf(buf+be," skilllevel %d %d\n",c,GetShort_String(data)); data+=2; len-=2; break; case CS_STAT_RANGE: { int rlen=*data; ++data; --len; sprintf(buf+be," range %*.*s\n",rlen,rlen,data); data+=rlen; len-=rlen; break; } case CS_STAT_TITLE: { int rlen=*data; ++data; --len; sprintf(buf+be," title %*.*s\n",rlen,rlen,data); data+=rlen; len-=rlen; break; } default: sprintf(buf+be," unknown %d %d bytes left\n",c,len); len=0; } } break; case MIXED: /* magicmap */ /* mapextended */ /* item1 item2 */ /* upditem */ /* image image2 */ /* face face1 face2 */ /* sound */ /* player */ /* * If we find that scripts need data from any of the above, we can * write special-case code as with stats. In the meantime, fall * through and just give a hex dump. Script writers should not * depend on that data format. */ case NODATA: default: { int be; int p; /*we may receive an null data, in which case len has no meaning*/ if (!data) len=0; be=sprintf(buf,"watch %s %d bytes unparsed:",cmd,len); for(p=0;p=0 && i * unwatch * request * issue * localcmd [] * draw * monitor * unmonitor */ if ( strncmp(cmd,"sync",4)==0 ) { c=cmd+4; while ( *c && *c!=' ' ) ++c; while ( *c==' ' ) ++c; scripts[i].sync_watch = -1; if ( isdigit(*c) ) { scripts[i].sync_watch = atoi(c); } script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */ } else if ( strncmp(cmd,"watch",5)==0 ) { c=cmd+5; while ( *c && *c!=' ' ) ++c; while ( *c==' ' ) ++c; c=strdup(c); scripts[i].watch=realloc(scripts[i].watch,(scripts[i].num_watch+1)*sizeof(scripts[i].watch[1])); scripts[i].watch[scripts[i].num_watch]=c; ++scripts[i].num_watch; } else if ( strncmp(cmd,"unwatch",7)==0 ) { int w; c=cmd+7; while ( *c && *c!=' ' ) ++c; while ( *c==' ' ) ++c; for (w=0;w Return the specified stats * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha * stat cmbt Return wc,ac,dam,speed,weapon_sp * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food * stat xp Return level,xp,skill-1 level,skill-1 xp,... * stat resists Return resistances * weight Return maxweight, weight * flags Return flags (fire, run) * items inv Return a list of items in the inventory, one per line * items actv Return a list of inventory items that are active, one per line * items on Return a list of items under the player, one per line * items cont Return a list of items in the open container, one per line * map pos Return the players x,y within the current map * map near Return the 3x3 grid of the map centered on the player * map all Return all the known map information * map Return the information about square x,y in the current map */ if ( strncmp(c,"range",5)==0 ) { char buf[1024]; sprintf(buf,"request range %s\n",cpl.range); write(scripts[i].out_fd,buf,strlen(buf)); } else if ( strncmp(c,"weight",5)==0 ) { char buf[1024]; sprintf(buf,"request weight %d %d\n",cpl.stats.weight_limit,(int)(cpl.ob->weight*1000)); write(scripts[i].out_fd,buf,strlen(buf)); } else if ( strncmp(c,"stat ",5)==0 ) { c+=4; while ( *c && *c!=' ' ) ++c; while ( *c==' ' ) ++c; if ( !*c ) return; /* bad request */ /* * stat stats Return Str,Con,Dex,Int,Wis,Pow,Cha * stat cmbt Return wc,ac,dam,speed,weapon_sp * stat hp Return hp,maxhp,sp,maxsp,grace,maxgrace,food * stat xp Return level,xp,skill-1 level,skill-1 xp,... * stat resists Return resistances */ if ( strncmp(c,"stats",5)==0 ) { char buf[1024]; sprintf(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); write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"cmbt",4)==0 ) { char buf[1024]; sprintf(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); write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"hp",2)==0 ) { char buf[1024]; sprintf(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); write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"xp",2)==0 ) { char buf[1024]; int s; sprintf(buf,"request stat xp %d %lld",cpl.stats.level,cpl.stats.exp); write(scripts[i].out_fd,buf,strlen(buf)); for(s=0;sinv; while (it) { script_send_item(i,"request items inv ",it); it=it->next; } buf="request items inv end\n"; write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"actv",4)==0 ) { char *buf; item *it=cpl.ob->inv; while (it) { if (it->applied) script_send_item(i,"request items actv ",it); it=it->next; } buf="request items actv end\n"; write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"on",2)==0 ) { char *buf; item *it=cpl.below->inv; while (it) { script_send_item(i,"request items on ",it); it=it->next; } buf="request items on end\n"; write(scripts[i].out_fd,buf,strlen(buf)); } if ( strncmp(c,"cont",4)==0 ) { char *buf; item *it=cpl.container->inv; while (it) { script_send_item(i,"request items cont ",it); it=it->next; } buf="request items cont end\n"; write(scripts[i].out_fd,buf,strlen(buf)); } } else if ( strncmp(c,"map ",4)==0 ) { int x,y; c+=3; while ( *c && *c!=' ' ) ++c; while ( *c==' ' ) ++c; if ( !*c ) return; /* bad request */ /* * map pos Return the players x,y within the current map * map near Return the 3x3 grid of the map centered on the player * map all Return all the known map information * map Return the information about square x,y in the current map */ if ( strncmp(c,"pos",3)==0 ) { char buf[1024]; sprintf(buf,"request map pos %d %d\n",pl_pos.x,pl_pos.y); write(scripts[i].out_fd,buf,strlen(buf)); } else if ( strncmp(c,"near",4)==0 ) { for(y=0;y<3;++y) for(x=0;x<3;++x) send_map(i, x+pl_pos.x+use_config[CONFIG_MAPWIDTH]/2-1, y+pl_pos.y+use_config[CONFIG_MAPHEIGHT]/2-1 ); } else if ( strncmp(c,"all",3)==0 ) { char buf[1024]; for(y=0;y" or "lock " */ if ( strncmp(c,"mark",4)==0 ) { int tag; SockList sl; char buf[MAX_BUF]; c+=4; while ( *c && !isdigit(*c) ) ++c; if ( !*c ) return; /* No tag specified */ tag=atoi(c); SockList_Init(&sl, buf); SockList_AddString(&sl, "mark "); SockList_AddInt(&sl, tag); SockList_Send(&sl, csocket.fd); } else if ( strncmp(c,"lock",4)==0 ) { int tag,locked; SockList sl; char buf[MAX_BUF]; c+=4; while ( *c && !isdigit(*c) ) ++c; if ( !*c ) return; /* No state specified */ locked=atoi(c); while ( *c && *c!=' ' ) ++c; while ( *c && !isdigit(*c) ) ++c; if ( !*c ) return; /* No tag specified */ tag=atoi(c); SockList_Init(&sl, buf); SockList_AddString(&sl, "lock "); SockList_AddChar(&sl, locked); SockList_AddInt(&sl, tag); SockList_Send(&sl, csocket.fd); } else { cs_print_string(csocket.fd, "%s", c); } } } else if ( strncmp(cmd,"localcmd",8)==0){ char* param; c=cmd+8; while (*c==' ') c++; param=c; while ( (*param!='\0') && (*param!=' ')) param++; if (*param==' '){ *param='\0'; *param++; } else param=NULL; if (!handle_local_command(c, param)){ char buf[1024]; sprintf(buf,"Script %s malfunction; localcmd not understood",scripts[i].name); draw_info(buf,NDI_RED); sprintf(buf,"Script <>",c,(param==NULL)?"":param); draw_info(buf,NDI_RED); } } else if ( strncmp(cmd,"draw",4)==0 ) { int color; c=cmd+4; while ( *c && !isdigit(*c) ) ++c; if ( !*c ) return; /* No color specified */ color=atoi(c); while ( *c && *c!=' ' ) ++c; if ( !*c ) return; /* No message specified */ while ( *c==' ' ) ++c; draw_info(c,color); } else if ( strncmp(cmd,"monitor",7)==0 ) scripts[i].monitor=1; else if ( strncmp(cmd,"unmonitor",9)==0 ) scripts[i].monitor=0; else { char buf[1024]; sprintf(buf,"Script %d %s malfunction; invalid command:",i+1,scripts[i].name); draw_info(buf,NDI_RED); draw_info(cmd,NDI_RED); } } /* * script_send_item() * * Send one line to the script with item information. * * A header string is passed in. The format is: * *
tag num weight flags type name * * flags are a bitmask: * magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated * 256 128 64 32 16 8 4 2 1 */ static void script_send_item(int i,char *head,item *it) { char buf[4096]; int flags; flags=it->magical; flags= (flags<<1)|it->cursed; flags= (flags<<1)|it->damned; flags= (flags<<1)|it->unpaid; flags= (flags<<1)|it->locked; flags= (flags<<1)|it->applied; flags= (flags<<1)|it->open; flags= (flags<<1)|it->was_open; flags= (flags<<1)|it->inv_updated; sprintf(buf,"%s%d %d %f %d %d %s\n",head,it->tag,it->nrof,it->weight,flags,it->type,it->d_name); write(scripts[i].out_fd,buf,strlen(buf)); } #endif /* CPROTO */