diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index bb70c17198..32b557c75a 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -4,6 +4,19 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. 2006/11/07 + * Applied FlavioJs's patch which enables colored console output for Windows + systems. It also includes a config setting called + "stdout_with_ansisequence" with which you can turn off the color codes (in + case you are logging all output) [Skotlex] + * Added error reporting when the max number of ground unit cells has been + reached (this may be the reason why sometimes it fails to recognize when + you step out of a song/dance/encore) [Skotlex] + * Added passing the Endure effect to other devoted people. Note that the + "hit count" is individual for each character, and only when it ends on the + Crusader himself will that force it to end on everyone else. It also will + not transfer on gvg grounds, but it does transfer in pvp. [Skotlex] + * Fixed Reflect-Shield triggering Auto-Guard instead on devoted chars. + [Skotlex] * Added a check to prevent casting ground skills on a target and vice-versa when said skill use packets are received. It really shouldn't be exploitable, but doing that certainly makes the server print a lot of diff --git a/conf-tmpl/Changelog.txt b/conf-tmpl/Changelog.txt index 7d7189082d..9cbc4cedba 100644 --- a/conf-tmpl/Changelog.txt +++ b/conf-tmpl/Changelog.txt @@ -1,5 +1,11 @@ Date Added +2006/11/07 + * New Setting "stdout_with_ansisequence" + (login_athena/char_athena/map_athena) allows you to specify whether color + control chars should be printed or not. Is useful to disable for a + "cleaner" output when you are logging the console output. All credit goes + to FlavioJS for coding the whole thing. [Skotlex] 2006/11/05 * Added maplags for PowerNPC quest. [KarLaeda] 2006/10/31 diff --git a/conf-tmpl/char_athena.conf b/conf-tmpl/char_athena.conf index c48c921383..840c40d2e1 100644 --- a/conf-tmpl/char_athena.conf +++ b/conf-tmpl/char_athena.conf @@ -47,6 +47,14 @@ char_port: 6121 //For full format information, consult the strftime() manual. //timestamp_format: [%d/%b %H:%M] +//Defines if the ansi sequences should be parsed or skipped. +//If set to yes the console output is in color. If the stream is redirected to +//a file, the ansi sequences are printed out. +//If set to no the console is colorless and, if redirected, will skip the ansi +//sequences. +//NOTE: this setting applyes for both stdout and stderr +stdout_with_ansisequence: yes + //Makes server output more silent by ommitting certain types of messages: //1: Hide Information messages //2: Hide Status messages diff --git a/conf-tmpl/login_athena.conf b/conf-tmpl/login_athena.conf index 41995a2dbe..834904a898 100644 --- a/conf-tmpl/login_athena.conf +++ b/conf-tmpl/login_athena.conf @@ -22,6 +22,14 @@ login_port: 6900 //For full format information, consult the strftime() manual. //timestamp_format: [%d/%b %H:%M] +//Defines if the ansi sequences should be parsed or skipped. +//If set to yes the console output is in color. If the stream is redirected to +//a file, the ansi sequences are printed out. +//If set to no the console is colorless and, if redirected, will skip the ansi +//sequences. +//NOTE: this setting applyes for both stdout and stderr +stdout_with_ansisequence: yes + //Makes server output more silent by ommitting certain types of messages: //1: Hide Information messages //2: Hide Status messages diff --git a/conf-tmpl/map_athena.conf b/conf-tmpl/map_athena.conf index ee9fc4ae8e..4812e5d1c9 100644 --- a/conf-tmpl/map_athena.conf +++ b/conf-tmpl/map_athena.conf @@ -57,6 +57,14 @@ map_port: 5121 //For full format information, consult the strftime() manual. //timestamp_format: [%d/%b %H:%M] +//Defines if the ansi sequences should be parsed or skipped. +//If set to yes the console output is in color. If the stream is redirected to +//a file, the ansi sequences are printed out. +//If set to no the console is colorless and, if redirected, will skip the ansi +//sequences. +//NOTE: this setting applyes for both stdout and stderr +stdout_with_ansisequence: yes + //Makes server output more silent by ommitting certain types of messages: //1: Hide Information messages //2: Hide Status messages diff --git a/src/char/char.c b/src/char/char.c index 0cc15a462c..6e96ec0f04 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -4100,6 +4100,8 @@ int char_config_read(const char *cfgName) { remove_control_chars((unsigned char *)w2); if(strcmpi(w1,"timestamp_format") == 0) { strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); } else if(strcmpi(w1,"console_silent")==0){ msg_silent = 0; //To always allow the next line to show up. ShowInfo("Console Silent Setting: %d\n", atoi(w2)); diff --git a/src/char_sql/char.c b/src/char_sql/char.c index 1fbbf44aa8..b2395623b7 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -4030,6 +4030,8 @@ int char_config_read(const char *cfgName) { remove_control_chars((unsigned char *) w2); if(strcmpi(w1,"timestamp_format")==0) { strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); } else if(strcmpi(w1,"console_silent")==0){ msg_silent = 0; //To always allow the next line to show up. ShowInfo("Console Silent Setting: %d\n", atoi(w2)); diff --git a/src/common/showmsg.c b/src/common/showmsg.c index 594234b8d8..b0f1d1d8fa 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -5,9 +5,14 @@ #include #include #include +#include // atexit +#include "../common/cbasetypes.h" #include "showmsg.h" #ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #ifdef DEBUGLOGMAP #define DEBUGLOGPATH "log\\map-server.log" #else @@ -20,6 +25,9 @@ #endif #endif #else + #include + #include + #ifdef DEBUGLOGMAP #define DEBUGLOGPATH "log/map-server.log" #else @@ -33,9 +41,605 @@ #endif #endif +/////////////////////////////////////////////////////////////////////////////// +/// behavioral parameter. +/// when true, prints ansi sequences also when redirecting outputs to file +/// otherwise remove them +int stdout_with_ansisequence = 1; + int msg_silent; //Specifies how silent the console is. -char tmp_output[1024] = {"\0"}; + +/////////////////////////////////////////////////////////////////////////////// +/// small reallocating temporary printer buffer +static char *tempbuf = NULL; +static size_t sz = 0; +#define tempbuf_size() (sz) +static void tempbuf_free(void){ free(tempbuf); } +static void tempbuf_alloc(void){ sz = 256; tempbuf = (char *)malloc(sz); atexit(tempbuf_free); } +static void tempbuf_realloc(void){ sz <<= 1; tempbuf = (char *)realloc(tempbuf,sz); } + +/////////////////////////////////////////////////////////////////////////////// +#ifdef _WIN32 +// XXX adapted from eApp (comments are left untouched) [flaviojs] + +/////////////////////////////////////////////////////////////////////////////// +// ansi compatible printf with control sequence parser for windows +// fast hack, handle with care, not everything implemented +// +// \033[#;...;#m - Set Graphics Rendition (SGR) +// +// printf("\x1b[1;31;40m"); // Bright red on black +// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented) +// printf("\x1b[1;30;47m"); // Bright black (grey) on dim white +// +// Style Foreground Background +// 1st Digit 2nd Digit 3rd Digit RGB +// 0 - Reset 30 - Black 40 - Black 000 +// 1 - FG Bright 31 - Red 41 - Red 100 +// 2 - Unknown 32 - Green 42 - Green 010 +// 3 - Blink 33 - Yellow 43 - Yellow 110 +// 4 - Underline 34 - Blue 44 - Blue 001 +// 5 - BG Bright 35 - Magenta 45 - Magenta 101 +// 6 - Unknown 36 - Cyan 46 - Cyan 011 +// 7 - Reverse 37 - White 47 - White 111 +// 8 - Concealed (invisible) +// +// \033[#A - Cursor Up (CUU) +// Moves the cursor up by the specified number of lines without changing columns. +// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A. +// +// \033[#B - Cursor Down (CUD) +// Moves the cursor down by the specified number of lines without changing columns. +// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B. +// +// \033[#C - Cursor Forward (CUF) +// Moves the cursor forward by the specified number of columns without changing lines. +// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C. +// +// \033[#D - Cursor Backward (CUB) +// Moves the cursor back by the specified number of columns without changing lines. +// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D. +// +// \033[#E - Cursor Next Line (CNL) +// Moves the cursor down the indicated # of rows, to column 1. \e[E is equivalent to \e[1E. +// +// \033[#F - Cursor Preceding Line (CPL) +// Moves the cursor up the indicated # of rows, to column 1. \e[F is equivalent to \e[1F. +// +// \033[#G - Cursor Horizontal Absolute (CHA) +// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G. +// +// \033[#;#H - Cursor Position (CUP) +// Moves the cursor to the specified position. The first # specifies the line number, +// the second # specifies the column. If you do not specify a position, the cursor moves to the home position: +// the upper-left corner of the screen (line 1, column 1). +// +// \033[#;#f - Horizontal & Vertical Position +// (same as \033[#;#H) +// +// \033[s - Save Cursor Position (SCP) +// The current cursor position is saved. +// +// \033[u - Restore cursor position (RCP) +// Restores the cursor position saved with the (SCP) sequence \033[s. +// (addition, restore to 0,0 if nothinh was saved before) +// + +// \033[#J - Erase Display (ED) +// Clears the screen and moves to the home position +// \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. (default) +// \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. +// \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). +// +// \033[#K - Erase Line (EL) +// Clears the current line from the cursor position +// \033[0K - Clears all characters from the cursor position to the end of the line (including the character at the cursor position). The cursor position is unchanged. (default) +// \033[1K - Clears all characters from start of line to the cursor position. (including the character at the cursor position). The cursor position is unchanged. +// \033[2K - Clears all characters of the whole line. The cursor position is unchanged. + + +/* +not implemented + +\033[#L +IL: Insert Lines: The cursor line and all lines below it move down # lines, leaving blank space. The cursor position is unchanged. The bottommost # lines are lost. \e[L is equivalent to \e[1L. +\033[#M +DL: Delete Line: The block of # lines at and below the cursor are deleted; all lines below them move up # lines to fill in the gap, leaving # blank lines at the bottom of the screen. The cursor position is unchanged. \e[M is equivalent to \e[1M. +\033[#\@ +ICH: Insert CHaracter: The cursor character and all characters to the right of it move right # columns, leaving behind blank space. The cursor position is unchanged. The rightmost # characters on the line are lost. \e[\@ is equivalent to \e[1\@. +\033[#P +DCH: Delete CHaracter: The block of # characters at and to the right of the cursor are deleted; all characters to the right of it move left # columns, leaving behind blank space. The cursor position is unchanged. \e[P is equivalent to \e[1P. + +Escape sequences for Select Character Set +*/ + +#define is_console(handle) (FILE_TYPE_CHAR==GetFileType(handle)) + +/////////////////////////////////////////////////////////////////////////////// +int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) +{ + ///////////////////////////////////////////////////////////////// + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + static COORD saveposition = {0,0}; + */ + + ///////////////////////////////////////////////////////////////// + unsigned long written; + char *p, *q; + + if(!fmt || !*fmt) + return 0; + + if(tempbuf == NULL) + tempbuf_alloc(); + for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); + // vsnprintf returns -1 in case of insufficient buffer size + // tempbuf_realloc doubles the size of the buffer in this case + + if( !is_console(handle) && stdout_with_ansisequence ) + { + WriteFile(handle,tempbuf, strlen(tempbuf), &written, 0); + return 0; + } + + // start with processing + p = tempbuf; + while ((q = strchr(p, 0x1b)) != NULL) + { // find the escape character + if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape + WriteFile(handle, p, q-p, &written, 0); + + if( q[1]!='[' ) + { // write the escape char (whatever purpose it has) + if(0==WriteConsole(handle, q, 1, &written, 0) ) + WriteFile(handle,q, 1, &written, 0); + p=q+1; //and start searching again + } + else + { // from here, we will skip the '\033[' + // we break at the first unprocessible position + // assuming regular text is starting there + uchar numbers[16], numpoint=0; + CONSOLE_SCREEN_BUFFER_INFO info; + + // initialize + GetConsoleScreenBufferInfo(handle, &info); + memset(numbers,0,sizeof(numbers)); + + // skip escape and bracket + q=q+2; + while(1) + { + if( isdigit((int)((unsigned char)*q)) ) + { // add number to number array, only accept 2digits, shift out the rest + // so // \033[123456789m will become \033[89m + numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0'); + ++q; + // and next character + continue; + } + else if( *q == ';' ) + { // delimiter + if(numpoint7) num=7; // set white for 37, 38 and 39 + info.wAttributes &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); + if( (num & 0x01)>0 ) // lowest bit set = red + info.wAttributes |= FOREGROUND_RED; + if( (num & 0x02)>0 ) // second bit set = green + info.wAttributes |= FOREGROUND_GREEN; + if( (num & 0x04)>0 ) // third bit set = blue + info.wAttributes |= FOREGROUND_BLUE; + } + else if( 0x40 == (0xF0 & numbers[i]) ) + { // background + uint num = numbers[i]&0x0F; + if(num==9) info.wAttributes |= BACKGROUND_INTENSITY; + if(num>7) num=7; // set white for 47, 48 and 49 + info.wAttributes &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE); + if( (num & 0x01)>0 ) // lowest bit set = red + info.wAttributes |= BACKGROUND_RED; + if( (num & 0x02)>0 ) // second bit set = green + info.wAttributes |= BACKGROUND_GREEN; + if( (num & 0x04)>0 ) // third bit set = blue + info.wAttributes |= BACKGROUND_BLUE; + } + } + // set the attributes + SetConsoleTextAttribute(handle, info.wAttributes); + } + else if( *q=='J' ) + { // \033[#J - Erase Display (ED) + // \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. + // \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. + // \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). + uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); + int cnt; + COORD origin = {0,0}; + if(num==1) + { // chars from start up to and including cursor + cnt = info.dwSize.X * info.dwCursorPosition.Y + info.dwCursorPosition.X + 1; + } + else if(num==2) + { // Number of chars on screen. + cnt = info.dwSize.X * info.dwSize.Y; + SetConsoleCursorPosition(handle, origin); + } + else// 0 and default + { // number of chars from cursor to end + origin = info.dwCursorPosition; + cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X; + } + FillConsoleOutputAttribute(handle,info.wAttributes,cnt,origin,NULL); + FillConsoleOutputCharacter(handle,' ', cnt,origin,NULL); + } + else if( *q=='K' ) + { // \033[K : clear line from actual position to end of the line + // \033[0K - Clears all characters from the cursor position to the end of the line. + // \033[1K - Clears all characters from start of line to the cursor position. + // \033[2K - Clears all characters of the whole line. + + uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); + COORD origin = {0,info.dwCursorPosition.Y}; + SHORT cnt; + if(num==1) + { + cnt = info.dwCursorPosition.X + 1; + } + else if(num==2) + { + cnt = info.dwSize.X; + } + else// 0 and default + { + origin = info.dwCursorPosition; + cnt = info.dwSize.X - info.dwCursorPosition.X; // how many spaces until line is full + } + FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, NULL); + FillConsoleOutputCharacter(handle, ' ', cnt, origin, NULL); + } + else if( *q == 'H' || *q == 'f' ) + { // \033[#;#H - Cursor Position (CUP) + // \033[#;#f - Horizontal & Vertical Position + // The first # specifies the line number, the second # specifies the column. + // The default for both is 1 + info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; + info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+(numbers[numpoint-1]&0x0F-1):0; + + if( info.dwCursorPosition.X >= info.dwSize.X ) info.dwCursorPosition.Y = info.dwSize.X-1; + if( info.dwCursorPosition.Y >= info.dwSize.Y ) info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q=='s' ) + { // \033[s - Save Cursor Position (SCP) + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(handle, &info); + saveposition = info.dwCursorPosition; + */ + } + else if( *q=='u' ) + { // \033[u - Restore cursor position (RCP) + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + SetConsoleCursorPosition(handle, saveposition); + */ + } + else if( *q == 'A' ) + { // \033[#A - Cursor Up (CUU) + // Moves the cursor UP # number of lines + info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.Y < 0 ) + info.dwCursorPosition.Y = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'B' ) + { // \033[#B - Cursor Down (CUD) + // Moves the cursor DOWN # number of lines + info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.Y >= info.dwSize.Y ) + info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'C' ) + { // \033[#C - Cursor Forward (CUF) + // Moves the cursor RIGHT # number of columns + info.dwCursorPosition.X += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.X >= info.dwSize.X ) + info.dwCursorPosition.X = info.dwSize.X-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'D' ) + { // \033[#D - Cursor Backward (CUB) + // Moves the cursor LEFT # number of columns + info.dwCursorPosition.X -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.X < 0 ) + info.dwCursorPosition.X = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'E' ) + { // \033[#E - Cursor Next Line (CNL) + // Moves the cursor down the indicated # of rows, to column 1 + info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + info.dwCursorPosition.X = 0; + + if( info.dwCursorPosition.Y >= info.dwSize.Y ) + info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'F' ) + { // \033[#F - Cursor Preceding Line (CPL) + // Moves the cursor up the indicated # of rows, to column 1. + info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + info.dwCursorPosition.X = 0; + + if( info.dwCursorPosition.Y < 0 ) + info.dwCursorPosition.Y = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'G' ) + { // \033[#G - Cursor Horizontal Absolute (CHA) + // Moves the cursor to indicated column in current row. + info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; + + if( info.dwCursorPosition.X >= info.dwSize.X ) + info.dwCursorPosition.X = info.dwSize.X-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') + { // not implemented, just skip + } + else + { // no number nor valid sequencer + // something is fishy, we break and give the current char free + --q; + } + // skip the sequencer and search again + p = q+1; + break; + }// end while + } + } + if (*p) // write the rest of the buffer + if( 0==WriteConsole(handle, p, strlen(p), &written, 0) ) + WriteFile(handle,p, strlen(p), &written, 0); + return 0; +} + +int FPRINTF(HANDLE handle, const char *fmt, ...) +{ + int ret; + va_list argptr; + va_start(argptr, fmt); + ret = VFPRINTF(handle,fmt,argptr); + va_end(argptr); + return ret; +} + +#define FFLUSH(handle) + +#define STDOUT GetStdHandle(STD_OUTPUT_HANDLE) +#define STDERR GetStdHandle(STD_ERROR_HANDLE) + +#else // not _WIN32 + + +//#define VPRINTF vprintf +//#define PRINTF printf + +#define is_console(file) (0!=isatty(fileno(file))) + +//vprintf_without_ansiformats +int VFPRINTF(FILE *file, const char *fmt, va_list argptr) +{ + char *p, *q; + + if(!fmt || !*fmt) + return 0; + + if( is_console(file) || stdout_with_ansisequence ) + { + vfprintf(file, fmt, argptr); + return 0; + } + + if(tempbuf == NULL) + tempbuf_alloc(); + for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); + // vsnprintf returns -1 in case of insufficient buffer size + // tempbuf.realloc doubles the size of the buffer in this case + + // start with processing + p = tempbuf; + while ((q = strchr(p, 0x1b)) != NULL) + { // find the escape character + fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape + if( q[1]!='[' ) + { // write the escape char (whatever purpose it has) + fprintf(file, "%.*s", 1, q); + p=q+1; //and start searching again + } + else + { // from here, we will skip the '\033[' + // we break at the first unprocessible position + // assuming regular text is starting there + + // skip escape and bracket + q=q+2; + while(1) + { + if( isdigit((int)((unsigned char)*q)) ) + { + ++q; + // and next character + continue; + } + else if( *q == ';' ) + { // delimiter + ++q; + // and next number + continue; + } + else if( *q == 'm' ) + { // \033[#;...;#m - Set Graphics Rendition (SGR) + // set the attributes + } + else if( *q=='J' ) + { // \033[#J - Erase Display (ED) + } + else if( *q=='K' ) + { // \033[K : clear line from actual position to end of the line + } + else if( *q == 'H' || *q == 'f' ) + { // \033[#;#H - Cursor Position (CUP) + // \033[#;#f - Horizontal & Vertical Position + } + else if( *q=='s' ) + { // \033[s - Save Cursor Position (SCP) + } + else if( *q=='u' ) + { // \033[u - Restore cursor position (RCP) + } + else if( *q == 'A' ) + { // \033[#A - Cursor Up (CUU) + // Moves the cursor UP # number of lines + } + else if( *q == 'B' ) + { // \033[#B - Cursor Down (CUD) + // Moves the cursor DOWN # number of lines + } + else if( *q == 'C' ) + { // \033[#C - Cursor Forward (CUF) + // Moves the cursor RIGHT # number of columns + } + else if( *q == 'D' ) + { // \033[#D - Cursor Backward (CUB) + // Moves the cursor LEFT # number of columns + } + else if( *q == 'E' ) + { // \033[#E - Cursor Next Line (CNL) + // Moves the cursor down the indicated # of rows, to column 1 + } + else if( *q == 'F' ) + { // \033[#F - Cursor Preceding Line (CPL) + // Moves the cursor up the indicated # of rows, to column 1. + } + else if( *q == 'G' ) + { // \033[#G - Cursor Horizontal Absolute (CHA) + // Moves the cursor to indicated column in current row. + } + else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') + { // not implemented, just skip + } + else + { // no number nor valid sequencer + // something is fishy, we break and give the current char free + --q; + } + // skip the sequencer and search again + p = q+1; + break; + }// end while + } + } + if (*p) // write the rest of the buffer + fprintf(file, "%s", p); + return 0; +} +int FPRINTF(FILE *file, const char *fmt, ...) +{ + int ret; + va_list argptr; + va_start(argptr, fmt); + ret = VFPRINTF(file,fmt,argptr); + va_end(argptr); + return ret; +} + +#define FFLUSH fflush + +#define STDOUT stdout +#define STDERR stderr + +#endif// not _WIN32 + + + + + + + + + + char timestamp_format[20] = ""; //For displaying Timestamps + // by MC Cameri int _vShowMessage(enum msg_type flag, const char *string, va_list ap) { @@ -47,10 +651,18 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) FILE *fp; #endif - if (!string || strlen(string) <= 0) { + if (!string || *string == '\0') { ShowError("Empty string passed to _vShowMessage().\n"); return 1; } + if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) || + (flag == MSG_INFORMATION && msg_silent&1) || + (flag == MSG_STATUS && msg_silent&2) || + (flag == MSG_NOTICE && msg_silent&4) || + (flag == MSG_WARNING && msg_silent&8) || + (flag == MSG_ERROR && msg_silent&16) || + (flag == MSG_SQL && msg_silent&16)) + return 0; //Do not print it. if (timestamp_format[0]) { //Display time format. [Skotlex] @@ -58,7 +670,6 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) strftime(prefix, 80, timestamp_format, localtime(&t)); } else prefix[0]='\0'; - switch (flag) { case MSG_NONE: // direct printf replacement break; @@ -91,60 +702,36 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) return 1; } - if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) || - (flag == MSG_INFORMATION && msg_silent&1) || - (flag == MSG_STATUS && msg_silent&2) || - (flag == MSG_NOTICE && msg_silent&4) || - (flag == MSG_WARNING && msg_silent&8) || - (flag == MSG_ERROR && msg_silent&16) || - (flag == MSG_SQL && msg_silent&16) - ) ; //Do not print it. - else { - if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL) - { //Send Errors to StdErr [Skotlex] - fprintf (stderr, "%s ", prefix); - vfprintf (stderr, string, ap); - fflush (stderr); - } else { - if (flag != MSG_NONE) - printf ("%s ", prefix); - vprintf (string, ap); - fflush (stdout); - } + if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL) + { //Send Errors to StdErr [Skotlex] + FPRINTF(STDERR, "%s ", prefix); + VFPRINTF(STDERR, string, ap); + FFLUSH(STDERR); + } else { + if (flag != MSG_NONE) + FPRINTF(STDOUT, "%s ", prefix); + VFPRINTF(STDOUT, string, ap); + FFLUSH(STDOUT); } #if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN) if(strlen(DEBUGLOGPATH) > 0) { fp=fopen(DEBUGLOGPATH,"a"); if (fp == NULL) { - printf(CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n",DEBUGLOGPATH); - fflush(stdout); - return 0; + FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n", DEBUGLOGPATH); + FFLUSH(STDERR); + } else { + fprintf(fp,"%s ", prefix); + vfprintf(fp,string,ap); + fclose(fp); } - fprintf(fp,"%s ", prefix); - vfprintf(fp,string,ap); - fclose(fp); } else { - printf(CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n"); + FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n"); + FFLUSH(STDERR); } #endif va_end(ap); -/* - if ((core_config.debug_output_level > -1) && (flag >= core_config.debug_output_level)) { - FILE *fp; - fp=fopen(OUTPUT_MESSAGES_LOG,"a"); - if (fp == NULL) { - ShowError("Could not open '"CL_WHITE"%s"CL_RESET"', file not found.\n",OUTPUT_MESSAGES_LOG); - fflush(stdout); - return; - } - StripColor(output); - strcpy(output,"\r"); - fwrite(output,strlen(output),1,fp); - fclose(fp); - } -*/ return 0; } @@ -156,64 +743,84 @@ void ClearScreen(void) } int _ShowMessage(enum msg_type flag, const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(flag, string, ap); + ret = _vShowMessage(flag, string, ap); + va_end(ap); + return ret; } // direct printf replacement int ShowMessage(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_NONE, string, ap); + ret = _vShowMessage(MSG_NONE, string, ap); + va_end(ap); + return ret; } int ShowStatus(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_STATUS, string, ap); + ret = _vShowMessage(MSG_STATUS, string, ap); + va_end(ap); + return ret; } int ShowSQL(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_SQL, string, ap); + ret = _vShowMessage(MSG_SQL, string, ap); + va_end(ap); + return ret; } int ShowInfo(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_INFORMATION, string, ap); + ret = _vShowMessage(MSG_INFORMATION, string, ap); + va_end(ap); + return ret; } int ShowNotice(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_NOTICE, string, ap); + ret = _vShowMessage(MSG_NOTICE, string, ap); + va_end(ap); + return ret; } int ShowWarning(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_WARNING, string, ap); + ret = _vShowMessage(MSG_WARNING, string, ap); + va_end(ap); + return ret; } int ShowDebug(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_DEBUG, string, ap); + ret = _vShowMessage(MSG_DEBUG, string, ap); + va_end(ap); + return ret; } -int ShowError(const char *string, ...) { +int ShowError(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_ERROR, string, ap); + ret = _vShowMessage(MSG_ERROR, string, ap); + va_end(ap); + return ret; } int ShowFatalError(const char *string, ...) { + int ret; va_list ap; - va_start(ap, string); - return _vShowMessage(MSG_FATALERROR, string, ap); + ret = _vShowMessage(MSG_FATALERROR, string, ap); + va_end(ap); + return ret; } diff --git a/src/common/showmsg.h b/src/common/showmsg.h index c0112685ad..f947b34e34 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -5,7 +5,6 @@ #define _SHOWMSG_H_ #define SHOW_DEBUG_MSG 1 - // for help with the console colors look here: // http://www.edoceo.com/liberum/?doc=printf-with-color // some code explanation (used here): @@ -14,53 +13,62 @@ // \033[0m : reset color parameter // \033[1m : use bold for font -#ifdef _WIN32 - #define CL_RESET "" - #define CL_CLS "" - #define CL_CLL "" - #define CL_BOLD "" - #define CL_NORMAL CL_RESET - #define CL_NONE CL_RESET - #define CL_WHITE "" - #define CL_GRAY "" - #define CL_RED "" - #define CL_GREEN "" - #define CL_YELLOW "" - #define CL_BLUE "" - #define CL_MAGENTA "" - #define CL_CYAN "" - #define CL_BT_YELLOW "" - #define CL_WTBL "" - #define CL_XXBL "" - #define CL_PASS "" -#else - #define CL_RESET "\033[0;0m" - #define CL_CLS "\033[2J" - #define CL_CLL "\033[K" +#define CL_RESET "\033[0m" +#define CL_CLS "\033[2J" +#define CL_CLL "\033[K" - // font settings - #define CL_BOLD "\033[1m" - #define CL_NORMAL CL_RESET - #define CL_NONE CL_RESET +// font settings +#define CL_BOLD "\033[1m" +#define CL_NORM CL_RESET +#define CL_NORMAL CL_RESET +#define CL_NONE CL_RESET +// foreground color and bold font (bright color on windows) +#define CL_WHITE "\033[1;37m" +#define CL_GRAY "\033[1;30m" +#define CL_RED "\033[1;31m" +#define CL_GREEN "\033[1;32m" +#define CL_YELLOW "\033[1;33m" +#define CL_BLUE "\033[1;34m" +#define CL_MAGENTA "\033[1;35m" +#define CL_CYAN "\033[1;36m" - #define CL_WHITE "\033[1;37m" - #define CL_GRAY "\033[1;30m" - #define CL_RED "\033[1;31m" - #define CL_GREEN "\033[1;32m" - #define CL_YELLOW "\033[1;33m" - #define CL_BLUE "\033[1;34m" - #define CL_MAGENTA "\033[1;35m" - #define CL_CYAN "\033[1;36m" - - #define CL_BT_YELLOW "\033[1;33m" - #define CL_WTBL "\033[37;44m" // white on blue - #define CL_XXBL "\033[0;44m" // default on blue - #define CL_PASS "\033[0;32;42m" // green on green -#endif +// background color +#define CL_BG_BLACK "\033[40m" +#define CL_BG_RED "\033[41m" +#define CL_BG_GREEN "\033[42m" +#define CL_BG_YELLOW "\033[43m" +#define CL_BG_BLUE "\033[44m" +#define CL_BG_MAGENTA "\033[45m" +#define CL_BG_CYAN "\033[46m" +#define CL_BG_WHITE "\033[47m" +// foreground color and normal font (normal color on windows) +#define CL_LT_BLACK "\033[0;30m" +#define CL_LT_RED "\033[0;31m" +#define CL_LT_GREEN "\033[0;32m" +#define CL_LT_YELLOW "\033[0;33m" +#define CL_LT_BLUE "\033[0;34m" +#define CL_LT_MAGENTA "\033[0;35m" +#define CL_LT_CYAN "\033[0;36m" +#define CL_LT_WHITE "\033[0;37m" +// foreground color and bold font (bright color on windows) +#define CL_BT_BLACK "\033[1;30m" +#define CL_BT_RED "\033[1;31m" +#define CL_BT_GREEN "\033[1;32m" +#define CL_BT_YELLOW "\033[1;33m" +#define CL_BT_BLUE "\033[1;34m" +#define CL_BT_MAGENTA "\033[1;35m" +#define CL_BT_CYAN "\033[1;36m" +#define CL_BT_WHITE "\033[1;37m" +#define CL_WTBL "\033[37;44m" // white on blue +#define CL_XXBL "\033[0;44m" // default on blue +#define CL_PASS "\033[0;32;42m" // green on green + +#define CL_SPACE " " // space aquivalent of the print messages + +extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs] extern int msg_silent; //Specifies how silent the console is. [Skotlex] extern char timestamp_format[20]; //For displaying Timestamps [Skotlex] -extern char tmp_output[1024]; enum msg_type { MSG_NONE, diff --git a/src/login/login.c b/src/login/login.c index ceb0448e41..038cff89b7 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -3603,6 +3603,8 @@ int login_config_read(const char *cfgName) { if(strcmpi(w1,"timestamp_format") == 0) { strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); } else if(strcmpi(w1,"console_silent")==0){ msg_silent = 0; //To always allow the next line to show up. ShowInfo("Console Silent Setting: %d\n", atoi(w2)); diff --git a/src/login_sql/login.c b/src/login_sql/login.c index 8dd0bcc484..903b38b350 100644 --- a/src/login_sql/login.c +++ b/src/login_sql/login.c @@ -2074,6 +2074,8 @@ int login_config_read(const char *cfgName){ remove_control_chars((unsigned char *) w2); if(strcmpi(w1,"timestamp_format") == 0) { strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); } else if(strcmpi(w1,"console_silent")==0){ msg_silent = 0; //To always allow the next line to show up. ShowInfo("Console Silent Setting: %d\n", atoi(w2)); diff --git a/src/map/map.c b/src/map/map.c index 52035d3bd4..f55a851709 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -3292,6 +3292,18 @@ int parse_console(char *buf) { return 0; } +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, franais, deutsch, espaol +//------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + return atoi(str); +} + /*========================================== * ݒt@C?? *------------------------------------------ @@ -3321,6 +3333,8 @@ int map_config_read(char *cfgName) { if(strcmpi(w1,"timestamp_format")==0){ strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); } else if(strcmpi(w1,"console_silent")==0){ msg_silent = 0; //To always allow the next line to show up. ShowInfo("Console Silent Setting: %d\n", atoi(w2)); diff --git a/src/map/skill.c b/src/map/skill.c index ea9f44c4f8..02c5e1e035 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -10087,8 +10087,13 @@ int skill_unit_move_sub (struct block_list *bl, va_list ap) } else { - if (flag&2 && skill_unit_index < 7) //Store this unit id. - skill_unit_temp[skill_unit_index++] = skill_id; + if (flag&2) { //Store this unit id. + if (skill_unit_index < 7) + skill_unit_temp[skill_unit_index++] = skill_id; + else if (battle_config.error_log) + ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n"); + } + } if (flag&4) skill_unit_onleft(skill_id,target,tick); @@ -10112,8 +10117,12 @@ int skill_unit_move_sub (struct block_list *bl, va_list ap) else { result = skill_unit_onout(unit,target,tick); - if (flag&2 && skill_unit_index < 7 && result) //Store this unit id. - skill_unit_temp[skill_unit_index++] = result; + if (flag&2 && result) { //Store this unit id. + if (skill_unit_index < 7) + skill_unit_temp[skill_unit_index++] = result; + else if (battle_config.error_log) + ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n"); + } } //TODO: Normally, this is dangerous since the unit and group could be freed diff --git a/src/map/status.c b/src/map/status.c index 6426eced32..f0b16afed9 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -4825,6 +4825,16 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val break; case SC_ENDURE: val2 = 7; // Hit-count [Celest] + if (!(flag&1) && sd && !map_flag_gvg(bl->m)) + { + struct map_session_data *tsd; + int i; + for (i = 0; i < 5; i++) + { //See if there are devoted characters, and pass the status to them. [Skotlex] + if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) + status_change_start(&tsd->bl,type,10000,val1,val2,val3,val4,tick,1); + } + } break; case SC_AUTOBERSERK: if (status->hp < status->max_hp>>2 && @@ -4884,14 +4894,14 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val break; case SC_REFLECTSHIELD: val2=10+val1*3; //% Dmg reflected - if (sd) + if (sd && !(flag&1)) { //Pass it to devoted chars. struct map_session_data *tsd; int i; for (i = 0; i < 5; i++) { //Pass the status to the other affected chars. [Skotlex] if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,SC_AUTOGUARD,10000,val1,val2,0,0,tick,1); + status_change_start(&tsd->bl,type,10000,val1,val2,0,0,tick,1); } } break; @@ -5103,7 +5113,7 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val break; case SC_AUTOGUARD: - if (!flag) + if (!(flag&1)) { struct map_session_data *tsd; int i,t; @@ -5115,13 +5125,13 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val for (i = 0; i < 5; i++) { //Pass the status to the other affected chars. [Skotlex] if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,SC_AUTOGUARD,10000,val1,val2,0,0,tick,1); + status_change_start(&tsd->bl,type,10000,val1,val2,0,0,tick,1); } } break; case SC_DEFENDER: - if (!flag) + if (!(flag&1)) { struct map_session_data *tsd; int i; @@ -5133,7 +5143,7 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val for (i = 0; i < 5; i++) { //See if there are devoted characters, and pass the status to them. [Skotlex] if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,SC_DEFENDER,10000,val1,5+val1*5,val3,val4,tick,1); + status_change_start(&tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,1); } } break; @@ -5361,16 +5371,16 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val { //Try to inherit the status from the Crusader [Skotlex] //Ideally, we should calculate the remaining time and use that, but we'll trust that //once the Crusader's status changes, it will reflect on the others. - int type2 = SC_AUTOGUARD; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - type2 = SC_DEFENDER; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - type2 = SC_REFLECTSHIELD; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - + const int types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE }; + int type2; + int i = map_flag_gvg(bl->m)?2:3; + while (i >= 0) { + type2 = types[i]; + if (src->sc.data[type2].timer != -1) + sc_start(bl,type2,100,src->sc.data[type2].val1, + skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); + i--; + } } break; } @@ -5992,6 +6002,7 @@ int status_change_end( struct block_list* bl , int type,int tid ) case SC_DEFENDER: case SC_REFLECTSHIELD: case SC_AUTOGUARD: + case SC_ENDURE: if (sd) { struct map_session_data *tsd; int i; @@ -6011,13 +6022,15 @@ int status_change_end( struct block_list* bl , int type,int tid ) md->devotion[sc->data[type].val2] = 0; clif_devotion(md); } - //Remove AutoGuard and Defender [Skotlex] + //Remove inherited status [Skotlex] if (sc->data[SC_AUTOGUARD].timer != -1) status_change_end(bl,SC_AUTOGUARD,-1); if (sc->data[SC_DEFENDER].timer != -1) status_change_end(bl,SC_DEFENDER,-1); if (sc->data[SC_REFLECTSHIELD].timer != -1) status_change_end(bl,SC_REFLECTSHIELD,-1); + if (sc->data[SC_ENDURE].timer != -1) + status_change_end(bl,SC_ENDURE,-1); break; } case SC_BLADESTOP: