diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt index 1792deb93..811af65cf 100644 --- a/conf/CMakeLists.txt +++ b/conf/CMakeLists.txt @@ -4,10 +4,10 @@ set(OUTPUT_CONFS ad.conf anongame_infos.conf address_translation.conf bnhelp.conf bnissue.txt bnmaps.conf bnxpcalc.conf bnmotd-enUS.txt bnmotd-csCZ.txt bnmotd-deDE.txt bnmotd-esES.txt bnmotd-frFR.txt bnmotd-nlNL.txt bnmotd-plPL.txt bnmotd-ruRU.txt bnmotd-zhCN.txt - bnmotd-zhTW.txt bnmotd-ptBR.txt + bnmotd-zhTW.txt bnmotd-ptBR.txt bnmotd_w3.txt bnxplevel.conf channel.conf command_groups.conf news.txt realm.conf sql_DB_layout2.conf sql_DB_layout.conf supportfile.conf topics.conf - tournament.conf versioncheck.conf) + tournament.conf versioncheck.conf icons.conf) foreach(CONF ${OUTPUT_CONFS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${CONF}.in ${CMAKE_CURRENT_BINARY_DIR}/${CONF} @ONLY) endforeach(CONF) @@ -25,16 +25,16 @@ if(WITH_BNETD) bnmotd-plPL.txt bnmotd-ruRU.txt bnmotd-zhCN.txt bnmotd-zhTW.txt bnmotd-ptBR.txt bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf news.txt bnmotd_w3.txt realm.conf sql_DB_layout.conf sql_DB_layout2.conf supportfile.conf topics.conf - tournament.conf versioncheck.conf) + tournament.conf versioncheck.conf icons.conf) - # special treatement for non .in files + # special treatment for non .in files install(FILES bnetd_default_user.cdb DESTINATION ${SYSCONFDIR}) endif(WITH_BNETD) if(WITH_D2CS) set(D2CS_CONFS d2cs.conf anongame_infos.conf) - # special treatement for non .in files + # special treatment for non .in files install(FILES d2server.ini DESTINATION ${SYSCONFDIR}) endif(WITH_D2CS) diff --git a/conf/bnetd.conf.in b/conf/bnetd.conf.in index f56351671..38e14a6df 100644 --- a/conf/bnetd.conf.in +++ b/conf/bnetd.conf.in @@ -108,6 +108,7 @@ anongame_infos_file = "${SYSCONFDIR}/anongame_infos.conf" DBlayoutfile = "${SYSCONFDIR}/sql_DB_layout.conf" supportfile = "${SYSCONFDIR}/supportfile.conf" transfile = "${SYSCONFDIR}/address_translation.conf" +customicons_file = "${SYSCONFDIR}/icons.conf" fortunecmd = /usr/games/fortune diff --git a/conf/bnetd.conf.win32 b/conf/bnetd.conf.win32 index ee1524b5d..9e9251483 100644 --- a/conf/bnetd.conf.win32 +++ b/conf/bnetd.conf.win32 @@ -86,6 +86,7 @@ aliasfile = conf\bnalias.conf anongame_infos_file = conf\anongame_infos.conf DBlayoutfile = conf\sql_DB_layout.conf supportfile = conf\supportfile.conf +customicons_file = conf\icons.conf fortunecmd = bin\fortune.exe diff --git a/conf/icons.conf.in b/conf/icons.conf.in new file mode 100644 index 000000000..d25fb8c0e --- /dev/null +++ b/conf/icons.conf.in @@ -0,0 +1,103 @@ +############################################################################## +# icons.conf - Custom icons configuration file # +#----------------------------------------------------------------------------# +# # +# Allowed clients: # +# W3XP, WAR3, STAR, SEXP, JSTR, SSHR, W2BN, DRTL, DSHR # +# # +# W3XP: It also disables icon selection from user portrait # +# # +# [icons] table format (first variable always corresponds to icon_key: # +# icon_key | rank | icon_code # +# # +# [stats] output format: # +# initialize variables under a client tag # +# band variables with figure brackets {{var}} # +# use {{variable->rank}} to display an icon rank for a custom variable # +# # +############################################################################## + + +############################################################################## +# General settings # +#----------------------------------------------------------------------------# + +# Enable icon sets below +custom_icons = false + + + +############################################################################## +# Warcraft 3 icon set # +#----------------------------------------------------------------------------# + +[W3XP] +solo_level = "Record\W3XP\solo_level" +solo_xp = "Record\W3XP\solo_xp" +solo_wins = "Record\W3XP\solo_wins" +solo_losses = "Record\W3XP\solo_losses" +team_level = "Record\W3XP\team_level" +team_xp = "Record\W3XP\team_xp" +team_wins = "Record\W3XP\team_wins" +team_losses = "Record\W3XP\team_losses" +ffa_level = "Record\W3XP\ffa_level" +ffa_xp = "Record\W3XP\ffa_xp" +ffa_wins = "Record\W3XP\ffa_wins" +ffa_losses = "Record\W3XP\ffa_losses" +username = "BNET\acct\username" + +[icons] +0 Beginner KBKB +1 Dungeon KBKD +2 Expert KBKE +3 Mid KBKM +4 Pro KBKP +5 World KBKW +6 Universe WCYB +[/icons] + +[stats] +{{username}}'s record: +Solo games: [{{solo_level->rank}}] {{solo_xp}} xp ({{solo_wins}} - {{solo_losses}}) +Team games: [{{team_level->rank}}] {{team_xp}} xp ({{team_wins}} - {{team_losses}}) +FFA games: [{{ffa_level->rank}}] {{ffa_xp}} xp ({{ffa_wins}} - {{ffa_losses}}) +[/stats] + + + +############################################################################## +# Starcraft icon set # +#----------------------------------------------------------------------------# + +[SEXP] +rating1 = "Record\SEXP\1_rating" +wins1 = "Record\SEXP\1_wins" +losses1 = "Record\SEXP\1_losses" +disconnects1 = "Record\SEXP\1_disconnects" +rating0 = "Record\SEXP\0_rating" +wins0 = "Record\SEXP\0_wins" +losses0 = "Record\SEXP\0_losses" +disconnects0 = "Record\SEXP\0_disconnects" +username = "BNET\acct\username" + +[icons] +1000 n00b NOOB +1250 Chobo+ CHO1 +1500 Chobo++ CHO2 +1750 Chobo+++ CHO3 +2000 Hasu+ HAS1 +2300 Hasu++ HAS2 +2650 Hasu+++ HAS3 +3000 Gosu+ GOS1 +3300 Gosu++ GOS2 +3650 Gosu+++ GOS3 +4000 Mega Gosu GOSU +5500 Father MEGA +[/icons] + +[stats] +{{username}}'s record: +Ladder games: [{{rating1->rank}}] {{rating1}} pts ({{wins1}}/{{losses1}}/{{disconnects1}}) +Normal games: [{{rating0->rank}}] {{rating0}} pts ({{wins0}}/{{losses0}}/{{disconnects0}}) +[/stats] + diff --git a/src/bnetd/command.cpp b/src/bnetd/command.cpp index d3dde2fc3..4fe3a553e 100644 --- a/src/bnetd/command.cpp +++ b/src/bnetd/command.cpp @@ -76,6 +76,7 @@ #include "clan.h" #include "common/setup_after.h" #include "common/flags.h" +#include "icons.h" #include "attrlayer.h" @@ -2185,6 +2186,29 @@ namespace pvpgn clienttag_uint = tag_case_str_to_uint(clienttag); + + // custom stats + if (prefs_get_custom_icons() == 1) + { + const char *text; + + // if text is not empty + if (text = get_custom_stats_text(account, clienttag_uint)) + { + // split by lines + char* output_array = strtok((char*)text, "\n"); + while (output_array) + { + message_send_text(c, message_type_info, c, output_array); + output_array = strtok(NULL, "\n"); + } + xfree((char*)text); + + return 0; + } + } + + switch (clienttag_uint) { case CLIENTTAG_BNCHATBOT_UINT: @@ -4679,7 +4703,7 @@ namespace pvpgn value = arg3; // disallow get/set value for password hash and username (hash can be cracked easily, account name should be permanent) - if (std::strcmp(key, "bnet\\acct\\passhash1") == 0 || std::strcmp(key, "bnet\\acct\\username") == 0 || std::strcmp(key, "bnet\\username") == 0) + if (strcasecmp(key, "bnet\\acct\\passhash1") == 0 || strcasecmp(key, "bnet\\acct\\username") == 0 || strcasecmp(key, "bnet\\username") == 0) { message_send_text(c, message_type_info, c, "Access denied due to security reason."); return 0; @@ -4709,6 +4733,10 @@ namespace pvpgn return 0; } + // unset value + if (strcasecmp(value, "null") == 0) + value = NULL; + std::sprintf(msgtemp, "for \"%s\" (%.64s = \"%.128s\")", account_get_name(account), key, value); if (account_set_strattr(account, key, value) < 0) diff --git a/src/bnetd/connection.cpp b/src/bnetd/connection.cpp index 31d4ba4d7..12af880cd 100644 --- a/src/bnetd/connection.cpp +++ b/src/bnetd/connection.cpp @@ -74,6 +74,7 @@ #include "attrlayer.h" #include "anongame_wol.h" #include "common/setup_after.h" +#include "icons.h" namespace pvpgn { @@ -2521,13 +2522,14 @@ namespace pvpgn return 0; } - + /* Player icon that displayed in a channel in all games (except Warcraft 3) */ extern char const * conn_get_playerinfo(t_connection const * c) { t_account * account; static char playerinfo[MAX_PLAYERINFO_STR]; t_clienttag clienttag; char revtag[5]; + char const * usericon; if (!c) { @@ -2547,7 +2549,22 @@ namespace pvpgn } tag_uint_to_revstr(revtag, clienttag); - if (clienttag == CLIENTTAG_BNCHATBOT_UINT) + // allow set icon to a user directly from the database (override default tag always if not null) + if (usericon = account_get_user_icon(account, clienttag)) + std::sprintf(revtag, usericon); + + // if custom_icons is enabled then set a custom client tag by player rating + if (prefs_get_custom_icons() == 1) + { + t_icon_info * icon; + + // do not override userselectedicon if it's not null + if (!usericon && (icon = get_custom_icon(account, clienttag))) + strcpy(revtag, icon->icon_code); + + std::strcpy(playerinfo, revtag); + } + else if (clienttag == CLIENTTAG_BNCHATBOT_UINT) { std::strcpy(playerinfo, revtag); /* FIXME: what to return here? */ } @@ -3674,6 +3691,7 @@ namespace pvpgn return count; } + /* Warcraft 3 icon that displayed in a channel */ extern int conn_update_w3_playerinfo(t_connection * c) { t_account * account; @@ -3725,15 +3743,35 @@ namespace pvpgn while ((*clantag_str) == 0) clantag_str++; } - if (acctlevel == 0) { + // allow set icon to a user directly from the database (override default icon always if not null) + usericon = account_get_user_icon(account, clienttag); + + // if custom stats is enabled then set a custom client icon by player rating + if (prefs_get_custom_icons() == 1) + { + t_icon_info * icon; + + // do not override userselectedicon if it's not null + if (!usericon && (icon = get_custom_icon(account, clienttag))) + usericon = xstrdup(icon->icon_code); + + acctlevel = 0; + if (clantag) + std::sprintf(tempplayerinfo, "%s %s %u %s", revtag, usericon, acctlevel, clantag_str); + else + std::sprintf(tempplayerinfo, "%s %s %u", revtag, usericon, acctlevel); + } + // default icon "WAR3" or "W3XP" + else if (acctlevel == 0 && !usericon) { if (clantag) std::sprintf(tempplayerinfo, "%s %s 0 %s", revtag, revtag, clantag_str); else std::strcpy(tempplayerinfo, revtag); eventlog(eventlog_level_info, __FUNCTION__, "[%d] %s", conn_get_socket(c), revtag); } - else { - usericon = account_get_user_icon(account, clienttag); + // display race icon with a level number + else + { if (!usericon) { if (clantag) std::sprintf(tempplayerinfo, "%s %1u%c3W %u %s", revtag, raceiconnumber, raceicon, acctlevel, clantag_str); diff --git a/src/bnetd/handle_anongame.cpp b/src/bnetd/handle_anongame.cpp index 92eb819ec..803db194b 100644 --- a/src/bnetd/handle_anongame.cpp +++ b/src/bnetd/handle_anongame.cpp @@ -38,6 +38,7 @@ #include "tournament.h" #include "channel.h" #include "common/setup_after.h" +#include "icons.h" namespace pvpgn { @@ -439,10 +440,10 @@ namespace pvpgn return 0; } + /* Open portrait in Warcraft 3 user profile */ static int _client_anongame_get_icon(t_connection * c, t_packet const * const packet) { t_packet * rpacket; - //BlacKDicK 04/20/2003 Need some huge re-work on this. { struct @@ -491,7 +492,16 @@ namespace pvpgn packet_set_type(rpacket, SERVER_FINDANONGAME_ICONREPLY); bn_int_set(&rpacket->u.server_findanongame_iconreply.count, bn_int_get(packet->u.client_findanongame_inforeq.count)); bn_byte_set(&rpacket->u.server_findanongame_iconreply.option, CLIENT_FINDANONGAME_GET_ICON); - if ((uicon = account_get_user_icon(acc, clienttag))) + + + if (prefs_get_custom_icons() == 1) + { + // get current custom icon + t_icon_info * icon; + if (icon = get_custom_icon(acc, clienttag)) + std::memcpy(&rpacket->u.server_findanongame_iconreply.curricon, icon->icon_code, 4); + } + else if ((uicon = account_get_user_icon(acc, clienttag))) { std::memcpy(&rpacket->u.server_findanongame_iconreply.curricon, uicon, 4); } @@ -517,7 +527,10 @@ namespace pvpgn //Building the icon for the races bn_short_set(&tempicon.required_wins, icon_req_race_wins); if (account_get_racewins(acc, race[i], clienttag) >= icon_req_race_wins) { - tempicon.client_enabled = 1; + if (prefs_get_custom_icons() == 1) + tempicon.client_enabled = 0; + else + tempicon.client_enabled = 1; } else{ tempicon.client_enabled = 0; @@ -528,7 +541,10 @@ namespace pvpgn icon_req_tourney_wins = anongame_infos_get_ICON_REQ_TOURNEY(j + 1); bn_short_set(&tempicon.required_wins, icon_req_tourney_wins); if (account_get_racewins(acc, race[i], clienttag) >= icon_req_tourney_wins) { - tempicon.client_enabled = 1; + if (prefs_get_custom_icons() == 1) + tempicon.client_enabled = 0; + else + tempicon.client_enabled = 1; } else{ tempicon.client_enabled = 0; @@ -544,6 +560,7 @@ namespace pvpgn return 0; } + /* Choose icon by user from profile > portrait */ static int _client_anongame_set_icon(t_connection * c, t_packet const * const packet) { //BlacKDicK 04/20/2003 @@ -552,6 +569,12 @@ namespace pvpgn t_account * account; t_clienttag ctag; + // disable with custom icons + if (prefs_get_custom_icons() == 1) + { + return 0; + } + /*FIXME: In this case we do not get a 'count' but insted of it we get the icon that the client wants to set.'W3H2' for an example. For now it is ok, since they share the same position on the packet*/ @@ -568,6 +591,7 @@ namespace pvpgn account = conn_get_account(c); + // ICON SWITCH HACK PROTECTION if (check_user_icon(account, user_icon) == 0) { eventlog(eventlog_level_info, __FUNCTION__, "[%s] \"%s\" ICON SWITCH hack attempt, icon set to default ", conn_get_username(c), user_icon); @@ -585,7 +609,7 @@ namespace pvpgn return 0; } - // check user for illegal icon + /* Check user choice for illegal icon */ static int check_user_icon(t_account * account, const char * user_icon) { unsigned int i, len; diff --git a/src/bnetd/handle_bnet.cpp b/src/bnetd/handle_bnet.cpp index c3d855ed4..76fd50463 100644 --- a/src/bnetd/handle_bnet.cpp +++ b/src/bnetd/handle_bnet.cpp @@ -24,6 +24,7 @@ #include "handle_bnet.h" #include +#include #include #include #include diff --git a/src/bnetd/icons.cpp b/src/bnetd/icons.cpp new file mode 100644 index 000000000..5535fd8f4 --- /dev/null +++ b/src/bnetd/icons.cpp @@ -0,0 +1,513 @@ +/* +* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "common/setup_before.h" + +#include +#include +#include +#include +#include + +#include "compat/strcasecmp.h" +#include "compat/snprintf.h" + +#include "common/token.h" + +#include "common/list.h" +#include "common/eventlog.h" +#include "common/xalloc.h" +#include "common/xstring.h" +#include "common/util.h" +#include "common/tag.h" + +#include "account.h" +#include "connection.h" +#include "icons.h" +#include "account_wrap.h" +#include "common/setup_after.h" + +namespace pvpgn +{ + + namespace bnetd + { + + static t_list * icon_head = NULL; + // TODO: wrapper to get value + static int enable_custom_icons = 0; + + + static int skip_comments(char *buff); + static t_icon_var_info * _read_option(char *str, unsigned lineno); + static t_icon_info * _find_custom_icon(int rating, char * clienttag); + static char * _find_attr_key(char * clienttag); + + + extern int prefs_get_custom_icons() + { + return enable_custom_icons; + } + + + /* Format stats text, with attributes from a storage, and output text to a user */ + extern const char * get_custom_stats_text(t_account * account, t_clienttag clienttag) + { + const char *value; + const char *text; + char clienttag_str[5], tmp[64]; + t_icon_info * icon; + t_iconset_info * iconset; + t_icon_var_info * var; + t_elem * curr; + t_elem * curr_var; + + tag_uint_to_str(clienttag_str, clienttag); + + text = NULL; + + if (icon_head) { + LIST_TRAVERSE(icon_head, curr) + { + if (!(iconset = (t_iconset_info*)elem_get_data(curr))) + { + eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); + continue; + } + + // find a needed tag + if (std::strcmp(iconset->clienttag, clienttag_str) != 0) + continue; + + if (!iconset->stats) + return NULL; + + text = xstrdup(iconset->stats); + + LIST_TRAVERSE(iconset->vars, curr_var) + { + if (!(var = (t_icon_var_info*)elem_get_data(curr_var))) + { + eventlog(eventlog_level_error, __FUNCTION__, "vars list contains NULL item"); + continue; + } + + // replace NULL attributes to "0" + if (!(value = account_get_strattr(account, var->value))) + value = "0"; + + // replace in a text + snprintf(tmp, sizeof(tmp), "{{%s}}", var->key); + text = str_replace((char*)text, tmp, (char*)value); + + // also replace {var}->rank + if (icon = _find_custom_icon(atoi(value), clienttag_str)) + { + snprintf(tmp, sizeof(tmp), "{{%s->rank}}", var->key); + text = str_replace((char*)text, tmp, icon->rank); + } + } + } + } + + return text; + } + + + /* find icon code by rating for the clienttag */ + extern t_icon_info * get_custom_icon(t_account * account, t_clienttag clienttag) + { + char * attr_key; + int rating; + char clienttag_str[5]; + t_icon_info * icon; + t_icon_info * uicon; + const char * usericon; + + tag_uint_to_str(clienttag_str, clienttag); + + // get attribute field name from a storage + if (!(attr_key = _find_attr_key((char*)clienttag_str))) + { + eventlog(eventlog_level_error, __FUNCTION__, "could not find attr_key in iconset for tag %s", clienttag_str); + return NULL; + } + + rating = account_get_numattr(account, attr_key); + + icon = _find_custom_icon(rating, clienttag_str); + return icon; + } + + + extern int customicons_unload(void) + { + t_elem * curr; + t_iconset_info * iconset; + + if (icon_head) { + LIST_TRAVERSE(icon_head, curr) + { + if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { + eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); + continue; + } + + if (list_remove_elem(icon_head, &curr) < 0) + eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); + + xfree(iconset); + } + + if (list_destroy(icon_head) < 0) + return -1; + icon_head = NULL; + } + return 0; + } + + + /*****/ + extern int customicons_load(char const * filename) + { + std::FILE * fp; + unsigned int line, pos, counter = 0; + bool end_of_iconset = false; + char *buff, *value; + char *rating, *rank, *icon; + t_icon_var_info * option; + + icon_head = list_create(); + + + if (!filename) { + eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); + return -1; + } + + if (!(fp = std::fopen(filename, "r"))) { + eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"%s\" for reading (std::fopen: %s)", filename, std::strerror(errno)); + return -1; + } + + /* 1) parse root config options */ + for (line = 1; (buff = file_get_line(fp)); line++) + { + if (skip_comments(buff) > 0) + { + continue; + } + // read root options + if (option = _read_option(buff, line)) + { + if (std::strcmp(option->key, "custom_icons") == 0) + if (std::strcmp(option->value, "true") == 0) + enable_custom_icons = 1; + else + enable_custom_icons = 0; + } + + /* 2) parse clienttags */ + if (std::strcmp(buff, "[W3XP]") == 0 || + std::strcmp(buff, "[WAR3]") == 0 || + std::strcmp(buff, "[STAR]") == 0 || + std::strcmp(buff, "[SEXP]") == 0 || + std::strcmp(buff, "[JSTR]") == 0 || + std::strcmp(buff, "[SSHR]") == 0 || + std::strcmp(buff, "[W2BN]") == 0 || + std::strcmp(buff, "[DRTL]") == 0 || + std::strcmp(buff, "[DSHR]") == 0) + { + if (skip_comments(buff) > 0) + { + continue; + } + value = std::strtok(buff, " []"); + + // new iconset for a clienttag + t_iconset_info * icon_set = (t_iconset_info*)xmalloc(sizeof(t_iconset_info)); + icon_set->clienttag = xstrdup(value); + icon_set->attr_key = NULL; + icon_set->icon_info = list_create(); + icon_set->vars = list_create(); + icon_set->stats = NULL; + + + /* 3) parse inner options under a clienttag */ + for (; (buff = file_get_line(fp)); line++) + { + if (end_of_iconset) + { + end_of_iconset = false; + break; + } + if (skip_comments(buff) > 0) + { + continue; + } + // fill variables list + if (option = _read_option(buff, line)) + { + // set attr_key with a first variable + if (!icon_set->attr_key) + icon_set->attr_key = xstrdup(option->value); + + // add to variables + list_append_data(icon_set->vars, option); + } + + /* 3) parse icons section */ + if (std::strcmp(buff, "[icons]") == 0) + { + counter = 0; + for (; (buff = file_get_line(fp)); line++) + { + if (skip_comments(buff) > 0) + { + continue; + } + // end if icons + if (std::strcmp(buff, "[/icons]") == 0) { + break; + } + + pos = 0; + if (!(rating = next_token(buff, &pos))) + { + eventlog(eventlog_level_error, __FUNCTION__, "missing value in line %u in file \"%s\"", line, filename); + continue; + } + if (!(rank = next_token(buff, &pos))) + { + eventlog(eventlog_level_error, __FUNCTION__, "missing rank in line %u in file \"%s\"", line, filename); + continue; + } + if (!(icon = next_token(buff, &pos))) + { + eventlog(eventlog_level_error, __FUNCTION__, "missing icon in line %u in file \"%s\"", line, filename); + continue; + } + counter++; + + t_icon_info * icon_info = (t_icon_info*)xmalloc(sizeof(t_icon_info)); + icon_info->rating = atoi(rating); + icon_info->rank = xstrdup(rank); + icon_info->icon_code = xstrdup(_strrev(icon)); // save reversed icon code + list_prepend_data(icon_set->icon_info, icon_info); + } + } + + /* 3) parse stats section */ + if (std::strcmp(buff, "[stats]") == 0) + { + std::string tmp; + for (; (buff = file_get_line(fp)); line++) + { + // end of stats + if (std::strcmp(buff, "[/stats]") == 0) { + // put whole text of stats after read + icon_set->stats = xstrdup(tmp.c_str()); + end_of_iconset = true; + break; + } + + tmp = tmp + buff + "\n"; + } + } + } + + if (!icon_set->attr_key) + { + eventlog(eventlog_level_error, __FUNCTION__, "attr_key is null for iconset %s", icon_set->clienttag); + continue; + } + list_append_data(icon_head, icon_set); + + eventlog(eventlog_level_trace, __FUNCTION__, "loaded %u custom icons for %s", counter, icon_set->clienttag); + } + } + + + return 0; + } + + static int skip_comments(char *buff) + { + char *temp; + int pos; + + for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); + if (buff[pos] == '\0' || buff[pos] == '#') { + return 1; + } + if ((temp = std::strrchr(buff, '#'))) { + unsigned int len; + unsigned int endpos; + + *temp = '\0'; + len = std::strlen(buff) + 1; + for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); + buff[endpos + 1] = '\0'; + } + return 0; + } + + /* Read an option like "key = value" from a str line, and split in into a pair: key, value */ + static t_icon_var_info * _read_option(char *str, unsigned lineno) + { + char *cp, prev, *directive; + t_icon_var_info * icon_var = (t_icon_var_info*)xmalloc(sizeof(t_icon_var_info)); + + directive = str; + str = str_skip_word(str + 1); + if (*str) *(str++) = '\0'; + + str = str_skip_space(str); + if (*str != '=') { + return NULL; + } + + str = str_skip_space(str + 1); + if (!*str) { + eventlog(eventlog_level_error, __FUNCTION__, "missing value at line %u", lineno); + return NULL; + } + + if (*str == '"') { + for (cp = ++str, prev = '\0'; *cp; cp++) { + switch (*cp) { + case '"': + if (prev != '\\') break; + prev = '"'; + continue; + case '\\': + if (prev == '\\') prev = '\0'; + else prev = '\\'; + continue; + default: + prev = *cp; + continue; + } + break; + } + + if (*cp != '"') { + eventlog(eventlog_level_error, __FUNCTION__, "missing end quota at line %u", lineno); + return NULL; + } + + *cp = '\0'; + cp = str_skip_space(cp + 1); + if (*cp) { + eventlog(eventlog_level_error, __FUNCTION__, "extra characters in value after ending quote at line %u", lineno); + return NULL; + } + } + else { + cp = str_skip_word(str); + if (*cp) { + *cp = '\0'; + cp = str_skip_space(cp + 1); + if (*cp) { + eventlog(eventlog_level_error, __FUNCTION__, "extra characters after the value at line %u", lineno); + return NULL; + } + } + } + + //if (!find && std::strcmp(find, directive) == 0) + // return str_skip_space(str); + //else + // return NULL; + + icon_var->key = xstrdup(directive); + icon_var->value = xstrdup(str_skip_space(str)); + + return icon_var; + } + + + + + /* Get custom icon by rating for clienttag */ + static t_icon_info * _find_custom_icon(int rating, char * clienttag) + { + t_elem * curr; + t_elem * curr_icon; + t_iconset_info * iconset; + t_icon_info * icon; + + if (icon_head) { + LIST_TRAVERSE(icon_head, curr) + { + if (!(iconset = (t_iconset_info*)elem_get_data(curr))) + { + eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); + continue; + } + + // find a needed tag + if (std::strcmp(iconset->clienttag, clienttag) != 0) + continue; + + // iterate all icons for the tag + LIST_TRAVERSE(iconset->icon_info, curr_icon) + { + if (!(icon = (t_icon_info*)elem_get_data(curr_icon))) + { + eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); + continue; + } + + if (rating >= icon->rating) + return icon; + } + // if nothing found then return last icon + return icon; + } + } + return NULL; + } + + + /* Get attr_key for clienttag */ + static char * _find_attr_key(char * clienttag) + { + t_elem * curr; + t_elem * curr_icon; + t_iconset_info * iconset; + + if (icon_head) { + LIST_TRAVERSE(icon_head, curr) + { + if (!(iconset = (t_iconset_info*)elem_get_data(curr))) + { + eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); + continue; + } + + // find a needed tag + if (std::strcmp(iconset->clienttag, clienttag) == 0) + return iconset->attr_key; + } + } + return NULL; + } + + + + + } +} diff --git a/src/bnetd/icons.h b/src/bnetd/icons.h new file mode 100644 index 000000000..2812b03b3 --- /dev/null +++ b/src/bnetd/icons.h @@ -0,0 +1,80 @@ +/* +* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef INCLUDED_ICONS_TYPES +#define INCLUDED_ICONS_TYPES + +namespace pvpgn +{ + + namespace bnetd + { + + typedef struct + { + unsigned int rating; // rating or something else + char * rank; // any string value + char * icon_code; // icon code + } t_icon_info; + + typedef struct + { + char * clienttag; + char * attr_key; + t_list * icon_info; // list of t_icon_info + + t_list * vars; // list of t_icon_var_info + const char * stats; + } t_iconset_info; + + typedef struct + { + char * key; + char * value; + } t_icon_var_info; + } + +} + +#endif + +#ifndef JUST_NEED_TYPES +#ifndef INCLUDED_ICONS_PROTOS +#define INCLUDED_ICONS_PROTOS + +#define JUST_NEED_TYPES +# include "account.h" +#undef JUST_NEED_TYPES + +namespace pvpgn +{ + + namespace bnetd + { + extern int prefs_get_custom_icons(); + extern t_icon_info * get_custom_icon(t_account * account, t_clienttag clienttag); + extern const char * get_custom_stats_text(t_account * account, t_clienttag clienttag); + + + extern int customicons_load(char const * filename); + extern int customicons_unload(void); + + } + +} + +/*****/ +#endif +#endif diff --git a/src/bnetd/main.cpp b/src/bnetd/main.cpp index c48c4306a..ab475ee56 100644 --- a/src/bnetd/main.cpp +++ b/src/bnetd/main.cpp @@ -81,6 +81,7 @@ #include "command_groups.h" #include "alias_command.h" #include "tournament.h" +#include "icons.h" #include "anongame_infos.h" #include "anongame_wol.h" #include "clan.h" @@ -294,6 +295,7 @@ char * write_to_pidfile(void) return pidfile; } +/* Initialize config files */ int pre_server_startup(void) { pvpgn_greeting(); @@ -364,6 +366,7 @@ int pre_server_startup(void) if (trans_load(prefs_get_transfile(), TRANS_BNETD) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load trans list"); tournament_init(prefs_get_tournament_file()); + customicons_load(prefs_get_customicons_file()); anongame_infos_load(prefs_get_anongame_infos_file()); anongame_wol_matchlist_create(); clanlist_load(); diff --git a/src/bnetd/prefs.cpp b/src/bnetd/prefs.cpp index eaafae98a..0b021bcdd 100644 --- a/src/bnetd/prefs.cpp +++ b/src/bnetd/prefs.cpp @@ -143,6 +143,7 @@ namespace pvpgn unsigned int account_force_username; char const * command_groups_file; char const * tournament_file; + char const * customicons_file; char const * aliasfile; char const * anongame_infos_file; unsigned int max_conns_per_IP; @@ -586,6 +587,10 @@ namespace pvpgn static const char *conf_get_tournament_file(void); static int conf_setdef_tournament_file(void); + static int conf_set_customicons_file(const char *valstr); + static const char *conf_get_customicons_file(void); + static int conf_setdef_customicons_file(void); + static int conf_set_aliasfile(const char *valstr); static const char *conf_get_aliasfile(void); static int conf_setdef_aliasfile(void); @@ -800,6 +805,7 @@ namespace pvpgn { "account_force_username", conf_set_account_force_username, conf_get_account_force_username, conf_setdef_account_force_username }, { "command_groups_file", conf_set_command_groups_file, conf_get_command_groups_file, conf_setdef_command_groups_file }, { "tournament_file", conf_set_tournament_file, conf_get_tournament_file, conf_setdef_tournament_file }, + { "customicons_file", conf_set_customicons_file, conf_get_customicons_file, conf_setdef_customicons_file }, { "aliasfile", conf_set_aliasfile, conf_get_aliasfile, conf_setdef_aliasfile }, { "anongame_infos_file", conf_set_anongame_infos_file, conf_get_anongame_infos_file, conf_setdef_anongame_infos_file }, { "max_conns_per_IP", conf_set_max_conns_per_IP, conf_get_max_conns_per_IP, conf_setdef_max_conns_per_IP }, @@ -3039,6 +3045,27 @@ namespace pvpgn } + extern char const * prefs_get_customicons_file(void) + { + return prefs_runtime_config.customicons_file; + } + + static int conf_set_customicons_file(const char *valstr) + { + return conf_set_str(&prefs_runtime_config.customicons_file, valstr, NULL); + } + + static int conf_setdef_customicons_file(void) + { + return conf_set_str(&prefs_runtime_config.customicons_file, NULL, BNETD_CUSTOMICONS_FILE); + } + + static const char* conf_get_customicons_file(void) + { + return prefs_runtime_config.customicons_file; + } + + extern char const * prefs_get_aliasfile(void) { return prefs_runtime_config.aliasfile; diff --git a/src/bnetd/prefs.h b/src/bnetd/prefs.h index 8885d7e6e..551dea14b 100644 --- a/src/bnetd/prefs.h +++ b/src/bnetd/prefs.h @@ -157,6 +157,7 @@ namespace pvpgn extern char const * prefs_get_command_groups_file(void); extern char const * prefs_get_tournament_file(void); + extern char const * prefs_get_customicons_file(void); extern char const * prefs_get_aliasfile(void); extern char const * prefs_get_anongame_infos_file(void); diff --git a/src/bnetd/server.cpp b/src/bnetd/server.cpp index f97feb0bb..4d10e0e82 100644 --- a/src/bnetd/server.cpp +++ b/src/bnetd/server.cpp @@ -83,6 +83,7 @@ #include "command_groups.h" #include "alias_command.h" #include "tournament.h" +#include "icons.h" #include "anongame_infos.h" #include "topic.h" #include "common/setup_after.h" @@ -1409,6 +1410,9 @@ namespace pvpgn tournament_reload(prefs_get_tournament_file()); + customicons_unload(); + customicons_load(prefs_get_customicons_file()); + anongame_infos_unload(); anongame_infos_load(prefs_get_anongame_infos_file()); diff --git a/src/common/setup_before.h b/src/common/setup_before.h index 2a4f2b903..47e05c1a0 100644 --- a/src/common/setup_before.h +++ b/src/common/setup_before.h @@ -153,6 +153,7 @@ const char * const BNETD_SUPPORT_FILE = "conf/supportfile.conf"; const char * const BNETD_COMMAND_GROUPS_FILE = "conf/command_groups.conf"; const char * const BNETD_TOURNAMENT_FILE = "conf/tournament.conf"; +const char * const BNETD_CUSTOMICONS_FILE = "conf/icons.conf"; const char * const BNETD_ALIASFILE = "conf/bnalias.conf"; /* time limit for new member as newer(whom cannot be promoted) in clan, (hrs) */ const unsigned CLAN_NEWER_TIME = 168; diff --git a/src/common/xstring.cpp b/src/common/xstring.cpp index 98ad6beda..aac40fc60 100644 --- a/src/common/xstring.cpp +++ b/src/common/xstring.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "compat/strdup.h" #include "common/xalloc.h" @@ -267,4 +268,51 @@ namespace pvpgn return result; } + + // You must free the result if result is non-NULL. + extern const char *str_replace(char *orig, char *rep, char *with) + { + char *result; // the return string + char *ins; // the next insert point + char *tmp; // varies + int len_rep; // length of rep + int len_with; // length of with + int len_front; // distance between rep and end of last rep + int count; // number of replacements + + if (!orig) + return NULL; + if (!rep) + rep = ""; + len_rep = strlen(rep); + if (!with) + with = ""; + len_with = strlen(with); + + ins = orig; + for (count = 0; tmp = strstr(ins, rep); ++count) { + ins = tmp + len_rep; + } + + // first time through the loop, all the variable are set correctly + // from here on, + // tmp points to the end of the result string + // ins points to the next occurrence of rep in orig + // orig points to the remainder of orig after "end of rep" + tmp = result = (char*)malloc(strlen(orig) + (len_with - len_rep) * count + 1); + + if (!result) + return NULL; + + while (count--) { + ins = strstr(orig, rep); + len_front = ins - orig; + tmp = strncpy(tmp, orig, len_front) + len_front; + tmp = strcpy(tmp, with) + len_with; + orig += len_front + len_rep; // move to next "end of rep" + } + strcpy(tmp, orig); + return result; + } + } diff --git a/src/common/xstring.h b/src/common/xstring.h index b5346f1dd..d3d2be448 100644 --- a/src/common/xstring.h +++ b/src/common/xstring.h @@ -28,6 +28,8 @@ namespace pvpgn extern char * * strtoargv(char const * str, unsigned int * count); extern char * arraytostr(char * * array, char const * delim, int count); extern char * str_strip_affix(char * str, char const * affix); + extern const char *str_replace(char *orig, char *rep, char *with); + }