LoginDatabase.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. */
  6. #include "../common/debug.h"
  7. #include <iostream>
  8. #include <sstream>
  9. #include <iomanip>
  10. using namespace std;
  11. #ifdef WIN32
  12. #define WIN32_LEAN_AND_MEAN
  13. #include <windows.h>
  14. #define snprintf _snprintf
  15. #define strncasecmp _strnicmp
  16. #define strcasecmp _stricmp
  17. #else
  18. #include "../common/unix.h"
  19. #include <netinet/in.h>
  20. #endif
  21. #include "../common/Log.h"
  22. #include "../common/DatabaseNew.h"
  23. #include "LoginDatabase.h"
  24. #include "LoginAccount.h"
  25. #include "../common/MiscFunctions.h"
  26. #include "../common/packet_functions.h"
  27. #include "../common/packet_dump.h"
  28. #include "LWorld.h"
  29. extern LoginDatabase database;
  30. extern LWorldList world_list;
  31. bool LoginDatabase::ConnectNewDatabase() {
  32. return dbLogin.Connect();
  33. }
  34. void LoginDatabase::RemoveDeletedCharacterData()
  35. {
  36. dbLogin.Query("DELETE FROM login_char_colors WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)");
  37. dbLogin.Query("DELETE FROM login_equipment WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)");
  38. }
  39. void LoginDatabase::SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet){
  40. if(packet){
  41. Query query;
  42. MYSQL_RES* result = 0;
  43. if ( version >= 1212 )
  44. result = query.RunQuery2(Q_SELECT, "SELECT name, description from ls_world_zones where server_id=%i and zone_id=%i", server_id, zone_id);
  45. MYSQL_ROW row;
  46. if(result && (row = mysql_fetch_row(result))) {
  47. if (row[0])
  48. packet->setMediumStringByName("zone", row[0]);
  49. else
  50. packet->setMediumStringByName("zone", " ");
  51. if(row[1])
  52. packet->setMediumStringByName("zonedesc", row[1]);
  53. else
  54. packet->setMediumStringByName("zonedesc", " ");
  55. }
  56. else{
  57. Query query2;
  58. MYSQL_RES* result2 = 0;
  59. if (version < 1212)
  60. result2 = query2.RunQuery2(Q_SELECT, "SELECT file, description from zones where id=%i", zone_id);
  61. else
  62. result2 = query2.RunQuery2(Q_SELECT, "SELECT name, description from zones where id=%i", zone_id);
  63. MYSQL_ROW row2;
  64. if(result2 && (row2 = mysql_fetch_row(result2))) {
  65. if (version != 546 && version < 1212)
  66. {
  67. if (row2[0])
  68. {
  69. int len = strlen(row2[0]);
  70. char* zoneName = new char[len + 2];
  71. strncpy(zoneName, row2[0], len);
  72. zoneName[len] = 0x2E;
  73. zoneName[len + 1] = 0x30;
  74. packet->setMediumStringByName("zone", zoneName);
  75. safe_delete_array(zoneName);
  76. }
  77. else
  78. packet->setMediumStringByName("zone", ".0");
  79. }
  80. else
  81. {
  82. if (row2[0])
  83. packet->setMediumStringByName("zone", row2[0]);
  84. else
  85. packet->setMediumStringByName("zone", " ");
  86. }
  87. if(row2[1])
  88. packet->setMediumStringByName("zonedesc", row2[1]);
  89. else
  90. packet->setMediumStringByName("zonedesc", " ");
  91. }
  92. }
  93. packet->setMediumStringByName("zonename2"," ");
  94. }
  95. }
  96. string LoginDatabase::GetZoneDescription(char* name){
  97. string ret;
  98. Query query;
  99. query.escaped_name = getEscapeString(name);
  100. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT description from zones where file=substring_index('%s', '.', 1)", query.escaped_name);
  101. MYSQL_ROW row;
  102. if((row = mysql_fetch_row(result))) {
  103. ret = string(row[0]);
  104. }
  105. return ret;
  106. }
  107. int32 LoginDatabase::GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id)
  108. {
  109. int32 ret;
  110. Query query;
  111. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id FROM login_characters WHERE server_id = %u AND char_id = %u AND deleted = 0 LIMIT 0,1", server_id, char_id);
  112. MYSQL_ROW row;
  113. if((row = mysql_fetch_row(result))) {
  114. ret = atoi(row[0]);
  115. }
  116. return ret;
  117. }
  118. void LoginDatabase::SetServerEquipmentAppearances(int32 server_id, map<int32, LoginEquipmentUpdate> equip_updates)
  119. {
  120. if(equip_updates.size() > 0)
  121. {
  122. LogWrite(LOGIN__DEBUG, 0, "Login", "Saving appearance info from world %u...", server_id);
  123. map<int32, LoginEquipmentUpdate>::iterator equip_itr;
  124. stringstream ss;
  125. ss << "replace into login_equipment (login_characters_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue, slot) values";
  126. int count=0;
  127. int32 char_id = 0;
  128. for(equip_itr = equip_updates.begin(); equip_itr != equip_updates.end(); equip_itr++)
  129. {
  130. char_id = GetLoginCharacterIDFromWorldCharID(server_id, (int32)equip_itr->second.world_char_id);
  131. if( char_id == 0 ) // invalid character/world match
  132. continue;
  133. LogWrite(LOGIN__DEBUG, 5, "Login", "--Processing character %u, slot %i", char_id, (int32)equip_itr->second.slot);
  134. if(count > 0)
  135. ss << ", ";
  136. ss << "(" << char_id << ", ";
  137. ss << (int32)equip_itr->second.equip_type << ", ";
  138. ss << (int32)equip_itr->second.red << ", ";
  139. ss << (int32)equip_itr->second.green << ", ";
  140. ss << (int32)equip_itr->second.blue << ", ";
  141. ss << (int32)equip_itr->second.highlight_red << ", ";
  142. ss << (int32)equip_itr->second.highlight_green << ", ";
  143. ss << (int32)equip_itr->second.highlight_blue << ", ";
  144. ss << (int32)equip_itr->second.slot << ")";
  145. count++;
  146. }
  147. Query query;
  148. query.RunQuery2(ss.str(), Q_REPLACE);
  149. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  150. LogWrite(LOGIN__ERROR, 0, "Login", "Error saving login_equipment data Error: ", query.GetError());
  151. }
  152. }
  153. void LoginDatabase::SetServerZoneDescriptions(int32 server_id, map<int32, LoginZoneUpdate> zone_descriptions){
  154. if(zone_descriptions.size() > 0){
  155. map<int32, LoginZoneUpdate>::iterator zone_itr;
  156. string query_string = "replace into ls_world_zones (server_id, zone_id, name, description) values";
  157. int count=0;
  158. char server_id_str[12] = {0};
  159. sprintf(server_id_str, "%i", server_id);
  160. for(zone_itr = zone_descriptions.begin(); zone_itr != zone_descriptions.end(); zone_itr++, count++){
  161. char zone_id_str[12] = {0};
  162. sprintf(zone_id_str, "%i", zone_itr->first);
  163. if(count > 0)
  164. query_string.append(", ");
  165. query_string.append("(").append(server_id_str).append(",");
  166. query_string.append(zone_id_str).append(",");
  167. query_string.append("'").append(getSafeEscapeString(zone_itr->second.name.c_str()).c_str()).append("', '");
  168. query_string.append(getSafeEscapeString(zone_itr->second.description.c_str()).c_str()).append("')");
  169. }
  170. Query query;
  171. query.RunQuery2(query_string, Q_REPLACE);
  172. }
  173. }
  174. //this is really just for the version that doesn't send the server id in its play request
  175. int32 LoginDatabase::GetServer(int32 accountID, int32 charID, string name) {
  176. int32 id = 0;
  177. Query query;
  178. MYSQL_ROW row;
  179. query.escaped_name = getEscapeString(name.c_str());
  180. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT server_id from login_characters where account_id=%i and char_id=%i and name='%s'", accountID, charID, query.escaped_name);
  181. if (result && mysql_num_rows(result) == 1) {
  182. row = mysql_fetch_row(result);
  183. id = atoi(row[0]);
  184. }
  185. return id;
  186. }
  187. void LoginDatabase::LoadCharacters(LoginAccount* acct, int16 version){
  188. if(acct != NULL)
  189. acct->flushCharacters ( );
  190. Query query;
  191. Query query2;
  192. int32 id = 0;
  193. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lc.char_id, lc.server_id, lc.name, lc.race, lc.class, lc.gender, lc.deity, lc.body_size, lc.body_age, lc.current_zone_id, lc.level, lc.soga_wing_type, lc.soga_chest_type, lc.soga_legs_type, lc.soga_hair_type, lc.legs_type, lc.chest_type, lc.wing_type, lc.hair_type, unix_timestamp(lc.created_date), unix_timestamp(lc.last_played), lc.id, lw.name, lc.facial_hair_type, lc.soga_facial_hair_type, lc.soga_model_type, lc.model_type from login_characters lc, login_worldservers lw where lw.id = lc.server_id and lc.account_id=%i and lc.deleted=0",acct->getLoginAccountID());
  194. if(result) {
  195. MYSQL_ROW row;
  196. MYSQL_ROW row2;
  197. MYSQL_ROW row3;
  198. while ((row = mysql_fetch_row(result))) {
  199. CharSelectProfile* player = new CharSelectProfile(version);
  200. id = atoul(row[0]);
  201. //for (int i = 0; i < 10; i++)
  202. // player->packet->setDataByName("hair_type", 0, i);
  203. //player->packet->setDataByName("test23", 413);
  204. //player->packet->setDataByName("test24", 414);
  205. player->packet->setDataByName("charid", id);
  206. player->packet->setDataByName("server_id", atoul(row[1]));
  207. player->packet->setMediumStringByName("name", row[2]);
  208. player->packet->setDataByName("race", atoi(row[3]));
  209. player->packet->setDataByName("class", atoi(row[4]));
  210. player->packet->setDataByName("gender", atoi(row[5]));
  211. player->packet->setDataByName("deity", atoi(row[6]));
  212. player->packet->setDataByName("body_size", atof(row[7]));
  213. player->packet->setDataByName("body_age", atof(row[8]));
  214. SetZoneInformation(atoi(row[1]), atoi(row[9]), version, player->packet);
  215. player->packet->setDataByName("level", atoi(row[10]));
  216. if(atoi(row[11]) > 0)
  217. player->packet->setDataByName("soga_wing_type", atoi(row[11]));
  218. else
  219. player->packet->setDataByName("soga_wing_type", atoi(row[17]));
  220. if(atoi(row[12]) > 0)
  221. player->packet->setDataByName("soga_chest_type", atoi(row[12]));
  222. else
  223. player->packet->setDataByName("soga_chest_type", atoi(row[16]));
  224. if(atoi(row[13]) > 0)
  225. player->packet->setDataByName("soga_legs_type", atoi(row[13]));
  226. else
  227. player->packet->setDataByName("soga_legs_type", atoi(row[15]));
  228. if(atoi(row[14]) > 0)
  229. player->packet->setDataByName("soga_hair_type", atoi(row[14]));
  230. else
  231. player->packet->setDataByName("soga_hair_type", atoi(row[18]));
  232. player->packet->setDataByName("legs_type", atoi(row[15]));
  233. player->packet->setDataByName("chest_type", atoi(row[16]));
  234. player->packet->setDataByName("wing_type", atoi(row[17]));
  235. player->packet->setDataByName("hair_type", atoi(row[18]));
  236. player->packet->setDataByName("created_date", atol(row[19]));
  237. if (row[20])
  238. player->packet->setDataByName("last_played", atol(row[20]));
  239. if(version == 546)
  240. player->packet->setDataByName("version", 11);
  241. else if(version >= 887)
  242. player->packet->setDataByName("version", 6);
  243. else
  244. player->packet->setDataByName("version", 5);
  245. player->packet->setDataByName("account_id", acct->getLoginAccountID());
  246. player->packet->setDataByName("account_id2", acct->getLoginAccountID());
  247. LoadAppearanceData(atol(row[21]), player->packet);
  248. if(row[22])
  249. player->packet->setMediumStringByName("server_name", row[22]);
  250. player->packet->setDataByName("hair_face_type", atoi(row[23]));
  251. if(atoi(row[24]) > 0)
  252. player->packet->setDataByName("soga_hair_face_type", atoi(row[24]));
  253. else
  254. player->packet->setDataByName("soga_hair_face_type", atoi(row[23]));
  255. if(atoi(row[25]) > 0)
  256. player->packet->setDataByName("soga_race_type", atoi(row[25]));
  257. else
  258. player->packet->setDataByName("soga_race_type", atoi(row[26]));
  259. player->packet->setDataByName("race_type", atoi(row[26]));
  260. player->packet->setDataByName("unknown3", 57);
  261. player->packet->setDataByName("unknown4", 56);
  262. player->packet->setDataByName("unknown6", 1, 1); //if not here will not display character
  263. player->packet->setDataByName("unknown8", 15);
  264. player->packet->setDataByName("unknown13", 212);
  265. player->packet->setColorByName("unknown14", 0xFF, 0xFF, 0xFF);
  266. uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00};
  267. for(size_t y=0;y<sizeof(tmp);y++)
  268. player->packet->setDataByName("unknown11", tmp[y], y);
  269. MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",atoi(row[21]));
  270. if(result3){
  271. for(int i=0;(row3 = mysql_fetch_row(result3)) && i<24; i++){
  272. player->packet->setEquipmentByName("equip", atoi(row3[1]), atoi(row3[2]), atoi(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoi(row3[7]), atoi(row3[0]));
  273. }
  274. }
  275. player->packet->setDataByName("mount", 1377);
  276. player->packet->setDataByName("mount_color1", 57);
  277. /*
  278. enum NetAppearance::NetAppearanceFlags
  279. {
  280. NAF_INVISIBLE=1,
  281. NAF_SHOW_HOOD=2
  282. };
  283. */
  284. acct->addCharacter(player);
  285. }
  286. }
  287. else
  288. LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoadCharacters query '%s': %s", query.GetQuery(), query.GetError());
  289. }
  290. void LoginDatabase::CheckCharacterTimeStamps(LoginAccount* acct){
  291. Query query;
  292. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT char_id, unix_timestamp from login_characters where account_id=%i",acct->getLoginAccountID());
  293. if(result && mysql_num_rows(result) > 0) {
  294. MYSQL_ROW row;
  295. ServerPacket* outpack = new ServerPacket(ServerOP_CharTimeStamp, sizeof(CharacterTimeStamp_Struct));
  296. CharacterTimeStamp_Struct* cts = (CharacterTimeStamp_Struct*) outpack->pBuffer;
  297. cts->account_id = acct->getLoginAccountID();
  298. int32 server_id = 0;
  299. LWorld* world_server = 0;
  300. while ((row = mysql_fetch_row(result))) {
  301. server_id = atoi(row[1]);
  302. if(server_id != 0)
  303. world_server = world_list.FindByAccount(server_id, World);
  304. if(world_server) // If the pointer is 0, the world server must be down, we can't do any updates...
  305. {
  306. cts->char_id = atoi(row[0]);
  307. cts->unix_timestamp = atoi(row[1]);
  308. world_server->SendPacket(outpack);
  309. //Reset for next character
  310. world_server = 0;
  311. server_id = 0;
  312. }
  313. }
  314. safe_delete(outpack);
  315. }
  316. }
  317. void LoginDatabase::SaveCharacterFloats(int32 char_id, char* type, float float1, float float2, float float3){
  318. Query query;
  319. string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue, signed_value) values(%i,'%s',%i,%i,%i, 1)");
  320. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, (sint8)(float1*100), (sint8)(float2*100), (sint8)(float3*100));
  321. }
  322. void LoginDatabase::SaveCharacterColors(int32 char_id, char* type, EQ2_Color color){
  323. Query query;
  324. string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue) values(%i,'%s',%i,%i,%i)");
  325. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, color.red, color.green, color.blue);
  326. }
  327. void LoginDatabase::LoadAppearanceData(int32 char_id, PacketStruct* char_select_packet){
  328. Query query;
  329. MYSQL_ROW row;
  330. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, signed_value, red, green, blue from login_char_colors where login_characters_id = %i",char_id);
  331. while((row = mysql_fetch_row(result))){
  332. if(atoi(row[1]) == 0)
  333. char_select_packet->setColorByName(row[0], atoi(row[2]), atoi(row[3]), atoi(row[4]));
  334. else{
  335. if (char_select_packet->GetVersion() <= 283)
  336. {
  337. char_select_packet->setDataByName(row[0], atoi(row[2]) * 2.5, 0);
  338. char_select_packet->setDataByName(row[0], atoi(row[3]) * 2.5, 1);
  339. char_select_packet->setDataByName(row[0], atoi(row[4]) * 2.5, 2);
  340. }
  341. else
  342. {
  343. char_select_packet->setDataByName(row[0], atoi(row[2]), 0);
  344. char_select_packet->setDataByName(row[0], atoi(row[3]), 1);
  345. char_select_packet->setDataByName(row[0], atoi(row[4]), 2);
  346. }
  347. }
  348. }
  349. }
  350. int16 LoginDatabase::GetAppearanceID(string name){
  351. int32 id = 0;
  352. Query query;
  353. MYSQL_ROW row;
  354. query.escaped_name = getEscapeString(name.c_str());
  355. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT appearance_id from appearances where name='%s'", query.escaped_name);
  356. if(result && mysql_num_rows(result) == 1){
  357. row = mysql_fetch_row(result);
  358. id = atoi(row[0]);
  359. }
  360. return id;
  361. }
  362. void LoginDatabase::DeactivateCharID(int32 server_id, int32 char_id, int32 exception_id){
  363. Query query;
  364. query.RunQuery2(Q_UPDATE, "update login_characters set deleted=1 where char_id=%u and server_id=%u and id!=%u",char_id,server_id,exception_id);
  365. }
  366. int32 LoginDatabase::SaveCharacter(PacketStruct* create, LoginAccount* acct, int32 world_charid, int32 client_version){
  367. int32 ret_id = 0;
  368. Query query;
  369. string create_char =
  370. string("Insert into login_characters (account_id, server_id, char_id, name, race, class, gender, deity, body_size, body_age, soga_wing_type, soga_chest_type, soga_legs_type, soga_hair_type, soga_facial_hair_type, legs_type, chest_type, wing_type, hair_type, facial_hair_type, soga_model_type, model_type)"
  371. " values(%i, %i, %i, '%s', %i, %i, %i, %i, %f, %f, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)");
  372. query.RunQuery2(Q_INSERT, create_char.c_str(),
  373. acct->getLoginAccountID(),
  374. create->getType_int32_ByName("server_id"), world_charid,
  375. create->getType_EQ2_16BitString_ByName("name").data.c_str(),
  376. create->getType_int8_ByName("race"),
  377. create->getType_int8_ByName("class"),
  378. create->getType_int8_ByName("gender"),
  379. create->getType_int8_ByName("deity"),
  380. create->getType_float_ByName("body_size"),
  381. create->getType_float_ByName("body_age"),
  382. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_wing_file").data),
  383. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_chest_file").data),
  384. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_legs_file").data),
  385. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_hair_file").data),
  386. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_face_file").data),
  387. GetAppearanceID(create->getType_EQ2_16BitString_ByName("legs_file").data),
  388. GetAppearanceID(create->getType_EQ2_16BitString_ByName("chest_file").data),
  389. GetAppearanceID(create->getType_EQ2_16BitString_ByName("wing_file").data),
  390. GetAppearanceID(create->getType_EQ2_16BitString_ByName("hair_file").data),
  391. GetAppearanceID(create->getType_EQ2_16BitString_ByName("face_file").data),
  392. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_race_file").data),
  393. GetAppearanceID(create->getType_EQ2_16BitString_ByName("race_file").data));
  394. if(query.GetError() && strlen(query.GetError()) > 0){
  395. LogWrite(LOGIN__ERROR, 0, "Login", "Error in SaveCharacter query '%s': %s", query.GetQuery(), query.GetError());
  396. return 0;
  397. }
  398. int32 last_insert_id = query.GetLastInsertedID();
  399. //mark any remaining characters with same id as deleted (creates problems if world deleted their db and started assigning new char ids)
  400. DeactivateCharID(create->getType_int32_ByName("server_id"), world_charid, last_insert_id);
  401. int32 char_id = last_insert_id;
  402. if (client_version <= 546) {
  403. SaveCharacterFloats(char_id, "skin_color", create->getType_float_ByName("skin_color", 0), create->getType_float_ByName("skin_color", 1), create->getType_float_ByName("skin_color", 2));
  404. SaveCharacterFloats(char_id, "eye_color", create->getType_float_ByName("eye_color", 0), create->getType_float_ByName("eye_color", 1), create->getType_float_ByName("eye_color", 2));
  405. SaveCharacterFloats(char_id, "hair_color1", create->getType_float_ByName("hair_color1", 0), create->getType_float_ByName("hair_color1", 1), create->getType_float_ByName("hair_color1", 2));
  406. SaveCharacterFloats(char_id, "hair_color2", create->getType_float_ByName("hair_color2", 0), create->getType_float_ByName("hair_color2", 1), create->getType_float_ByName("hair_color2", 2));
  407. SaveCharacterFloats(char_id, "hair_highlight", create->getType_float_ByName("hair_highlight", 0), create->getType_float_ByName("hair_highlight", 1), create->getType_float_ByName("hair_highlight", 2));
  408. SaveCharacterFloats(char_id, "hair_type_color", create->getType_float_ByName("hair_type_color", 0), create->getType_float_ByName("hair_type_color", 1), create->getType_float_ByName("hair_type_color", 2));
  409. SaveCharacterFloats(char_id, "hair_type_highlight_color", create->getType_float_ByName("hair_type_highlight_color", 0), create->getType_float_ByName("hair_type_highlight_color", 1), create->getType_float_ByName("hair_type_highlight_color", 2));
  410. SaveCharacterFloats(char_id, "hair_type_color", create->getType_float_ByName("hair_type_color", 0), create->getType_float_ByName("hair_type_color", 1), create->getType_float_ByName("hair_type_color", 2));
  411. SaveCharacterFloats(char_id, "hair_type_highlight_color", create->getType_float_ByName("hair_type_highlight_color", 0), create->getType_float_ByName("hair_type_highlight_color", 1), create->getType_float_ByName("hair_type_highlight_color", 2));
  412. SaveCharacterFloats(char_id, "hair_face_color", create->getType_float_ByName("hair_face_color", 0), create->getType_float_ByName("hair_face_color", 1), create->getType_float_ByName("hair_face_color", 2));
  413. SaveCharacterFloats(char_id, "hair_face_highlight_color", create->getType_float_ByName("hair_face_highlight_color", 0), create->getType_float_ByName("hair_face_highlight_color", 1), create->getType_float_ByName("hair_face_highlight_color", 2));
  414. SaveCharacterFloats(char_id, "shirt_color", create->getType_float_ByName("shirt_color", 0), create->getType_float_ByName("shirt_color", 1), create->getType_float_ByName("shirt_color", 2));
  415. SaveCharacterFloats(char_id, "unknown_chest_color", create->getType_float_ByName("unknown_chest_color", 0), create->getType_float_ByName("unknown_chest_color", 1), create->getType_float_ByName("unknown_chest_color", 2));
  416. SaveCharacterFloats(char_id, "pants_color", create->getType_float_ByName("pants_color", 0), create->getType_float_ByName("pants_color", 1), create->getType_float_ByName("pants_color", 2));
  417. SaveCharacterFloats(char_id, "unknown_legs_color", create->getType_float_ByName("unknown_legs_color", 0), create->getType_float_ByName("unknown_legs_color", 1), create->getType_float_ByName("unknown_legs_color", 2));
  418. SaveCharacterFloats(char_id, "unknown9", create->getType_float_ByName("unknown9", 0), create->getType_float_ByName("unknown9", 1), create->getType_float_ByName("unknown9", 2));
  419. }
  420. else {
  421. SaveCharacterColors(char_id, "skin_color", create->getType_EQ2_Color_ByName("skin_color"));
  422. SaveCharacterColors(char_id, "model_color", create->getType_EQ2_Color_ByName("model_color"));
  423. SaveCharacterColors(char_id, "eye_color", create->getType_EQ2_Color_ByName("eye_color"));
  424. SaveCharacterColors(char_id, "hair_color1", create->getType_EQ2_Color_ByName("hair_color1"));
  425. SaveCharacterColors(char_id, "hair_color2", create->getType_EQ2_Color_ByName("hair_color2"));
  426. SaveCharacterColors(char_id, "hair_highlight", create->getType_EQ2_Color_ByName("hair_highlight"));
  427. SaveCharacterColors(char_id, "hair_type_color", create->getType_EQ2_Color_ByName("hair_type_color"));
  428. SaveCharacterColors(char_id, "hair_type_highlight_color", create->getType_EQ2_Color_ByName("hair_type_highlight_color"));
  429. SaveCharacterColors(char_id, "hair_face_color", create->getType_EQ2_Color_ByName("hair_face_color"));
  430. SaveCharacterColors(char_id, "hair_face_highlight_color", create->getType_EQ2_Color_ByName("hair_face_highlight_color"));
  431. SaveCharacterColors(char_id, "wing_color1", create->getType_EQ2_Color_ByName("wing_color1"));
  432. SaveCharacterColors(char_id, "wing_color2", create->getType_EQ2_Color_ByName("wing_color2"));
  433. SaveCharacterColors(char_id, "shirt_color", create->getType_EQ2_Color_ByName("shirt_color"));
  434. SaveCharacterColors(char_id, "unknown_chest_color", create->getType_EQ2_Color_ByName("unknown_chest_color"));
  435. SaveCharacterColors(char_id, "pants_color", create->getType_EQ2_Color_ByName("pants_color"));
  436. SaveCharacterColors(char_id, "unknown_legs_color", create->getType_EQ2_Color_ByName("unknown_legs_color"));
  437. SaveCharacterColors(char_id, "unknown9", create->getType_EQ2_Color_ByName("unknown9"));
  438. SaveCharacterColors(char_id, "soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color"));
  439. SaveCharacterColors(char_id, "soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color"));
  440. SaveCharacterColors(char_id, "soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color"));
  441. SaveCharacterColors(char_id, "soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1"));
  442. SaveCharacterColors(char_id, "soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2"));
  443. SaveCharacterColors(char_id, "soga_hair_highlight", create->getType_EQ2_Color_ByName("soga_hair_highlight"));
  444. SaveCharacterColors(char_id, "soga_hair_type_color", create->getType_EQ2_Color_ByName("soga_hair_type_color"));
  445. SaveCharacterColors(char_id, "soga_hair_type_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_type_highlight_color"));
  446. SaveCharacterColors(char_id, "soga_hair_face_color", create->getType_EQ2_Color_ByName("soga_hair_face_color"));
  447. SaveCharacterColors(char_id, "soga_hair_face_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_face_highlight_color"));
  448. SaveCharacterColors(char_id, "soga_wing_color1", create->getType_EQ2_Color_ByName("soga_wing_color1"));
  449. SaveCharacterColors(char_id, "soga_wing_color2", create->getType_EQ2_Color_ByName("soga_wing_color2"));
  450. SaveCharacterColors(char_id, "soga_shirt_color", create->getType_EQ2_Color_ByName("soga_shirt_color"));
  451. SaveCharacterColors(char_id, "soga_unknown_chest_color", create->getType_EQ2_Color_ByName("soga_unknown_chest_color"));
  452. SaveCharacterColors(char_id, "soga_pants_color", create->getType_EQ2_Color_ByName("soga_pants_color"));
  453. SaveCharacterColors(char_id, "soga_unknown_legs_color", create->getType_EQ2_Color_ByName("soga_unknown_legs_color"));
  454. SaveCharacterColors(char_id, "soga_unknown13", create->getType_EQ2_Color_ByName("soga_unknown13"));
  455. SaveCharacterFloats(char_id, "soga_eye_type", create->getType_float_ByName("soga_eyes2", 0), create->getType_float_ByName("soga_eyes2", 1), create->getType_float_ByName("soga_eyes2", 2));
  456. SaveCharacterFloats(char_id, "soga_ear_type", create->getType_float_ByName("soga_ears", 0), create->getType_float_ByName("soga_ears", 1), create->getType_float_ByName("soga_ears", 2));
  457. SaveCharacterFloats(char_id, "soga_eye_brow_type", create->getType_float_ByName("soga_eye_brows", 0), create->getType_float_ByName("soga_eye_brows", 1), create->getType_float_ByName("soga_eye_brows", 2));
  458. SaveCharacterFloats(char_id, "soga_cheek_type", create->getType_float_ByName("soga_cheeks", 0), create->getType_float_ByName("soga_cheeks", 1), create->getType_float_ByName("soga_cheeks", 2));
  459. SaveCharacterFloats(char_id, "soga_lip_type", create->getType_float_ByName("soga_lips", 0), create->getType_float_ByName("soga_lips", 1), create->getType_float_ByName("soga_lips", 2));
  460. SaveCharacterFloats(char_id, "soga_chin_type", create->getType_float_ByName("soga_chin", 0), create->getType_float_ByName("soga_chin", 1), create->getType_float_ByName("soga_chin", 2));
  461. SaveCharacterFloats(char_id, "soga_nose_type", create->getType_float_ByName("soga_nose", 0), create->getType_float_ByName("soga_nose", 1), create->getType_float_ByName("soga_nose", 2));
  462. }
  463. SaveCharacterFloats(char_id, "eye_type", create->getType_float_ByName("eyes2", 0), create->getType_float_ByName("eyes2", 1), create->getType_float_ByName("eyes2", 2));
  464. SaveCharacterFloats(char_id, "ear_type", create->getType_float_ByName("ears", 0), create->getType_float_ByName("ears", 1), create->getType_float_ByName("ears", 2));
  465. SaveCharacterFloats(char_id, "eye_brow_type", create->getType_float_ByName("eye_brows", 0), create->getType_float_ByName("eye_brows", 1), create->getType_float_ByName("eye_brows", 2));
  466. SaveCharacterFloats(char_id, "cheek_type", create->getType_float_ByName("cheeks", 0), create->getType_float_ByName("cheeks", 1), create->getType_float_ByName("cheeks", 2));
  467. SaveCharacterFloats(char_id, "lip_type", create->getType_float_ByName("lips", 0), create->getType_float_ByName("lips", 1), create->getType_float_ByName("lips", 2));
  468. SaveCharacterFloats(char_id, "chin_type", create->getType_float_ByName("chin", 0), create->getType_float_ByName("chin", 1), create->getType_float_ByName("chin", 2));
  469. SaveCharacterFloats(char_id, "nose_type", create->getType_float_ByName("nose", 0), create->getType_float_ByName("nose", 1), create->getType_float_ByName("nose", 2));
  470. SaveCharacterFloats(char_id, "body_size", create->getType_float_ByName("body_size", 0), 0, 0);
  471. return ret_id;
  472. }
  473. bool LoginDatabase::DeleteCharacter(int32 account_id, int32 character_id, int32 server_id){
  474. Query query;
  475. string delete_char = string("delete from login_characters where char_id=%i and account_id=%i and server_id=%i");
  476. query.RunQuery2(Q_DELETE, delete_char.c_str(),character_id,account_id,server_id);
  477. if(!query.GetAffectedRows())
  478. {
  479. //No error just in case ppl try doing stupid stuff
  480. return false;
  481. }
  482. return true;
  483. }
  484. string LoginDatabase::GetCharacterName(int32 char_id, int32 server_id, int32 account_id){
  485. Query query;
  486. MYSQL_ROW row;
  487. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_characters where char_id=%lu and server_id=%lu and account_id=%lu and deleted = 0 limit 1", char_id, server_id, account_id);
  488. if(result && mysql_num_rows(result) == 1){
  489. row = mysql_fetch_row(result);
  490. return string(row[0]);
  491. }
  492. return string("");
  493. }
  494. bool LoginDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update, int32 server_id){
  495. Query query;
  496. string update_charts = string("update login_characters set unix_timestamp=%lu where char_id=%lu and account_id=%lu and server_id=%lu");
  497. query.RunQuery2(Q_UPDATE, update_charts.c_str(),timestamp_update,character_id,account_id,server_id);
  498. if(!query.GetAffectedRows())
  499. {
  500. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError());
  501. return false;
  502. }
  503. return true;
  504. }
  505. bool LoginDatabase::UpdateCharacterLevel(int32 account_id, int32 character_id, int8 in_level, int32 server_id){
  506. Query query;
  507. string update_charts = string("update login_characters set level=%i where char_id=%lu and account_id=%lu and server_id=%lu");
  508. query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_level,character_id,account_id,server_id);
  509. if(!query.GetAffectedRows())
  510. {
  511. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterLevel query '%s': %s", query.GetQuery(), query.GetError());
  512. return false;
  513. }
  514. return true;
  515. }
  516. bool LoginDatabase::UpdateCharacterRace(int32 account_id, int32 character_id, int16 in_racetype, int8 in_race, int32 server_id){
  517. Query query;
  518. string update_charts = string("update login_characters set race_type=%i, race=%i where char_id=%lu and account_id=%lu and server_id=%lu");
  519. query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_racetype,in_race,character_id,account_id,server_id);
  520. if(!query.GetAffectedRows())
  521. {
  522. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterRace query '%s': %s", query.GetQuery(), query.GetError());
  523. return false;
  524. }
  525. return true;
  526. }
  527. bool LoginDatabase::UpdateCharacterZone(int32 account_id, int32 character_id, int32 zone_id, int32 server_id){
  528. Query query;
  529. string update_chars = string("update login_characters set current_zone_id=%i where char_id=%lu and account_id=%lu and server_id=%lu");
  530. query.RunQuery2(Q_UPDATE, update_chars.c_str(), zone_id, character_id, account_id, server_id);
  531. if(!query.GetAffectedRows())
  532. {
  533. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterZone query '%s': %s", query.GetQuery(), query.GetError());
  534. return false;
  535. }
  536. return true;
  537. }
  538. bool LoginDatabase::UpdateCharacterClass(int32 account_id, int32 character_id, int8 in_class, int32 server_id){
  539. Query query;
  540. string update_charts = string("update login_characters set class=%i where char_id=%lu and account_id=%lu and server_id=%lu");
  541. query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_class,character_id,account_id,server_id);
  542. if(!query.GetAffectedRows())
  543. {
  544. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterClass query '%s': %s", query.GetQuery(), query.GetError());
  545. return false;
  546. }
  547. return true;
  548. }
  549. bool LoginDatabase::UpdateCharacterGender(int32 account_id, int32 character_id, int8 in_gender, int32 server_id){
  550. Query query;
  551. string update_charts = string("update login_characters set gender=%i where char_id=%lu and account_id=%lu and server_id=%lu");
  552. query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_gender,character_id,account_id,server_id);
  553. if(!query.GetAffectedRows())
  554. {
  555. LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterClass query '%s': %s", query.GetQuery(), query.GetError());
  556. return false;
  557. }
  558. return true;
  559. }
  560. LoginAccount* LoginDatabase::LoadAccount(const char* name, const char* password, bool attemptAccountCreation){
  561. LoginAccount* acct = NULL;
  562. Query query;
  563. query.escaped_name = getEscapeString(name);
  564. query.escaped_pass = getEscapeString(password);
  565. time_t now = time(0); //get the current epoc time
  566. MYSQL_ROW row;
  567. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s' and passwd=sha2('%s',512)", query.escaped_name, query.escaped_pass);
  568. if(result){
  569. if (mysql_num_rows(result) == 1){
  570. row = mysql_fetch_row(result);
  571. int32 id = atol(row[0]);
  572. acct = new LoginAccount(id, name, password);
  573. acct->setAuthenticated(true);
  574. }
  575. else if(mysql_num_rows(result) > 0)
  576. LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoginAccount: more than one account returned for '%s'", name);
  577. else if (attemptAccountCreation && !database.GetAccountIDByName(name))
  578. {
  579. Query newquery;
  580. newquery.RunQuery2(Q_INSERT, "insert into account set name='%s',passwd=sha2('%s',512), created_date=%i", query.escaped_name, query.escaped_pass, now);
  581. // re-run the query for select only not account creation
  582. return LoadAccount(name, password, false);
  583. }
  584. }
  585. return acct;
  586. }
  587. int32 LoginDatabase::GetAccountIDByName(const char* name) {
  588. int32 id = 0;
  589. Query query;
  590. MYSQL_ROW row;
  591. query.escaped_name = getEscapeString(name);
  592. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s'", query.escaped_name);
  593. if (result && mysql_num_rows(result) == 1) {
  594. row = mysql_fetch_row(result);
  595. id = atoi(row[0]);
  596. }
  597. return id;
  598. }
  599. int32 LoginDatabase::CheckServerAccount(char* name, char* passwd){
  600. int32 id = 0;
  601. Query query;
  602. MYSQL_ROW row;
  603. query.escaped_name = getEscapeString(name);
  604. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lower(password), id from login_worldservers where account='%s' and disabled = 0", query.escaped_name);
  605. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccount Account=%s\nSHA=%s", (char*)query.escaped_name, passwd);
  606. if(result && mysql_num_rows(result) == 1){
  607. row = mysql_fetch_row(result);
  608. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResult Account=%s\nPassword=%s", (char*)query.escaped_name, (row && row[0]) ? row[0] : "(NULL)");
  609. if (memcmp(row[0], passwd, strnlen(row[0], 256)) == 0)
  610. {
  611. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResultMatch Account=%s", (char*)query.escaped_name);
  612. id = atoi(row[1]);
  613. }
  614. }
  615. return id;
  616. }
  617. bool LoginDatabase::IsServerAccountDisabled(char* name){
  618. Query query;
  619. MYSQL_ROW row;
  620. query.escaped_name = getEscapeString(name);
  621. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_worldservers where account='%s' and disabled = 1", query.escaped_name);
  622. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerAccountDisabled Account=%s", (char*)query.escaped_name);
  623. if(result && mysql_num_rows(result) > 0){
  624. row = mysql_fetch_row(result);
  625. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerAccountDisabled Match Account=%s", (char*)query.escaped_name);
  626. return true;
  627. }
  628. return false;
  629. }
  630. bool LoginDatabase::IsIPBanned(char* ipaddr){
  631. if(!ipaddr)
  632. return false;
  633. Query query;
  634. MYSQL_ROW row;
  635. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ip from login_bannedips where '%s' LIKE CONCAT(ip ,'%%')", ipaddr);
  636. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerIPBanned IPPartial=%s", (char*)ipaddr);
  637. if(result && mysql_num_rows(result) > 0){
  638. row = mysql_fetch_row(result);
  639. LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerIPBanned Match IPBan=%s", row[0]);
  640. return true;
  641. }
  642. return false;
  643. }
  644. void LoginDatabase::GetServerAccounts(vector<LWorld*>* server_list){
  645. Query query;
  646. MYSQL_ROW row;
  647. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, account, name, admin_id from login_worldservers");
  648. while((row = mysql_fetch_row(result))){
  649. LWorld* world = new LWorld(atol(row[0]), row[1], row[2], atoi(row[3]));
  650. world->SetID(world->GetAccountID());
  651. server_list->push_back(world);
  652. }
  653. }
  654. void LoginDatabase::SaveClientLog(const char* type, const char* message, const char* player_name, int16 version){
  655. Query query;
  656. query.escaped_data1 = getEscapeString(message);
  657. query.escaped_name = getEscapeString(player_name);
  658. query.RunQuery2(Q_INSERT, "insert into log_messages (type, message, name, version) values('%s', '%s', '%s', %i)", type, query.escaped_data1, query.escaped_name, version);
  659. }
  660. bool LoginDatabase::VerifyDelete(int32 account_id, int32 character_id, const char* name){
  661. Query query;
  662. query.escaped_name = getEscapeString(name);
  663. query.RunQuery2(Q_UPDATE, "update login_characters set deleted = 1 where char_id=%i and account_id=%i and name='%s'", character_id, account_id, query.escaped_name);
  664. if(query.GetAffectedRows() == 1)
  665. return true;
  666. else
  667. return false;
  668. }
  669. char* LoginDatabase::GetServerAccountName(int32 id){
  670. Query query;
  671. MYSQL_ROW row;
  672. char* name = 0;
  673. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_worldservers where id=%lu", id);
  674. if(result && mysql_num_rows(result) == 1){
  675. row = mysql_fetch_row(result);
  676. if(strlen(row[0]) > 0){
  677. name = new char[strlen(row[0])+1];
  678. strcpy(name, row[0]);
  679. }
  680. }
  681. return name;
  682. }
  683. int32 LoginDatabase::GetRaceID(char* name){
  684. int32 ret = 1487;
  685. Query query;
  686. MYSQL_ROW row;
  687. query.escaped_name = getEscapeString(name);
  688. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_type from login_races where name='%s'", query.escaped_name);
  689. if(result && mysql_num_rows(result) == 1){
  690. row = mysql_fetch_row(result);
  691. ret = atol(row[0]);
  692. }
  693. else if(!result || mysql_num_rows(result) == 0)
  694. UpdateRaceID(query.escaped_name);
  695. return ret;
  696. }
  697. void LoginDatabase::UpdateRaceID(char* name){
  698. Query query;
  699. query.RunQuery2(Q_UPDATE, "insert into login_races (name) values('%s')", name);
  700. }
  701. bool LoginDatabase::CheckVersion(char* in_version){
  702. Query query;
  703. query.escaped_data1 = getEscapeString(in_version);
  704. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_versions where version='%s' or version='*'", query.escaped_data1);
  705. if(result && mysql_num_rows(result) > 0)
  706. return true;
  707. else
  708. return false;
  709. }
  710. void LoginDatabase::GetLatestTableVersions(LatestTableVersions* table_versions){
  711. Query query;
  712. MYSQL_ROW row;
  713. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name, max(version) from login_table_versions group by name order by id");
  714. if(result && mysql_num_rows(result) > 0){
  715. table_versions->SetTableSize(mysql_num_rows(result));
  716. }
  717. else // we need to return if theres no result, otherwise it will crash attempting to loop through rows
  718. return;
  719. while((row = mysql_fetch_row(result))){
  720. if(VerifyDataTable(row[0]))
  721. table_versions->AddTable(row[0], atoi(row[1]), GetDataVersion(row[0]));
  722. else
  723. table_versions->AddTable(row[0], atoi(row[1]), 0);
  724. }
  725. }
  726. bool LoginDatabase::VerifyDataTable(char* name){
  727. Query query;
  728. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT table_name from download_tables where table_name='%s'", name);
  729. if(result && mysql_num_rows(result) > 0)
  730. return true;
  731. return false;
  732. }
  733. string LoginDatabase::GetColumnNames(char* name){
  734. Query query;
  735. MYSQL_ROW row;
  736. string columns = "(";
  737. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "show columns from %s", name);
  738. if(result && mysql_num_rows(result) > 0){
  739. int16 i = 0;
  740. while((row = mysql_fetch_row(result))){
  741. if(strcmp(row[0], "table_data_version") != 0){
  742. if(i>0)
  743. columns.append(",");
  744. columns.append(row[0]);
  745. i++;
  746. }
  747. }
  748. }
  749. columns.append(") ");
  750. return columns;
  751. }
  752. TableDataQuery* LoginDatabase::GetTableDataQuery(int32 server_ip, char* name, int16 version){
  753. Query query;
  754. MYSQL_ROW row;
  755. query.escaped_name = getEscapeString(name);
  756. TableDataQuery* table_query = 0;
  757. MYSQL_RES* result = 0;
  758. string columns;
  759. if(VerifyDataTable(query.escaped_name)){
  760. result = query.RunQuery2(Q_SELECT, "SELECT * from %s where table_data_version > %i", query.escaped_name, version);
  761. columns = GetColumnNames(query.escaped_name);
  762. }
  763. if(result && mysql_num_rows(result) > 0){
  764. table_query = new TableDataQuery(query.escaped_name);
  765. table_query->num_queries = mysql_num_rows(result);
  766. table_query->columns_size = columns.length() + 1;
  767. table_query->columns = new char[table_query->columns_size + 1];
  768. table_query->version = GetDataVersion(query.escaped_name);
  769. strcpy(table_query->columns, (char*)columns.c_str());
  770. string query_data;
  771. MYSQL_FIELD* field;
  772. int* int_list = new int[mysql_num_fields(result)];
  773. int16 ndx = 0;
  774. while((field = mysql_fetch_field(result))){
  775. int_list[ndx] = IS_NUM(field->type);
  776. if(strcmp(field->name,"table_data_version") == 0)
  777. int_list[ndx] = 2;
  778. ndx++;
  779. }
  780. ndx = 0;
  781. while((row = mysql_fetch_row(result))){
  782. query_data = "";
  783. for(int i=0;i<mysql_num_fields(result);i++){
  784. if(int_list[i]<2){
  785. if(i>0)
  786. query_data.append(",");
  787. if(!int_list[i]){
  788. query_data.append("'").append(getEscapeString(row[i])).append("'");
  789. }
  790. else
  791. query_data.append(row[i]);
  792. }
  793. }
  794. TableData* new_query = new TableData;
  795. new_query->size = query_data.length() + 1;
  796. new_query->query = new char[query_data.length() + 1];
  797. strcpy(new_query->query, query_data.c_str());
  798. table_query->queries.push_back(new_query);
  799. ndx++;
  800. }
  801. safe_delete_array(int_list);
  802. }
  803. else{
  804. string query2 = string("The user tried to download the following table: ").append(query.escaped_name);
  805. SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Data", server_ip);
  806. }
  807. return table_query;
  808. }
  809. TableQuery* LoginDatabase::GetLatestTableQuery(int32 server_ip, char* name, int16 version){
  810. Query query;
  811. MYSQL_ROW row;
  812. query.escaped_name = getEscapeString(name);
  813. TableQuery* table_query = 0;
  814. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT query, version from login_table_versions where name = '%s' and version>=%i order by version", query.escaped_name, version + 1);
  815. if(result && mysql_num_rows(result) > 0){
  816. int16 i = 0;
  817. table_query = new TableQuery;
  818. while((row = mysql_fetch_row(result))){
  819. char* rowdata = row[0];
  820. if(strstr(rowdata, ";")){
  821. char* token = strtok(rowdata,";");
  822. while(token){
  823. char* new_query = new char[strlen(token) + 1];
  824. strcpy(new_query, token);
  825. table_query->AddQuery(new_query);
  826. token = strtok(NULL, ";");
  827. }
  828. }
  829. else
  830. table_query->AddQuery(rowdata);
  831. table_query->latest_version = atoi(row[1]);
  832. }
  833. strcpy(table_query->tablename, name);
  834. table_query->your_version = version;
  835. }
  836. else{
  837. string query2 = string("The following was the DB Query: ").append(query.GetQuery());
  838. SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Query", server_ip);
  839. }
  840. return table_query;
  841. }
  842. sint16 LoginDatabase::GetDataVersion(char* name){
  843. Query query;
  844. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(table_data_version) from %s", name);
  845. sint16 ret_version = 0;
  846. if(result && mysql_num_rows(result) > 0) {
  847. MYSQL_ROW row;
  848. row = mysql_fetch_row(result);
  849. if(row[0])
  850. ret_version = atoi(row[0]);
  851. }
  852. return ret_version;
  853. }
  854. void LoginDatabase::RemoveOldWorldServerStats(){
  855. Query query;
  856. query.RunQuery2(Q_DELETE, "delete from login_worldstats where (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 86400");
  857. }
  858. void LoginDatabase::UpdateWorldServerStats(LWorld* world, sint32 status)
  859. {
  860. Query query;
  861. query.RunQuery2(Q_INSERT, "insert into login_worldstats (world_id, world_status, current_players, current_zones, last_update, world_max_level) values(%u, %i, %i, %i, NOW(), %i) ON DUPLICATE KEY UPDATE current_players=%i,current_zones=%i,world_max_level=%i,world_status=%i,last_update=NOW()",
  862. world->GetAccountID(), status, world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), status);
  863. string update_stats = string("update login_worldservers set lastseen=%u where id=%i");
  864. query.RunQuery2(Q_UPDATE, update_stats.c_str(), Timer::GetUnixTimeStamp(), world->GetAccountID());
  865. }
  866. bool LoginDatabase::ResetWorldServerStatsConnectedTime(LWorld* world){
  867. if(!world || world->GetAccountID() == 0)
  868. return false;
  869. Query query;
  870. string update_stats = string("update login_worldstats set connected_time=now() where world_id=%i and (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 300");
  871. query.RunQuery2(Q_UPDATE, update_stats.c_str(),world->GetAccountID());
  872. return true;
  873. }
  874. void LoginDatabase::ResetWorldStats ( )
  875. {
  876. Query query;
  877. string update_stats = string("update login_worldstats set world_status=-4, current_players=0, current_zones=0");
  878. query.RunQuery2(update_stats.c_str(), Q_UPDATE);
  879. }
  880. void LoginDatabase::SaveBugReport(int32 world_id, char* category, char* subcategory, char* causes_crash, char* reproducible, char* summary, char* description, char* version, char* player, int32 account_id, char* spawn_name, int32 spawn_id, int32 zone_id){
  881. Query query;
  882. string bug_report = string("insert into bugs (world_id, category, subcategory, causes_crash, reproducible, summary, description, version, player, account_id, spawn_name, spawn_id, zone_id) values(%lu, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %lu, '%s', %lu, %lu)");
  883. query.RunQuery2(Q_INSERT, bug_report.c_str(), world_id, database.getSafeEscapeString(category).c_str(), database.getSafeEscapeString(subcategory).c_str(),
  884. database.getSafeEscapeString(causes_crash).c_str(), database.getSafeEscapeString(reproducible).c_str(), database.getSafeEscapeString(summary).c_str(),
  885. database.getSafeEscapeString(description).c_str(), database.getSafeEscapeString(version).c_str(), database.getSafeEscapeString(player).c_str(), account_id,
  886. database.getSafeEscapeString(spawn_name).c_str(), spawn_id, zone_id);
  887. FixBugReport();
  888. }
  889. void LoginDatabase::FixBugReport(){
  890. Query query;
  891. string bug_report = string("update bugs set description = REPLACE(description,SUBSTRING(description,INSTR(description,'%'), 3),char(CONV(SUBSTRING(description,INSTR(description,'%')+1, 2), 16, 10))), summary = REPLACE(summary,SUBSTRING(summary,INSTR(summary,'%'), 3),char(CONV(SUBSTRING(summary,INSTR(summary,'%')+1, 2), 16, 10)))");
  892. query.RunQuery2(bug_report.c_str(), Q_UPDATE);
  893. }
  894. void LoginDatabase::UpdateWorldIPAddress(int32 world_id, int32 address){
  895. struct in_addr in;
  896. in.s_addr = address;
  897. Query query;
  898. query.RunQuery2(Q_UPDATE, "update login_worldservers set ip_address='%s' where id=%lu", inet_ntoa(in), world_id);
  899. }
  900. void LoginDatabase::UpdateAccountIPAddress(int32 account_id, int32 address){
  901. struct in_addr in;
  902. in.s_addr = address;
  903. Query query;
  904. query.RunQuery2(Q_UPDATE, "update account set ip_address='%s' where id=%lu", inet_ntoa(in), account_id);
  905. }
  906. //devn00b: There is no rulesystem for login, so im going to use login_config for future things like this.
  907. //devn00b: Returns the number of characters a player may create per account. This should be set by server owners -> login,
  908. //devn00b: However, better semi-working for now than not working at all.
  909. //devn00b: TODO: EQ2World sends max char per acct.
  910. int8 LoginDatabase::GetMaxCharsSetting() {
  911. //live defaults to 7 for GOLD members.
  912. int8 max_chars = 7;
  913. Query query;
  914. MYSQL_ROW row;
  915. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select config_value from login_config where config_name='max_characters_per_account'");
  916. if (result && mysql_num_rows(result) == 1) {
  917. row = mysql_fetch_row(result);
  918. if (row[0])
  919. max_chars = atoi(row[0]);
  920. }
  921. //if nothing else return the default.
  922. return max_chars;
  923. }
  924. int16 LoginDatabase::GetAccountBonus(int32 acct_id) {
  925. int32 bonus = 0;
  926. int16 world_id = 0;
  927. Query query;
  928. MYSQL_ROW row;
  929. Query query2;
  930. MYSQL_ROW row2;
  931. //get the world ID for the character. TODO: Support multi server characters.
  932. MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "select server_id from login_characters where account_id=%i", acct_id);
  933. if (result2 && mysql_num_rows(result2) >= 1) {
  934. row2 = mysql_fetch_row(result2);
  935. if (row2[0])
  936. world_id = atoi(row2[0]);
  937. }
  938. //pull all characters greater than the max level from the server
  939. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM login_characters WHERE LEVEL >= (select world_max_level from login_worldstats where world_id=%i) AND account_id=%i", world_id, acct_id);
  940. if (result && mysql_num_rows(result) == 1) {
  941. row = mysql_fetch_row(result);
  942. if(row[0])
  943. bonus = atoi(row[0]);
  944. }
  945. return bonus;
  946. }
  947. void LoginDatabase::UpdateWorldVersion(int32 world_id, char* version) {
  948. Query query;
  949. query.RunQuery2(Q_UPDATE, "update login_worldservers set login_version='%s' where id=%u", version, world_id);
  950. }
  951. void LoginDatabase::UpdateAccountClientDataVersion(int32 account_id, int16 version)
  952. {
  953. Query query;
  954. query.RunQuery2(Q_UPDATE, "UPDATE account SET last_client_version='%i' WHERE id = %u", version, account_id);
  955. }
  956. //devn00b todo: finish this.
  957. void LoginDatabase::SaveCharacterPicture(int32 account_id, int32 character_id, int32 server_id, int16 picture_size, uchar* picture) {
  958. stringstream ss_hex;
  959. stringstream ss_query;
  960. ss_hex.flags(ios::hex);
  961. for (int32 i = 0; i < picture_size; i++)
  962. ss_hex << setfill('0') << setw(2) << (int32)picture[i];
  963. ss_query << "INSERT INTO `ls_character_picture` (`server_id`, `account_id`, `character_id`, `picture`) VALUES (" << server_id << ", " << account_id << ", " << character_id << ", '" << ss_hex.str() << "') ON DUPLICATE KEY UPDATE `picture` = '" << ss_hex.str() << "'";
  964. if (!dbLogin.Query(ss_query.str().c_str()))
  965. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", dbLogin.GetError(), dbLogin.GetErrorMsg());
  966. }