Advertisement
Guest User

config.cpp

a guest
Apr 15th, 2016
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 29.74 KB | None | 0 0
  1. #include "stdafx.h"
  2. #include <sstream>
  3. #ifndef __WIN32__
  4. #include <ifaddrs.h>
  5. #endif
  6.  
  7. #include "constants.h"
  8. #include "utils.h"
  9. #include "log.h"
  10. #include "desc.h"
  11. #include "desc_manager.h"
  12. #include "item_manager.h"
  13. #include "p2p.h"
  14. #include "char.h"
  15. #include "ip_ban.h"
  16. #include "war_map.h"
  17. #include "locale_service.h"
  18. #include "config.h"
  19. #include "dev_log.h"
  20. #include "db.h"
  21. #include "skill_power.h"
  22. #include "check_server.h"
  23.  
  24. using std::string;
  25.  
  26. BYTE    g_bChannel = 0;
  27. WORD    mother_port = 50080;
  28. int     passes_per_sec = 25;
  29. WORD    db_port = 0;
  30. WORD    p2p_port = 50900;
  31. char    db_addr[ADDRESS_MAX_LEN + 1];
  32. int     save_event_second_cycle = passes_per_sec * 120; // 3분
  33. int     ping_event_second_cycle = passes_per_sec * 60;
  34. bool    g_bNoMoreClient = false;
  35. bool    g_bNoRegen = false;
  36. bool    g_bNoPasspod = false;
  37.  
  38. // TRAFFIC_PROFILER
  39. bool        g_bTrafficProfileOn = false;
  40. DWORD       g_dwTrafficProfileFlushCycle = 3600;
  41. // END_OF_TRAFFIC_PROFILER
  42.  
  43. int         test_server = 0;
  44. int         speed_server = 0;
  45. #ifdef __AUCTION__
  46. int         auction_server = 0;
  47. #endif
  48. bool        distribution_test_server = false;
  49. bool        china_event_server = false;
  50. bool        guild_mark_server = true;
  51. BYTE        guild_mark_min_level = 3;
  52. bool        no_wander = false;
  53. int     g_iUserLimit = 32768;
  54.  
  55. char        g_szPublicIP[16] = "0";
  56. char        g_szInternalIP[16] = "0";
  57. bool        g_bSkillDisable = false;
  58. int         g_iFullUserCount = 1200;
  59. int         g_iBusyUserCount = 650;
  60. //Canada
  61. //int           g_iFullUserCount = 600;
  62. //int           g_iBusyUserCount = 350;
  63. //Brazil
  64. //int           g_iFullUserCount = 650;
  65. //int           g_iBusyUserCount = 450;
  66. bool        g_bEmpireWhisper = true;
  67. BYTE        g_bAuthServer = false;
  68.  
  69. bool        g_bCheckClientVersion = true;
  70. string  g_stClientVersion = "1215955205";
  71.  
  72. BYTE        g_bBilling = false;
  73.  
  74. string  g_stAuthMasterIP;
  75. WORD        g_wAuthMasterPort = 0;
  76.  
  77. static std::set<DWORD> s_set_dwFileCRC;
  78. static std::set<DWORD> s_set_dwProcessCRC;
  79.  
  80. string g_stHostname = "";
  81. string g_table_postfix = "";
  82.  
  83. string g_stQuestDir = "./quest";
  84. //string g_stQuestObjectDir = "./quest/object";
  85. string g_stDefaultQuestObjectDir = "./quest/object";
  86. std::set<string> g_setQuestObjectDir;
  87.  
  88. std::vector<std::string>    g_stAdminPageIP;
  89. std::string g_stAdminPagePassword = "SHOWMETHEMONEY";
  90.  
  91. string g_stBlockDate = "30000705";
  92.  
  93. extern string g_stLocale;
  94.  
  95. char    teen_addr[ADDRESS_MAX_LEN + 1] = {0};
  96. WORD    teen_port   = 0;
  97.  
  98. int SPEEDHACK_LIMIT_COUNT   = 50;
  99. int SPEEDHACK_LIMIT_BONUS   = 80;
  100. int g_iSyncHackLimitCount = 10;
  101.  
  102. //시야 = VIEW_RANGE + VIEW_BONUS_RANGE
  103. //VIEW_BONUSE_RANGE : 클라이언트와 시야 처리에서너무 딱 떨어질경우 문제가 발생할수있어 500CM의 여분을 항상준다.
  104. int VIEW_RANGE = 5000;
  105. int VIEW_BONUS_RANGE = 500;
  106.  
  107. int g_server_id = 0;
  108. string g_strWebMallURL = "www.metin2.de";
  109.  
  110. unsigned int g_uiSpamBlockDuration = 60 * 15; // 기본 15분
  111. unsigned int g_uiSpamBlockScore = 100; // 기본 100점
  112. unsigned int g_uiSpamReloadCycle = 60 * 10; // 기본 10분
  113.  
  114. bool        g_bCheckMultiHack = true;
  115.  
  116. int         g_iSpamBlockMaxLevel = 10;
  117.  
  118. void        LoadStateUserCount();
  119. void        LoadValidCRCList();
  120. bool        LoadClientVersion();
  121. bool            g_protectNormalPlayer   = false;        // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
  122. bool            g_noticeBattleZone      = false;        // 중립지대에 입장하면 안내메세지를 알려줌
  123.  
  124. bool        isHackShieldEnable = false;
  125. int         HackShield_FirstCheckWaitTime = passes_per_sec * 30;
  126. int         HackShield_CheckCycleTime = passes_per_sec * 180;
  127.  
  128. bool        bXTrapEnabled = false;
  129.  
  130. int gPlayerMaxLevel = 99;
  131. char g_szIPExterne[16] = "0";
  132. bool g_BlockCharCreation = false;
  133.  
  134.  
  135. //OPENID
  136. int     openid_server = 0;
  137. char    openid_host[256];
  138. char    openid_uri[256];
  139.  
  140. bool is_string_true(const char * string)
  141. {
  142.     bool    result = 0;
  143.     if (isnhdigit(*string))
  144.     {
  145.         str_to_number(result, string);
  146.         return result > 0 ? true : false;
  147.     }
  148.     else if (LOWER(*string) == 't')
  149.         return true;
  150.     else
  151.         return false;
  152. }
  153.  
  154. static std::set<int> s_set_map_allows;
  155.  
  156. bool map_allow_find(int index)
  157. {
  158.     if (g_bAuthServer)
  159.         return false;
  160.  
  161.     if (s_set_map_allows.find(index) == s_set_map_allows.end())
  162.         return false;
  163.  
  164.     return true;
  165. }
  166.  
  167. void map_allow_log()
  168. {
  169.     std::set<int>::iterator i;
  170.  
  171.     for (i = s_set_map_allows.begin(); i != s_set_map_allows.end(); ++i)
  172.         sys_log(0, "MAP_ALLOW: %d", *i);
  173. }
  174.  
  175. void map_allow_add(int index)
  176. {
  177.     if (map_allow_find(index) == true)
  178.     {
  179.         fprintf(stdout, "!!! FATAL ERROR !!! multiple MAP_ALLOW setting!!\n");
  180.         exit(1);
  181.     }
  182.  
  183.     fprintf(stdout, "MAP ALLOW %d\n", index);
  184.     s_set_map_allows.insert(index);
  185. }
  186.  
  187. void map_allow_copy(long * pl, int size)
  188. {
  189.     int iCount = 0;
  190.     std::set<int>::iterator it = s_set_map_allows.begin();
  191.  
  192.     while (it != s_set_map_allows.end())
  193.     {
  194.         int i = *(it++);
  195.         *(pl++) = i;
  196.  
  197.         if (++iCount > size)
  198.             break;
  199.     }
  200. }
  201.  
  202. static void FN_add_adminpageIP(char *line)
  203. {
  204.     char    *last;
  205.     const char *delim = " \t\r\n";
  206.     char *v = strtok_r(line, delim, &last);
  207.  
  208.     while (v)
  209.     {
  210.         g_stAdminPageIP.push_back(v);
  211.         v = strtok_r(NULL, delim, &last);
  212.     }
  213. }
  214.  
  215. static void FN_log_adminpage()
  216. {
  217.     itertype(g_stAdminPageIP) iter = g_stAdminPageIP.begin();
  218.  
  219.     while (iter != g_stAdminPageIP.end())
  220.     {
  221.         dev_log(LOG_DEB0, "ADMIN_PAGE_IP = %s", (*iter).c_str());
  222.         ++iter;
  223.     }
  224.  
  225.     dev_log(LOG_DEB0, "ADMIN_PAGE_PASSWORD = %s", g_stAdminPagePassword.c_str());
  226. }
  227.  
  228.  
  229. bool GetIPInfo()
  230. {
  231. #ifndef __WIN32__
  232.     struct ifaddrs* ifaddrp = NULL;
  233.  
  234.     if (0 != getifaddrs(&ifaddrp))
  235.         return false;
  236.  
  237.     for( struct ifaddrs* ifap=ifaddrp ; NULL != ifap ; ifap = ifap->ifa_next )
  238.     {
  239.         struct sockaddr_in * sai = (struct sockaddr_in *) ifap->ifa_addr;
  240.  
  241.         if (!ifap->ifa_netmask ||  // ignore if no netmask
  242.                 sai->sin_addr.s_addr == 0 || // ignore if address is 0.0.0.0
  243.                 sai->sin_addr.s_addr == 16777343) // ignore if address is 127.0.0.1
  244.             continue;
  245. #else
  246.     WSADATA wsa_data;
  247.     char host_name[100];
  248.     HOSTENT* host_ent;
  249.     int n = 0;
  250.  
  251.     if (WSAStartup(0x0101, &wsa_data)) {
  252.         return false;
  253.     }
  254.  
  255.     gethostname(host_name, sizeof(host_name));
  256.     host_ent = gethostbyname(host_name);
  257.     if (host_ent == NULL) {
  258.         return false;
  259.     }
  260.     for ( ; host_ent->h_addr_list[n] != NULL; ++n) {
  261.         struct sockaddr_in addr;
  262.         struct sockaddr_in* sai = &addr;
  263.         memcpy(&sai->sin_addr.s_addr, host_ent->h_addr_list[n], host_ent->h_length);
  264. #endif
  265.  
  266.         char * netip = inet_ntoa(sai->sin_addr);
  267.  
  268.         if (!strncmp(netip, "999.999", 7)) // ignore if address is starting with 192
  269.         {
  270.             strlcpy(g_szInternalIP, netip, sizeof(g_szInternalIP));
  271. #ifndef __WIN32__
  272.             fprintf(stderr, "INTERNAL_IP: %s interface %s\n", netip, ifap->ifa_name);
  273. #else
  274.             fprintf(stderr, "INTERNAL_IP: %s\n", netip);
  275. #endif
  276.         }
  277.         else if (!strncmp(netip, "10.", 3))
  278.         {
  279.             strlcpy(g_szInternalIP, netip, sizeof(g_szInternalIP));
  280. #ifndef __WIN32__
  281.             fprintf(stderr, "INTERNAL_IP: %s interface %s\n", netip, ifap->ifa_name);
  282. #else
  283.             fprintf(stderr, "INTERNAL_IP: %s\n", netip);
  284. #endif
  285.         }
  286.         else if (g_szPublicIP[0] == '0')
  287.         {
  288.             strlcpy(g_szPublicIP, g_szIPExterne, sizeof(g_szPublicIP));
  289. #ifndef __WIN32__
  290.             fprintf(stderr, "PUBLIC_IP: %s interface %s\n", g_szIPExterne, ifap->ifa_name);
  291. #else
  292.             fprintf(stderr, "PUBLIC_IP: %s\n", g_szIPExterne);
  293. #endif
  294.         }
  295.     }
  296.  
  297. #ifndef __WIN32__
  298.     freeifaddrs( ifaddrp );
  299. #else
  300.     WSACleanup();
  301. #endif
  302.  
  303.     if (g_szPublicIP[0] != '0')
  304.         return true;
  305.     else
  306.         return false;
  307. }
  308.  
  309. void config_init(const string& st_localeServiceName)
  310. {
  311.     FILE    *fp;
  312.  
  313.     char    buf[256];
  314.     char    token_string[256];
  315.     char    value_string[256];
  316.  
  317.     // LOCALE_SERVICE
  318.     string  st_configFileName;
  319.  
  320.     st_configFileName.reserve(32);
  321.     st_configFileName = "CONFIG";
  322.  
  323.     if (!st_localeServiceName.empty())
  324.     {
  325.         st_configFileName += ".";
  326.         st_configFileName += st_localeServiceName;
  327.     }
  328.     // END_OF_LOCALE_SERVICE
  329.  
  330.     if (!(fp = fopen(st_configFileName.c_str(), "r")))
  331.     {
  332.         fprintf(stderr, "Can not open [%s]\n", st_configFileName.c_str());
  333.         exit(1);
  334.     }
  335.  
  336.     if (!GetIPInfo())
  337.     {
  338.         strlcpy(g_szPublicIP, g_szIPExterne, sizeof(g_szPublicIP));
  339.     }
  340.  
  341.     char db_host[2][64], db_user[2][64], db_pwd[2][64], db_db[2][64];
  342.     // ... 아... db_port는 이미 있는데... 네이밍 어찌해야함...
  343.     int mysql_db_port[2];
  344.  
  345.     for (int n = 0; n < 2; ++n)
  346.     {
  347.         *db_host[n] = '\0';
  348.         *db_user[n] = '\0';
  349.         *db_pwd[n]= '\0';
  350.         *db_db[n]= '\0';
  351.         mysql_db_port[n] = 0;
  352.     }
  353.  
  354.     char log_host[64], log_user[64], log_pwd[64], log_db[64];
  355.     int log_port = 0;
  356.  
  357.     *log_host = '\0';
  358.     *log_user = '\0';
  359.     *log_pwd = '\0';
  360.     *log_db = '\0';
  361.  
  362.  
  363.     // DB에서 로케일정보를 세팅하기위해서는 다른 세팅값보다 선행되어서
  364.     // DB정보만 읽어와 로케일 세팅을 한후 다른 세팅을 적용시켜야한다.
  365.     // 이유는 로케일관련된 초기화 루틴이 곳곳에 존재하기 때문.
  366.  
  367.     bool isCommonSQL = false;  
  368.     bool isPlayerSQL = false;
  369.  
  370.     FILE* fpOnlyForDB;
  371.  
  372.     if (!(fpOnlyForDB = fopen(st_configFileName.c_str(), "r")))
  373.     {
  374.         fprintf(stderr, "Can not open [%s]\n", st_configFileName.c_str());
  375.         exit(1);
  376.     }
  377.  
  378.     while (fgets(buf, 256, fpOnlyForDB))
  379.     {
  380.         parse_token(buf, token_string, value_string);
  381.  
  382.         TOKEN("BLOCK_LOGIN")
  383.         {
  384.             g_stBlockDate = value_string;
  385.         }
  386.  
  387.         TOKEN("adminpage_ip")
  388.         {
  389.             FN_add_adminpageIP(value_string);
  390.             //g_stAdminPageIP[0] = value_string;
  391.         }
  392.  
  393.         TOKEN("adminpage_ip1")
  394.         {
  395.             FN_add_adminpageIP(value_string);
  396.             //g_stAdminPageIP[0] = value_string;
  397.         }
  398.  
  399.         TOKEN("adminpage_ip2")
  400.         {
  401.             FN_add_adminpageIP(value_string);
  402.             //g_stAdminPageIP[1] = value_string;
  403.         }
  404.  
  405.         TOKEN("adminpage_ip3")
  406.         {
  407.             FN_add_adminpageIP(value_string);
  408.             //g_stAdminPageIP[2] = value_string;
  409.         }
  410.  
  411.         TOKEN("adminpage_password")
  412.         {
  413.             g_stAdminPagePassword = value_string;
  414.         }
  415.  
  416.         TOKEN("hostname")
  417.         {
  418.             g_stHostname = value_string;
  419.             fprintf(stdout, "HOSTNAME: %s\n", g_stHostname.c_str());
  420.             continue;
  421.         }
  422.  
  423.         TOKEN("channel")
  424.         {
  425.             str_to_number(g_bChannel, value_string);
  426.             continue;
  427.         }
  428.  
  429.         TOKEN("player_sql")
  430.         {
  431.             const char * line = two_arguments(value_string, db_host[0], sizeof(db_host[0]), db_user[0], sizeof(db_user[0]));
  432.             line = two_arguments(line, db_pwd[0], sizeof(db_pwd[0]), db_db[0], sizeof(db_db[0]));
  433.  
  434.             if (NULL != line[0])
  435.             {
  436.                 char buf[256];
  437.                 one_argument(line, buf, sizeof(buf));
  438.                 str_to_number(mysql_db_port[0], buf);
  439.             }
  440.  
  441.             if (!*db_host[0] || !*db_user[0] || !*db_pwd[0] || !*db_db[0])
  442.             {
  443.                 fprintf(stderr, "PLAYER_SQL syntax: logsql <host user password db>\n");
  444.                 exit(1);
  445.             }
  446.  
  447.             char buf[1024];
  448.             snprintf(buf, sizeof(buf), "PLAYER_SQL: %s %s %s %s %d", db_host[0], db_user[0], db_pwd[0], db_db[0], mysql_db_port[0]);
  449.             isPlayerSQL = true;
  450.             continue;
  451.         }
  452.  
  453.         TOKEN("common_sql")
  454.         {
  455.             const char * line = two_arguments(value_string, db_host[1], sizeof(db_host[1]), db_user[1], sizeof(db_user[1]));
  456.             line = two_arguments(line, db_pwd[1], sizeof(db_pwd[1]), db_db[1], sizeof(db_db[1]));
  457.  
  458.             if (NULL != line[0])
  459.             {
  460.                 char buf[256];
  461.                 one_argument(line, buf, sizeof(buf));
  462.                 str_to_number(mysql_db_port[1], buf);
  463.             }
  464.  
  465.             if (!*db_host[1] || !*db_user[1] || !*db_pwd[1] || !*db_db[1])
  466.             {
  467.                 fprintf(stderr, "COMMON_SQL syntax: logsql <host user password db>\n");
  468.                 exit(1);
  469.             }
  470.  
  471.             char buf[1024];
  472.             snprintf(buf, sizeof(buf), "COMMON_SQL: %s %s %s %s %d", db_host[1], db_user[1], db_pwd[1], db_db[1], mysql_db_port[1]);
  473.             isCommonSQL = true;
  474.             continue;
  475.         }
  476.  
  477.         TOKEN("log_sql")
  478.         {
  479.             const char * line = two_arguments(value_string, log_host, sizeof(log_host), log_user, sizeof(log_user));
  480.             line = two_arguments(line, log_pwd, sizeof(log_pwd), log_db, sizeof(log_db));
  481.  
  482.             if (NULL != line[0])
  483.             {
  484.                 char buf[256];
  485.                 one_argument(line, buf, sizeof(buf));
  486.                 str_to_number(log_port, buf);
  487.             }
  488.  
  489.             if (!*log_host || !*log_user || !*log_pwd || !*log_db)
  490.             {
  491.                 fprintf(stderr, "LOG_SQL syntax: logsql <host user password db>\n");
  492.                 exit(1);
  493.             }
  494.  
  495.             char buf[1024];
  496.             snprintf(buf, sizeof(buf), "LOG_SQL: %s %s %s %s %d", log_host, log_user, log_pwd, log_db, log_port);
  497.             continue;
  498.         }
  499.  
  500.        
  501.         //OPENID       
  502.         TOKEN("WEB_AUTH")
  503.         {
  504.             const char * line = two_arguments(value_string, openid_host, sizeof(openid_host), openid_uri, sizeof(openid_uri));
  505.  
  506.             if (!*openid_host || !*openid_uri)
  507.             {
  508.                 fprintf(stderr, "WEB_AUTH syntax error (ex: WEB_AUTH <host(metin2.co.kr) uri(/kyw/gameauth.php)>\n");
  509.                 exit(1);
  510.             }
  511.  
  512.             char buf[1024];
  513.             openid_server = 1;
  514.             snprintf(buf, sizeof(buf), "WEB_AUTH: %s %s", openid_host, openid_uri);
  515.             continue;
  516.         }
  517.     }
  518.  
  519.     //처리가 끝났으니 파일을 닫자.
  520.     fclose(fpOnlyForDB);
  521.  
  522.     // CONFIG_SQL_INFO_ERROR
  523.     if (!isCommonSQL)
  524.     {
  525.         puts("LOAD_COMMON_SQL_INFO_FAILURE:");
  526.         puts("");
  527.         puts("CONFIG:");
  528.         puts("------------------------------------------------");
  529.         puts("COMMON_SQL: HOST USER PASSWORD DATABASE");
  530.         puts("");
  531.         exit(1);
  532.     }
  533.  
  534.     if (!isPlayerSQL)
  535.     {
  536.         puts("LOAD_PLAYER_SQL_INFO_FAILURE:");
  537.         puts("");
  538.         puts("CONFIG:");
  539.         puts("------------------------------------------------");
  540.         puts("PLAYER_SQL: HOST USER PASSWORD DATABASE");
  541.         puts("");
  542.         exit(1);
  543.     }
  544.  
  545.     // Common DB 가 Locale 정보를 가지고 있기 때문에 가장 먼저 접속해야 한다.
  546.     AccountDB::instance().Connect(db_host[1], mysql_db_port[1], db_user[1], db_pwd[1], db_db[1]);
  547.  
  548.     if (false == AccountDB::instance().IsConnected())
  549.     {
  550.         fprintf(stderr, "cannot start server while no common sql connected\n");
  551.         exit(1);
  552.     }
  553.  
  554.     fprintf(stdout, "CommonSQL connected\n");
  555.  
  556.     // 로케일 정보를 가져오자
  557.     // <경고> 쿼리문에 절대 조건문(WHERE) 달지 마세요. (다른 지역에서 문제가 생길수 있습니다)
  558.     {
  559.         char szQuery[512];
  560.         snprintf(szQuery, sizeof(szQuery), "SELECT mKey, mValue FROM locale");
  561.  
  562.         std::auto_ptr<SQLMsg> pMsg(AccountDB::instance().DirectQuery(szQuery));
  563.  
  564.         if (pMsg->Get()->uiNumRows == 0)
  565.         {
  566.             fprintf(stderr, "COMMON_SQL: DirectQuery failed : %s\n", szQuery);
  567.             exit(1);
  568.         }
  569.  
  570.         MYSQL_ROW row;
  571.  
  572.         while (NULL != (row = mysql_fetch_row(pMsg->Get()->pSQLResult)))
  573.         {
  574.             // 로케일 세팅
  575.             if (strcasecmp(row[0], "LOCALE") == 0)
  576.             {
  577.                 if (LocaleService_Init(row[1]) == false)
  578.                 {
  579.                     fprintf(stderr, "COMMON_SQL: invalid locale key %s\n", row[1]);
  580.                     exit(1);
  581.                 }
  582.             }
  583.         }
  584.     }
  585.  
  586.     // 로케일 정보를 COMMON SQL에 세팅해준다.
  587.     // 참고로 g_stLocale 정보는 LocaleService_Init() 내부에서 세팅된다.
  588.     fprintf(stdout, "Setting DB to locale %s\n", g_stLocale.c_str());
  589.  
  590.     AccountDB::instance().SetLocale(g_stLocale);
  591.  
  592.     AccountDB::instance().ConnectAsync(db_host[1], mysql_db_port[1], db_user[1], db_pwd[1], db_db[1], g_stLocale.c_str());
  593.  
  594.     // Player DB 접속
  595.     DBManager::instance().Connect(db_host[0], mysql_db_port[0], db_user[0], db_pwd[0], db_db[0]);
  596.  
  597.     if (!DBManager::instance().IsConnected())
  598.     {
  599.         fprintf(stderr, "PlayerSQL.ConnectError\n");
  600.         exit(1);
  601.     }
  602.  
  603.     fprintf(stdout, "PlayerSQL connected\n");
  604.  
  605.     if (false == g_bAuthServer) // 인증 서버가 아닐 경우
  606.     {
  607.         // Log DB 접속
  608.         LogManager::instance().Connect(log_host, log_port, log_user, log_pwd, log_db);
  609.  
  610.         if (!LogManager::instance().IsConnected())
  611.         {
  612.             fprintf(stderr, "LogSQL.ConnectError\n");
  613.             exit(1);
  614.         }
  615.  
  616.         fprintf(stdout, "LogSQL connected\n");
  617.  
  618.         LogManager::instance().BootLog(g_stHostname.c_str(), g_bChannel);
  619.     }
  620.  
  621.     // SKILL_POWER_BY_LEVEL
  622.     // 스트링 비교의 문제로 인해서 AccountDB::instance().SetLocale(g_stLocale) 후부터 한다.
  623.     // 물론 국내는 별로 문제가 안된다(해외가 문제)
  624.     {
  625.         char szQuery[256];
  626.         snprintf(szQuery, sizeof(szQuery), "SELECT mValue FROM locale WHERE mKey='SKILL_POWER_BY_LEVEL'");
  627.         std::auto_ptr<SQLMsg> pMsg(AccountDB::instance().DirectQuery(szQuery));
  628.  
  629.         if (pMsg->Get()->uiNumRows == 0)
  630.         {
  631.             fprintf(stderr, "[SKILL_PERCENT] Query failed: %s", szQuery);
  632.             exit(1);
  633.         }
  634.  
  635.         MYSQL_ROW row;
  636.  
  637.         row = mysql_fetch_row(pMsg->Get()->pSQLResult);
  638.  
  639.         const char * p = row[0];
  640.         int cnt = 0;
  641.         char num[128];
  642.         int aiBaseSkillPowerByLevelTable[SKILL_MAX_LEVEL+1];
  643.  
  644.         fprintf(stdout, "SKILL_POWER_BY_LEVEL %s\n", p);
  645.         while (*p != '\0' && cnt < (SKILL_MAX_LEVEL + 1))
  646.         {
  647.             p = one_argument(p, num, sizeof(num));
  648.             aiBaseSkillPowerByLevelTable[cnt++] = atoi(num);
  649.  
  650.             //fprintf(stdout, "%d %d\n", cnt - 1, aiBaseSkillPowerByLevelTable[cnt - 1]);
  651.             if (*p == '\0')
  652.             {
  653.                 if (cnt != (SKILL_MAX_LEVEL + 1))
  654.                 {
  655.                     fprintf(stderr, "[SKILL_PERCENT] locale table has not enough skill information! (count: %d query: %s)", cnt, szQuery);
  656.                     exit(1);
  657.                 }
  658.  
  659.                 fprintf(stdout, "SKILL_POWER_BY_LEVEL: Done! (count %d)\n", cnt);
  660.                 break;
  661.             }
  662.         }
  663.  
  664.         // 종족별 스킬 세팅
  665.         for (int job = 0; job < JOB_MAX_NUM * 2; ++job)
  666.         {
  667.             snprintf(szQuery, sizeof(szQuery), "SELECT mValue from locale where mKey='SKILL_POWER_BY_LEVEL_TYPE%d' ORDER BY CAST(mValue AS unsigned)", job);
  668.             std::auto_ptr<SQLMsg> pMsg(AccountDB::instance().DirectQuery(szQuery));
  669.  
  670.             // 세팅이 안되어있으면 기본테이블을 사용한다.
  671.             if (pMsg->Get()->uiNumRows == 0)
  672.             {
  673.                 CTableBySkill::instance().SetSkillPowerByLevelFromType(job, aiBaseSkillPowerByLevelTable);
  674.                 continue;
  675.             }
  676.  
  677.             row = mysql_fetch_row(pMsg->Get()->pSQLResult);
  678.             cnt = 0;
  679.             p = row[0];
  680.             int aiSkillTable[SKILL_MAX_LEVEL + 1];
  681.  
  682.             fprintf(stdout, "SKILL_POWER_BY_JOB %d %s\n", job, p);
  683.             while (*p != '\0' && cnt < (SKILL_MAX_LEVEL + 1))
  684.             {          
  685.                 p = one_argument(p, num, sizeof(num));
  686.                 aiSkillTable[cnt++] = atoi(num);
  687.  
  688.                 //fprintf(stdout, "%d %d\n", cnt - 1, aiBaseSkillPowerByLevelTable[cnt - 1]);
  689.                 if (*p == '\0')
  690.                 {
  691.                     if (cnt != (SKILL_MAX_LEVEL + 1))
  692.                     {
  693.                         fprintf(stderr, "[SKILL_PERCENT] locale table has not enough skill information! (count: %d query: %s)", cnt, szQuery);
  694.                         exit(1);
  695.                     }
  696.  
  697.                     fprintf(stdout, "SKILL_POWER_BY_JOB: Done! (job: %d count: %d)\n", job, cnt);
  698.                     break;
  699.                 }
  700.             }
  701.  
  702.             CTableBySkill::instance().SetSkillPowerByLevelFromType(job, aiSkillTable);
  703.         }      
  704.     }
  705.     // END_SKILL_POWER_BY_LEVEL
  706.  
  707.     // LOG_KEEP_DAYS_EXTEND
  708.     log_set_expiration_days(2);
  709.     // END_OF_LOG_KEEP_DAYS_EXTEND
  710.  
  711.     while (fgets(buf, 256, fp))
  712.     {
  713.         parse_token(buf, token_string, value_string);
  714.  
  715.         TOKEN("empire_whisper")
  716.         {
  717.             bool b_value = 0;
  718.             str_to_number(b_value, value_string);
  719.             g_bEmpireWhisper = !!b_value;
  720.             continue;
  721.         }
  722.  
  723.         TOKEN("mark_server")
  724.         {
  725.             guild_mark_server = is_string_true(value_string);
  726.             continue;
  727.         }
  728.  
  729.         TOKEN("mark_min_level")
  730.         {
  731.             str_to_number(guild_mark_min_level, value_string);
  732.             guild_mark_min_level = MINMAX(0, guild_mark_min_level, GUILD_MAX_LEVEL);
  733.             continue;
  734.         }
  735.  
  736.         TOKEN("port")
  737.         {
  738.             str_to_number(mother_port, value_string);
  739.             continue;
  740.         }
  741.  
  742.         TOKEN("log_keep_days")
  743.         {
  744.             int i = 0;
  745.             str_to_number(i, value_string);
  746.             log_set_expiration_days(MINMAX(1, i, 90));
  747.             continue;
  748.         }
  749.  
  750.         TOKEN("passes_per_sec")
  751.         {
  752.             str_to_number(passes_per_sec, value_string);
  753.             continue;
  754.         }
  755.  
  756.         TOKEN("p2p_port")
  757.         {
  758.             str_to_number(p2p_port, value_string);
  759.             continue;
  760.         }
  761.  
  762.         TOKEN("db_port")
  763.         {
  764.             str_to_number(db_port, value_string);
  765.             continue;
  766.         }
  767.  
  768.         TOKEN("db_addr")
  769.         {
  770.             strlcpy(db_addr, value_string, sizeof(db_addr));
  771.  
  772.             for (int n =0; n < ADDRESS_MAX_LEN; ++n)
  773.             {
  774.                 if (db_addr[n] == ' ')
  775.                     db_addr[n] = '\0';
  776.             }
  777.  
  778.             continue;
  779.         }
  780.  
  781.         TOKEN("save_event_second_cycle")
  782.         {
  783.             int cycle = 0;
  784.             str_to_number(cycle, value_string);
  785.             save_event_second_cycle = cycle * passes_per_sec;
  786.             continue;
  787.         }
  788.  
  789.         TOKEN("ping_event_second_cycle")
  790.         {
  791.             int cycle = 0;
  792.             str_to_number(cycle, value_string);
  793.             ping_event_second_cycle = cycle * passes_per_sec;
  794.             continue;
  795.         }
  796.  
  797.         TOKEN("table_postfix")
  798.         {
  799.             g_table_postfix = value_string;
  800.             continue;
  801.         }
  802.  
  803.         TOKEN("test_server")
  804.         {
  805.             printf("-----------------------------------------------\n");
  806.             printf("TEST_SERVER\n");
  807.             printf("-----------------------------------------------\n");
  808.             str_to_number(test_server, value_string);
  809.             continue;
  810.         }
  811.  
  812.         TOKEN("speed_server")
  813.         {
  814.             printf("-----------------------------------------------\n");
  815.             printf("SPEED_SERVER\n");
  816.             printf("-----------------------------------------------\n");
  817.             str_to_number(speed_server, value_string);
  818.             continue;
  819.         }
  820. #ifdef __AUCTION__
  821.         TOKEN("auction_server")
  822.         {
  823.             printf("-----------------------------------------------\n");
  824.             printf("AUCTION_SERVER\n");
  825.             printf("-----------------------------------------------\n");
  826.             str_to_number(auction_server, value_string);
  827.             continue;
  828.         }
  829. #endif
  830.         TOKEN("distribution_test_server")
  831.         {
  832.             str_to_number(distribution_test_server, value_string);
  833.             continue;
  834.         }
  835.  
  836.         TOKEN("china_event_server")
  837.         {
  838.             str_to_number(china_event_server, value_string);
  839.             continue;
  840.         }
  841.  
  842.         TOKEN("shutdowned")
  843.         {
  844.             g_bNoMoreClient = true;
  845.             continue;
  846.         }
  847.  
  848.         TOKEN("no_regen")
  849.         {
  850.             g_bNoRegen = true;
  851.             continue;
  852.         }
  853.  
  854.         TOKEN("traffic_profile")
  855.         {
  856.             g_bTrafficProfileOn = true;
  857.             continue;
  858.         }
  859.  
  860.  
  861.         TOKEN("map_allow")
  862.         {
  863.             char * p = value_string;
  864.             string stNum;
  865.  
  866.             for (; *p; p++)
  867.             {  
  868.                 if (isnhspace(*p))
  869.                 {
  870.                     if (stNum.length())
  871.                     {
  872.                         int index = 0;
  873.                         str_to_number(index, stNum.c_str());
  874.                         map_allow_add(index);
  875.                         stNum.clear();
  876.                     }
  877.                 }
  878.                 else
  879.                     stNum += *p;
  880.             }
  881.  
  882.             if (stNum.length())
  883.             {
  884.                 int index = 0;
  885.                 str_to_number(index, stNum.c_str());
  886.                 map_allow_add(index);
  887.             }
  888.  
  889.             continue;
  890.         }
  891.  
  892.         TOKEN("no_wander")
  893.         {
  894.             no_wander = true;
  895.             continue;
  896.         }
  897.  
  898.         TOKEN("user_limit")
  899.         {
  900.             str_to_number(g_iUserLimit, value_string);
  901.             continue;
  902.         }
  903.  
  904.         TOKEN("skill_disable")
  905.         {
  906.             str_to_number(g_bSkillDisable, value_string);
  907.             continue;
  908.         }
  909.  
  910.         TOKEN("auth_server")
  911.         {
  912.             char szIP[32];
  913.             char szPort[32];
  914.  
  915.             two_arguments(value_string, szIP, sizeof(szIP), szPort, sizeof(szPort));
  916.  
  917.             if (!*szIP || (!*szPort && strcasecmp(szIP, "master")))
  918.             {
  919.                 fprintf(stderr, "AUTH_SERVER: syntax error: <ip|master> <port>\n");
  920.                 exit(1);
  921.             }
  922.  
  923.             g_bAuthServer = true;
  924.  
  925.             LoadBanIP("BANIP");
  926.  
  927.             if (!strcasecmp(szIP, "master"))
  928.                 fprintf(stdout, "AUTH_SERVER: I am the master\n");
  929.             else
  930.             {
  931.                 g_stAuthMasterIP = szIP;
  932.                 str_to_number(g_wAuthMasterPort, szPort);
  933.  
  934.                 fprintf(stdout, "AUTH_SERVER: master %s %u\n", g_stAuthMasterIP.c_str(), g_wAuthMasterPort);
  935.             }
  936.             continue;
  937.         }
  938.  
  939.         TOKEN("billing")
  940.         {
  941.             g_bBilling = true;
  942.         }
  943.  
  944.         TOKEN("quest_dir")
  945.         {
  946.             sys_log(0, "QUEST_DIR SETTING : %s", value_string);
  947.             g_stQuestDir = value_string;
  948.         }
  949.  
  950.         TOKEN("quest_object_dir")
  951.         {
  952.             //g_stQuestObjectDir = value_string;
  953.             std::istringstream is(value_string);
  954.             sys_log(0, "QUEST_OBJECT_DIR SETTING : %s", value_string);
  955.             string dir;
  956.             while (!is.eof())
  957.             {
  958.                 is >> dir;
  959.                 if (is.fail())
  960.                     break;
  961.                 g_setQuestObjectDir.insert(dir);
  962.                 sys_log(0, "QUEST_OBJECT_DIR INSERT : %s", dir .c_str());
  963.             }
  964.         }
  965.  
  966.         TOKEN("teen_addr")
  967.         {
  968.             strlcpy(teen_addr, value_string, sizeof(teen_addr));
  969.  
  970.             for (int n =0; n < ADDRESS_MAX_LEN; ++n)
  971.             {
  972.                 if (teen_addr[n] == ' ')
  973.                     teen_addr[n] = '\0';
  974.             }
  975.  
  976.             continue;
  977.         }
  978.  
  979.         TOKEN("teen_port")
  980.         {
  981.             str_to_number(teen_port, value_string);
  982.         }
  983.  
  984.         TOKEN("synchack_limit_count")
  985.         {
  986.             str_to_number(g_iSyncHackLimitCount, value_string);
  987.         }
  988.  
  989.         TOKEN("speedhack_limit_count")
  990.         {
  991.             str_to_number(SPEEDHACK_LIMIT_COUNT, value_string);
  992.         }
  993.  
  994.         TOKEN("speedhack_limit_bonus")
  995.         {
  996.             str_to_number(SPEEDHACK_LIMIT_BONUS, value_string);
  997.         }
  998.  
  999.         TOKEN("server_id")
  1000.         {
  1001.             str_to_number(g_server_id, value_string);
  1002.         }
  1003.  
  1004.         TOKEN("mall_url")
  1005.         {
  1006.             g_strWebMallURL = value_string;
  1007.         }
  1008.  
  1009.         TOKEN("bind_ip")
  1010.         {
  1011.             strlcpy(g_szPublicIP, value_string, sizeof(g_szPublicIP));
  1012.             strlcpy(g_szIPExterne, value_string, sizeof(g_szIPExterne));
  1013.         }
  1014.  
  1015.         TOKEN("view_range")
  1016.         {
  1017.             str_to_number(VIEW_RANGE, value_string);
  1018.         }
  1019.  
  1020.         TOKEN("spam_block_duration")
  1021.         {
  1022.             str_to_number(g_uiSpamBlockDuration, value_string);
  1023.         }
  1024.  
  1025.         TOKEN("spam_block_score")
  1026.         {
  1027.             str_to_number(g_uiSpamBlockScore, value_string);
  1028.             g_uiSpamBlockScore = MAX(1, g_uiSpamBlockScore);
  1029.         }
  1030.  
  1031.         TOKEN("spam_block_reload_cycle")
  1032.         {
  1033.             str_to_number(g_uiSpamReloadCycle, value_string);
  1034.             g_uiSpamReloadCycle = MAX(60, g_uiSpamReloadCycle); // 최소 1분
  1035.         }
  1036.  
  1037.         TOKEN("check_multihack")
  1038.         {
  1039.             str_to_number(g_bCheckMultiHack, value_string);
  1040.         }
  1041.  
  1042.         TOKEN("spam_block_max_level")
  1043.         {
  1044.             str_to_number(g_iSpamBlockMaxLevel, value_string);
  1045.         }
  1046.         TOKEN("protect_normal_player")
  1047.         {
  1048.             str_to_number(g_protectNormalPlayer, value_string);
  1049.         }
  1050.         TOKEN("notice_battle_zone")
  1051.         {
  1052.             str_to_number(g_noticeBattleZone, value_string);
  1053.         }
  1054.  
  1055.         TOKEN("hackshield_enable")
  1056.         {
  1057.             int flag = 0;
  1058.  
  1059.             str_to_number(flag, value_string);
  1060.  
  1061.             //if (1 == flag && LC_IsEurope() )
  1062.             if (1 == flag)
  1063.             {
  1064.                 isHackShieldEnable = true;
  1065.             }
  1066.         }
  1067.  
  1068.         TOKEN("hackshield_first_check_time")
  1069.         {
  1070.             int secs = 30;
  1071.             str_to_number(secs, value_string);
  1072.  
  1073.             HackShield_FirstCheckWaitTime = passes_per_sec * secs;
  1074.         }
  1075.  
  1076.         TOKEN("hackshield_check_cycle_time")
  1077.         {
  1078.             int secs = 180;
  1079.             str_to_number(secs, value_string);
  1080.  
  1081.             HackShield_CheckCycleTime = passes_per_sec * secs;
  1082.         }
  1083.  
  1084.         TOKEN("xtrap_enable")
  1085.         {
  1086.             int flag = 0;
  1087.             str_to_number(flag, value_string);
  1088.  
  1089.             if (1 == flag )
  1090.             {
  1091.                 bXTrapEnabled = true;
  1092.             }
  1093.         }
  1094.  
  1095.         TOKEN("pk_protect_level")
  1096.         {
  1097.             str_to_number(PK_PROTECT_LEVEL, value_string);
  1098.             fprintf(stderr, "PK_PROTECT_LEVEL: %d", PK_PROTECT_LEVEL);
  1099.         }
  1100.  
  1101.         TOKEN("max_level")
  1102.         {
  1103.             str_to_number(gPlayerMaxLevel, value_string);
  1104.  
  1105.             gPlayerMaxLevel = MINMAX(1, gPlayerMaxLevel, PLAYER_MAX_LEVEL_CONST);
  1106.  
  1107.             fprintf(stderr, "PLAYER_MAX_LEVEL: %d\n", gPlayerMaxLevel);
  1108.         }
  1109.  
  1110.         TOKEN("block_char_creation")
  1111.         {
  1112.             int tmp = 0;
  1113.  
  1114.             str_to_number(tmp, value_string);
  1115.  
  1116.             if (0 == tmp)
  1117.                 g_BlockCharCreation = false;
  1118.             else
  1119.                 g_BlockCharCreation = true;
  1120.  
  1121.             continue;
  1122.         }
  1123.  
  1124.         TOKEN("server_key")
  1125.         {
  1126.             CheckServer::AddServerKey(value_string);
  1127.             continue;
  1128.         }
  1129.     }
  1130.  
  1131.     if (g_setQuestObjectDir.empty())
  1132.         g_setQuestObjectDir.insert(g_stDefaultQuestObjectDir);
  1133.  
  1134.     if (0 == db_port)
  1135.     {
  1136.         fprintf(stderr, "DB_PORT not configured\n");
  1137.         exit(1);
  1138.     }
  1139.  
  1140.     if (0 == g_bChannel)
  1141.     {
  1142.         fprintf(stderr, "CHANNEL not configured\n");
  1143.         exit(1);
  1144.     }
  1145.  
  1146.     if (g_stHostname.empty())
  1147.     {
  1148.         fprintf(stderr, "HOSTNAME must be configured.\n");
  1149.         exit(1);
  1150.     }
  1151.  
  1152.     // LOCALE_SERVICE
  1153.     LocaleService_LoadLocaleStringFile();
  1154.     LocaleService_TransferDefaultSetting();
  1155.     LocaleService_LoadEmpireTextConvertTables();
  1156.     // END_OF_LOCALE_SERVICE
  1157.  
  1158.     fclose(fp);
  1159.  
  1160.     if ((fp = fopen("CMD", "r")))
  1161.     {
  1162.         while (fgets(buf, 256, fp))
  1163.         {
  1164.             char cmd[32], levelname[32];
  1165.             int level;
  1166.  
  1167.             two_arguments(buf, cmd, sizeof(cmd), levelname, sizeof(levelname));
  1168.  
  1169.             if (!*cmd || !*levelname)
  1170.             {
  1171.                 fprintf(stderr, "CMD syntax error: <cmd> <DISABLE | LOW_WIZARD | WIZARD | HIGH_WIZARD | GOD>\n");
  1172.                 exit(1);
  1173.             }
  1174.  
  1175.             if (!strcasecmp(levelname, "LOW_WIZARD"))
  1176.                 level = GM_LOW_WIZARD;
  1177.             else if (!strcasecmp(levelname, "WIZARD"))
  1178.                 level = GM_WIZARD;
  1179.             else if (!strcasecmp(levelname, "HIGH_WIZARD"))
  1180.                 level = GM_HIGH_WIZARD;
  1181.             else if (!strcasecmp(levelname, "GOD"))
  1182.                 level = GM_GOD;
  1183.             else if (!strcasecmp(levelname, "IMPLEMENTOR"))
  1184.                 level = GM_IMPLEMENTOR;
  1185.             else if (!strcasecmp(levelname, "DISABLE"))
  1186.                 level = GM_IMPLEMENTOR + 1;
  1187.             else
  1188.             {
  1189.                 fprintf(stderr, "CMD syntax error: <cmd> <DISABLE | LOW_WIZARD | WIZARD | HIGH_WIZARD | GOD>\n");
  1190.                 exit(1);
  1191.             }
  1192.  
  1193.             interpreter_set_privilege(cmd, level);
  1194.         }
  1195.  
  1196.         fclose(fp);
  1197.     }
  1198.  
  1199.     LoadValidCRCList();
  1200.     LoadStateUserCount();
  1201.  
  1202.     CWarMapManager::instance().LoadWarMapInfo(NULL);
  1203.  
  1204.     FN_log_adminpage();
  1205. }
  1206.  
  1207. const char* get_table_postfix()
  1208. {
  1209.     return g_table_postfix.c_str();
  1210. }
  1211.  
  1212. void LoadValidCRCList()
  1213. {
  1214.     s_set_dwProcessCRC.clear();
  1215.     s_set_dwFileCRC.clear();
  1216.  
  1217.     FILE * fp;
  1218.     char buf[256];
  1219.  
  1220.     if ((fp = fopen("CRC", "r")))
  1221.     {
  1222.         while (fgets(buf, 256, fp))
  1223.         {
  1224.             if (!*buf)
  1225.                 continue;
  1226.  
  1227.             DWORD dwValidClientProcessCRC;
  1228.             DWORD dwValidClientFileCRC;
  1229.  
  1230.             sscanf(buf, " %u %u ", &dwValidClientProcessCRC, &dwValidClientFileCRC);
  1231.  
  1232.             s_set_dwProcessCRC.insert(dwValidClientProcessCRC);
  1233.             s_set_dwFileCRC.insert(dwValidClientFileCRC);
  1234.  
  1235.             fprintf(stderr, "CLIENT_CRC: %u %u\n", dwValidClientProcessCRC, dwValidClientFileCRC);
  1236.         }
  1237.  
  1238.         fclose(fp);
  1239.     }
  1240. }
  1241.  
  1242. bool LoadClientVersion()
  1243. {
  1244.     FILE * fp = fopen("VERSION", "r");
  1245.  
  1246.     if (!fp)
  1247.         return false;
  1248.  
  1249.     char buf[256];
  1250.     fgets(buf, 256, fp);
  1251.  
  1252.     char * p = strchr(buf, '\n');
  1253.     if (p) *p = '\0';
  1254.  
  1255.     fprintf(stderr, "VERSION: \"%s\"\n", buf);
  1256.  
  1257.     g_stClientVersion = buf;
  1258.     fclose(fp);
  1259.     return true;
  1260. }
  1261.  
  1262. void CheckClientVersion()
  1263. {
  1264.     if (LC_IsEurope())
  1265.     {
  1266.         g_bCheckClientVersion = true;
  1267.     }
  1268.     else
  1269.     {
  1270.         g_bCheckClientVersion = false;
  1271.     }
  1272.  
  1273.     const DESC_MANAGER::DESC_SET & set = DESC_MANAGER::instance().GetClientSet();
  1274.     DESC_MANAGER::DESC_SET::const_iterator it = set.begin();
  1275.  
  1276.     while (it != set.end())
  1277.     {
  1278.         LPDESC d = *(it++);
  1279.  
  1280.         if (!d->GetCharacter())
  1281.             continue;
  1282.  
  1283.  
  1284.         int version = atoi(g_stClientVersion.c_str());
  1285.         int date    = atoi(d->GetClientVersion() );
  1286.  
  1287.         //if (0 != g_stClientVersion.compare(d->GetClientVersion()) )
  1288.         if (version > date)
  1289.         {
  1290.             d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("클라이언트 버전이 틀려 로그아웃 됩니다. 정상적으로 패치 후 접속하세요."));
  1291.             d->DelayedDisconnect(10);
  1292.         }
  1293.     }
  1294. }
  1295.  
  1296. void LoadStateUserCount()
  1297. {
  1298.     FILE * fp = fopen("state_user_count", "r");
  1299.  
  1300.     if (!fp)
  1301.         return;
  1302.  
  1303.     if (!LC_IsHongKong())
  1304.         fscanf(fp, " %d %d ", &g_iFullUserCount, &g_iBusyUserCount);
  1305.  
  1306.     fclose(fp);
  1307. }
  1308.  
  1309. bool IsValidProcessCRC(DWORD dwCRC)
  1310. {
  1311.     return s_set_dwProcessCRC.find(dwCRC) != s_set_dwProcessCRC.end();
  1312. }
  1313.  
  1314. bool IsValidFileCRC(DWORD dwCRC)
  1315. {
  1316.     return s_set_dwFileCRC.find(dwCRC) != s_set_dwFileCRC.end();
  1317. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement