char_clif.cpp 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "char_clif.hpp"
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include <memory>
  7. #include <unordered_map>
  8. #include <vector>
  9. #include <common/malloc.hpp>
  10. #include <common/mapindex.hpp>
  11. #include <common/mmo.hpp>
  12. #include <common/random.hpp>
  13. #include <common/showmsg.hpp>
  14. #include <common/socket.hpp>
  15. #include <common/sql.hpp>
  16. #include <common/strlib.hpp>
  17. #include <common/timer.hpp>
  18. #include <common/utilities.hpp>
  19. #include <common/utils.hpp>
  20. #include "char.hpp"
  21. #include "char_logif.hpp"
  22. #include "char_mapif.hpp"
  23. #include "inter.hpp"
  24. #include "packets.hpp"
  25. using namespace rathena;
  26. std::vector<struct s_point_str> accessible_maps{
  27. s_point_str{ MAP_PRONTERA, 273, 354 },
  28. s_point_str{ MAP_GEFFEN, 120, 100 },
  29. s_point_str{ MAP_MORROC, 160, 94 },
  30. s_point_str{ MAP_ALBERTA, 116, 57 },
  31. s_point_str{ MAP_PAYON, 87, 117 },
  32. s_point_str{ MAP_IZLUDE, 94, 103 }
  33. };
  34. #if PACKETVER_SUPPORTS_PINCODE
  35. bool pincode_allowed( char* pincode );
  36. #endif
  37. //------------------------------------------------
  38. //Add On system
  39. //------------------------------------------------
  40. // reason
  41. // 0: success
  42. // 1: failed
  43. void chclif_moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason ){
  44. WFIFOHEAD(fd,8);
  45. WFIFOW(fd,0) = HEADER_HC_ACK_CHANGE_CHARACTER_SLOT;
  46. WFIFOW(fd,2) = 8;
  47. WFIFOW(fd,4) = reason;
  48. WFIFOW(fd,6) = sd->char_moves[index];
  49. WFIFOSET(fd,8);
  50. }
  51. /*
  52. * Client is requesting to move a charslot
  53. */
  54. int chclif_parse_moveCharSlot( int fd, struct char_session_data* sd){
  55. FIFOSD_CHECK(8);
  56. uint16 from, to;
  57. from = RFIFOW(fd,2);
  58. to = RFIFOW(fd,4);
  59. //Cnt = RFIFOW(fd,6); //how many time we have left to change (client.. lol we don't trust him)
  60. RFIFOSKIP(fd,8);
  61. // Have we changed to often or is it disabled?
  62. if( (charserv_config.charmove_config.char_move_enabled)==0
  63. || ( (charserv_config.charmove_config.char_moves_unlimited)==0 && sd->char_moves[from] <= 0 ) ){
  64. chclif_moveCharSlotReply( fd, sd, from, 1 );
  65. return 1;
  66. }
  67. // We don't even have a character on the chosen slot?
  68. if( sd->found_char[from] <= 0 || to >= sd->char_slots ){
  69. chclif_moveCharSlotReply( fd, sd, from, 1 );
  70. return 1;
  71. }
  72. if( sd->found_char[to] > 0 ){
  73. // We want to move to a used position
  74. if( charserv_config.charmove_config.char_movetoused ){ // TODO: check if the target is in deletion process
  75. // Admin is friendly and uses triangle exchange
  76. if( SQL_ERROR == Sql_QueryStr(sql_handle, "START TRANSACTION")
  77. || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'",schema_config.char_db, to, sd->found_char[from] )
  78. || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'", schema_config.char_db, from, sd->found_char[to] )
  79. || SQL_ERROR == Sql_QueryStr(sql_handle, "COMMIT")
  80. ){
  81. chclif_moveCharSlotReply( fd, sd, from, 1 );
  82. Sql_ShowDebug(sql_handle);
  83. Sql_QueryStr(sql_handle,"ROLLBACK");
  84. return 1;
  85. }
  86. }else{
  87. // Admin doesn't allow us to
  88. chclif_moveCharSlotReply( fd, sd, from, 1 );
  89. return 1;
  90. }
  91. }else if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d'", schema_config.char_db, to, sd->found_char[from] ) ){
  92. Sql_ShowDebug(sql_handle);
  93. chclif_moveCharSlotReply( fd, sd, from, 1 );
  94. return 1;
  95. }
  96. if( (charserv_config.charmove_config.char_moves_unlimited)==0 ){
  97. sd->char_moves[from]--;
  98. Sql_Query(sql_handle, "UPDATE `%s` SET `moves`='%d' WHERE `char_id`='%d'", schema_config.char_db, sd->char_moves[from], sd->found_char[from] );
  99. }
  100. // We successfully moved the char - time to notify the client
  101. chclif_moveCharSlotReply( fd, sd, from, 0 );
  102. chclif_mmo_char_send(fd, sd);
  103. return 1;
  104. }
  105. #if PACKETVER_SUPPORTS_PINCODE
  106. /* pincode_sendstate transmist the pincode state to client
  107. * S 08b9 <seed>.L <aid>.L <state>.W (HC_SECOND_PASSWD_LOGIN)
  108. * state :
  109. * 0 = disabled / pin is correct
  110. * 1 = ask for pin - client sends 0x8b8
  111. * 2 = create new pin - client sends 0x8ba
  112. * 3 = pin must be changed - client 0x8be
  113. * 4 = create new pin - client sends 0x8ba
  114. * 5 = client shows msgstr(1896)
  115. * 6 = client shows msgstr(1897) Unable to use your KSSN number
  116. * 7 = char select window shows a button - client sends 0x8c5
  117. * 8 = pincode was incorrect
  118. */
  119. void chclif_pincode_sendstate( int fd, struct char_session_data* sd, enum pincode_state state ){
  120. WFIFOHEAD(fd, 12);
  121. WFIFOW(fd, 0) = 0x8b9;
  122. WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF;
  123. WFIFOL(fd, 6) = sd->account_id;
  124. WFIFOW(fd,10) = state;
  125. WFIFOSET(fd,12);
  126. }
  127. /*
  128. * Client just entering charserv from login, send him pincode confirmation
  129. */
  130. int chclif_parse_reqpincode_window(int fd, struct char_session_data* sd){
  131. FIFOSD_CHECK(6);
  132. if( charserv_config.pincode_config.pincode_enabled && RFIFOL(fd,2) == sd->account_id ){
  133. if( strlen( sd->pincode ) <= 0 ){
  134. chclif_pincode_sendstate( fd, sd, PINCODE_NEW );
  135. }else{
  136. chclif_pincode_sendstate( fd, sd, PINCODE_ASK );
  137. }
  138. }
  139. RFIFOSKIP(fd,6);
  140. return 1;
  141. }
  142. /*
  143. * Client as anwsered pincode questionning, checking if valid anwser
  144. */
  145. int chclif_parse_pincode_check( int fd, struct char_session_data* sd ){
  146. FIFOSD_CHECK(10);
  147. char pin[PINCODE_LENGTH+1];
  148. if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
  149. set_eof(fd);
  150. return 1;
  151. }
  152. memset(pin,0,PINCODE_LENGTH+1);
  153. strncpy((char*)pin, RFIFOCP(fd, 6), PINCODE_LENGTH);
  154. RFIFOSKIP(fd,10);
  155. if (!char_pincode_decrypt(sd->pincode_seed, pin )) {
  156. set_eof(fd);
  157. return 1;
  158. }
  159. if( char_pincode_compare( fd, sd, pin ) ){
  160. sd->pincode_correct = true;
  161. chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
  162. }
  163. return 1;
  164. }
  165. /*
  166. * Helper function to check if a new pincode contains illegal characters or combinations
  167. */
  168. bool pincode_allowed( char* pincode ){
  169. int i;
  170. char c, n, compare[PINCODE_LENGTH+1];
  171. memset( compare, 0, PINCODE_LENGTH+1);
  172. // Sanity check for bots to prevent errors
  173. for( i = 0; i < PINCODE_LENGTH; i++ ){
  174. c = pincode[i];
  175. if( c < '0' || c > '9' ){
  176. return false;
  177. }
  178. }
  179. // Is it forbidden to use only the same character?
  180. if( !charserv_config.pincode_config.pincode_allow_repeated ){
  181. c = pincode[0];
  182. // Check if the first character equals the rest of the input
  183. for( i = 0; i < PINCODE_LENGTH; i++ ){
  184. compare[i] = c;
  185. }
  186. if( strncmp( pincode, compare, PINCODE_LENGTH + 1 ) == 0 ){
  187. return false;
  188. }
  189. }
  190. // Is it forbidden to use a sequential combination of numbers?
  191. if( !charserv_config.pincode_config.pincode_allow_sequential ){
  192. c = pincode[0];
  193. // Check if it is an ascending sequence
  194. for( i = 0; i < PINCODE_LENGTH; i++ ){
  195. n = c + i;
  196. if( n > '9' ){
  197. compare[i] = '0' + ( n - '9' ) - 1;
  198. }else{
  199. compare[i] = n;
  200. }
  201. }
  202. if( strncmp( pincode, compare, PINCODE_LENGTH + 1 ) == 0 ){
  203. return false;
  204. }
  205. // Check if it is an descending sequence
  206. for( i = 0; i < PINCODE_LENGTH; i++ ){
  207. n = c - i;
  208. if( n < '0' ){
  209. compare[i] = '9' - ( '0' - n ) + 1;
  210. }else{
  211. compare[i] = n;
  212. }
  213. }
  214. if( strncmp( pincode, compare, PINCODE_LENGTH + 1 ) == 0 ){
  215. return false;
  216. }
  217. }
  218. return true;
  219. }
  220. /*
  221. * Client request to change pincode
  222. */
  223. int chclif_parse_pincode_change( int fd, struct char_session_data* sd ){
  224. FIFOSD_CHECK(14);
  225. if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
  226. set_eof(fd);
  227. return 1;
  228. }
  229. else {
  230. char oldpin[PINCODE_LENGTH+1];
  231. char newpin[PINCODE_LENGTH+1];
  232. memset(oldpin,0,PINCODE_LENGTH+1);
  233. memset(newpin,0,PINCODE_LENGTH+1);
  234. strncpy(oldpin, RFIFOCP(fd,6), PINCODE_LENGTH);
  235. strncpy(newpin, RFIFOCP(fd,10), PINCODE_LENGTH);
  236. RFIFOSKIP(fd,14);
  237. if (!char_pincode_decrypt(sd->pincode_seed,oldpin) || !char_pincode_decrypt(sd->pincode_seed,newpin)) {
  238. set_eof(fd);
  239. return 1;
  240. }
  241. if( !char_pincode_compare( fd, sd, oldpin ) )
  242. return 1;
  243. if( pincode_allowed(newpin) ){
  244. chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
  245. strncpy(sd->pincode, newpin, sizeof(newpin));
  246. ShowInfo("Pincode changed for AID: %d\n", sd->account_id);
  247. sd->pincode_correct = true;
  248. chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
  249. }else{
  250. chclif_pincode_sendstate( fd, sd, PINCODE_ILLEGAL );
  251. }
  252. }
  253. return 1;
  254. }
  255. /*
  256. * activate PIN system and set first PIN
  257. */
  258. int chclif_parse_pincode_setnew( int fd, struct char_session_data* sd ){
  259. FIFOSD_CHECK(10);
  260. if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
  261. set_eof(fd);
  262. return 1;
  263. }
  264. else {
  265. char newpin[PINCODE_LENGTH+1];
  266. memset(newpin,0,PINCODE_LENGTH+1);
  267. strncpy( newpin, RFIFOCP(fd,6), PINCODE_LENGTH );
  268. RFIFOSKIP(fd,10);
  269. if (!char_pincode_decrypt( sd->pincode_seed, newpin )) {
  270. set_eof(fd);
  271. return 1;
  272. }
  273. if( pincode_allowed(newpin) ){
  274. chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
  275. strncpy( sd->pincode, newpin, sizeof( newpin ) );
  276. sd->pincode_correct = true;
  277. chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
  278. }else{
  279. chclif_pincode_sendstate( fd, sd, PINCODE_ILLEGAL );
  280. }
  281. }
  282. return 1;
  283. }
  284. #endif
  285. //----------------------------------------
  286. // Tell client how many pages, kRO sends 17 (Yommy)
  287. //----------------------------------------
  288. void chclif_charlist_notify( int fd, struct char_session_data* sd ){
  289. // This is needed on RE clients from october 2015 onwards
  290. #if defined(PACKETVER_RE) && PACKETVER >= 20151001 && PACKETVER < 20180103
  291. WFIFOHEAD(fd, 10);
  292. WFIFOW(fd, 0) = 0x9a0;
  293. // pages to req / send them all in 1 until mmo_chars_fromsql can split them up
  294. WFIFOL(fd, 2) = (sd->char_slots>3)?sd->char_slots/3:1; //int TotalCnt (nb page to load)
  295. WFIFOL(fd, 6) = sd->char_slots;
  296. WFIFOSET(fd,10);
  297. #else
  298. WFIFOHEAD(fd, 6);
  299. WFIFOW(fd, 0) = 0x9a0;
  300. // pages to req / send them all in 1 until mmo_chars_fromsql can split them up
  301. WFIFOL(fd, 2) = (sd->char_slots>3)?sd->char_slots/3:1; //int TotalCnt (nb page to load)
  302. WFIFOSET(fd,6);
  303. #endif
  304. }
  305. //----------------------------------------
  306. // Function to send characters to a player
  307. //----------------------------------------
  308. int chclif_mmo_send006b(int fd, struct char_session_data* sd){
  309. int j, offset;
  310. #if PACKETVER >= 20100413
  311. offset = 3;
  312. #else
  313. offset = 0;
  314. #endif
  315. if (charserv_config.save_log)
  316. ShowInfo("Loading Char Data (" CL_BOLD "%d" CL_RESET ")\n",sd->account_id);
  317. j = 24 + offset; // offset
  318. WFIFOHEAD(fd,j + MAX_CHARS*MAX_CHAR_BUF);
  319. WFIFOW(fd,0) = 0x6b;
  320. #if PACKETVER >= 20100413
  321. WFIFOB(fd,4) = MAX_CHARS; // Max slots.
  322. WFIFOB(fd,5) = MIN_CHARS; // Available slots. (PremiumStartSlot)
  323. WFIFOB(fd,6) = MIN_CHARS+sd->chars_vip; // Premium slots. (Any existent chars past sd->char_slots but within MAX_CHARS will show a 'Premium Service' in red)
  324. #endif
  325. memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes
  326. j+=char_mmo_chars_fromsql(sd, WFIFOP(fd,j));
  327. WFIFOW(fd,2) = j; // packet len
  328. WFIFOSET(fd,j);
  329. return 0;
  330. }
  331. //----------------------------------------
  332. // Notify client about charselect window data [Ind]
  333. //----------------------------------------
  334. void chclif_mmo_send082d(int fd, struct char_session_data* sd) {
  335. if (charserv_config.save_log)
  336. ShowInfo("Loading Char Data (" CL_BOLD "%d" CL_RESET ")\n",sd->account_id);
  337. WFIFOHEAD(fd,29);
  338. WFIFOW(fd,0) = 0x82d;
  339. WFIFOW(fd,2) = 29;
  340. WFIFOB(fd,4) = MIN_CHARS; // normal_slot
  341. WFIFOB(fd,5) = sd->chars_vip; // premium_slot
  342. WFIFOB(fd,6) = sd->chars_billing; // billing_slot
  343. WFIFOB(fd,7) = sd->char_slots; // producible_slot
  344. WFIFOB(fd,8) = MAX_CHARS; // valid_slot
  345. memset(WFIFOP(fd,9), 0, 20); // unused bytes
  346. WFIFOSET(fd,29);
  347. }
  348. void chclif_mmo_send099d(int fd, struct char_session_data *sd) {
  349. uint8 count = 0;
  350. WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF));
  351. WFIFOW(fd,0) = HEADER_HC_ACK_CHARINFO_PER_PAGE;
  352. WFIFOW(fd,2) = char_mmo_chars_fromsql(sd, WFIFOP(fd,4), &count) + 4;
  353. WFIFOSET(fd,WFIFOW(fd,2));
  354. // This is something special Gravity came up with.
  355. // The client triggers some finalization code only if count is != 3.
  356. if( count == 3 ){
  357. WFIFOHEAD(fd,4);
  358. WFIFOW(fd,0) = HEADER_HC_ACK_CHARINFO_PER_PAGE;
  359. WFIFOW(fd,2) = 4;
  360. WFIFOSET(fd,4);
  361. }
  362. }
  363. /*
  364. * Function to choose wich kind of charlist to send to client depending on his version
  365. */
  366. void chclif_mmo_char_send(int fd, struct char_session_data* sd){
  367. #if PACKETVER >= 20130000
  368. chclif_mmo_send082d(fd, sd);
  369. chclif_mmo_send006b(fd, sd);
  370. chclif_charlist_notify(fd, sd);
  371. #else
  372. chclif_mmo_send006b(fd,sd);
  373. //@FIXME dump from kro doesn't show 6b transmission
  374. #endif
  375. #if PACKETVER >= 20060819
  376. chclif_block_character(fd,sd);
  377. #endif
  378. }
  379. /*
  380. * Transmit auth result to client
  381. * <result>.B ()
  382. * result :
  383. * 1 : Server closed
  384. * 2 : Someone has already logged in with this id
  385. * 8 : already online
  386. */
  387. void chclif_send_auth_result(int fd,char result){
  388. WFIFOHEAD(fd,3);
  389. WFIFOW(fd,0) = 0x81;
  390. WFIFOB(fd,2) = result;
  391. WFIFOSET(fd,3);
  392. }
  393. /// @param result
  394. /// 0 (0x718): An unknown error has occurred.
  395. /// 1: none/success
  396. /// 3 (0x719): A database error occurred.
  397. /// 4 (0x71a): To delete a character you must withdraw from the guild.
  398. /// 5 (0x71b): To delete a character you must withdraw from the party.
  399. /// Any (0x718): An unknown error has occurred.
  400. /// HC: <0828>.W <char id>.L <Msg:0-5>.L <deleteDate>.L
  401. void chclif_char_delete2_ack(int fd, uint32 char_id, uint32 result, time_t delete_date) {
  402. WFIFOHEAD(fd,14);
  403. WFIFOW(fd,0) = 0x828;
  404. WFIFOL(fd,2) = char_id;
  405. WFIFOL(fd,6) = result;
  406. #if PACKETVER_CHAR_DELETEDATE
  407. WFIFOL(fd,10) = TOL(delete_date-time(nullptr));
  408. #else
  409. WFIFOL(fd,10) = TOL(delete_date);
  410. #endif
  411. WFIFOSET(fd,14);
  412. }
  413. /// @param result
  414. /// 0 (0x718): An unknown error has occurred.
  415. /// 1: none/success
  416. /// 2 (0x71c): Due to system settings can not be deleted.
  417. /// 3 (0x719): A database error occurred.
  418. /// 4 (0x71d): Deleting not yet possible time.
  419. /// 5 (0x71e): Date of birth do not match.
  420. /// 6 Name does not match.
  421. /// 7 Character Deletion has failed because you have entered an incorrect e-mail address.
  422. /// Any (0x718): An unknown error has occurred.
  423. /// HC: <082a>.W <char id>.L <Msg>.L
  424. void chclif_char_delete2_accept_ack(int fd, uint32 char_id, uint32 result) {
  425. #if PACKETVER >= 20130000
  426. if(result == 1 ){
  427. chclif_mmo_char_send(fd, (char_session_data*)session[fd]->session_data);
  428. }
  429. #endif
  430. WFIFOHEAD(fd,10);
  431. WFIFOW(fd,0) = 0x82a;
  432. WFIFOL(fd,2) = char_id;
  433. WFIFOL(fd,6) = result;
  434. WFIFOSET(fd,10);
  435. }
  436. /// @param result
  437. /// 1 (0x718): none/success, (if char id not in deletion process): An unknown error has occurred.
  438. /// 2 (0x719): A database error occurred.
  439. /// Any (0x718): An unknown error has occurred.
  440. /// HC: <082c>.W <char id>.L <Msg:1-2>.L
  441. void chclif_char_delete2_cancel_ack(int fd, uint32 char_id, uint32 result) {
  442. WFIFOHEAD(fd,10);
  443. WFIFOW(fd,0) = 0x82c;
  444. WFIFOL(fd,2) = char_id;
  445. WFIFOL(fd,6) = result;
  446. WFIFOSET(fd,10);
  447. }
  448. // CH: <0827>.W <char id>.L
  449. int chclif_parse_char_delete2_req(int fd, struct char_session_data* sd) {
  450. FIFOSD_CHECK(6)
  451. {
  452. uint32 char_id, i, guild_id, party_id;
  453. char* data;
  454. time_t delete_date;
  455. char_id = RFIFOL(fd,2);
  456. RFIFOSKIP(fd,6);
  457. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
  458. if( i == MAX_CHARS )
  459. {// character not found
  460. chclif_char_delete2_ack(fd, char_id, 3, 0);
  461. return 1;
  462. }
  463. if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `delete_date`,`party_id`,`guild_id` FROM `%s` WHERE `char_id`='%d'", schema_config.char_db, char_id) || SQL_SUCCESS != Sql_NextRow(sql_handle) )
  464. {
  465. Sql_ShowDebug(sql_handle);
  466. chclif_char_delete2_ack(fd, char_id, 3, 0);
  467. return 1;
  468. }
  469. Sql_GetData(sql_handle, 0, &data, nullptr); delete_date = strtoul(data, nullptr, 10);
  470. Sql_GetData(sql_handle, 1, &data, nullptr); party_id = strtoul(data, nullptr, 10);
  471. Sql_GetData(sql_handle, 2, &data, nullptr); guild_id = strtoul(data, nullptr, 10);
  472. if( delete_date ) {// character already queued for deletion
  473. chclif_char_delete2_ack(fd, char_id, 0, 0);
  474. return 1;
  475. }
  476. if (charserv_config.char_config.char_del_restriction&CHAR_DEL_RESTRICT_GUILD && guild_id) // character is in guild
  477. {
  478. chclif_char_delete2_ack(fd, char_id, 4, 0);
  479. return 1;
  480. }
  481. if (charserv_config.char_config.char_del_restriction&CHAR_DEL_RESTRICT_PARTY && party_id) // character is in party
  482. {
  483. chclif_char_delete2_ack(fd, char_id, 5, 0);
  484. return 1;
  485. }
  486. // success
  487. delete_date = time(nullptr)+(charserv_config.char_config.char_del_delay);
  488. if( SQL_SUCCESS != Sql_Query(sql_handle, "UPDATE `%s` SET `delete_date`='%lu' WHERE `char_id`='%d'", schema_config.char_db, (unsigned long)delete_date, char_id) )
  489. {
  490. Sql_ShowDebug(sql_handle);
  491. chclif_char_delete2_ack(fd, char_id, 3, 0);
  492. return 1;
  493. }
  494. chclif_char_delete2_ack(fd, char_id, 1, delete_date);
  495. }
  496. return 1;
  497. }
  498. /**
  499. * Check char deletion code
  500. * @param sd
  501. * @param delcode E-mail or birthdate
  502. * @param flag Delete flag
  503. * @return true:Success, false:Failure
  504. **/
  505. bool chclif_delchar_check(struct char_session_data *sd, char *delcode, uint8 flag) {
  506. // E-Mail check
  507. if (flag&CHAR_DEL_EMAIL && (
  508. !stricmp(delcode, sd->email) || //email does not match or
  509. (
  510. !stricmp("a@a.com", sd->email) && //it is default email and
  511. !strcmp("", delcode) //user sent an empty email
  512. ))) {
  513. ShowInfo("" CL_RED "Char Deleted" CL_RESET " " CL_GREEN "(E-Mail)" CL_RESET ".\n");
  514. return true;
  515. }
  516. // Birthdate (YYMMDD)
  517. if (flag&CHAR_DEL_BIRTHDATE && (
  518. !strcmp(sd->birthdate+2, delcode) || // +2 to cut off the century
  519. (
  520. !strcmp("",sd->birthdate) && // it is default birthdate and
  521. !strcmp("",delcode) // user sent an empty birthdate
  522. ))) {
  523. ShowInfo("" CL_RED "Char Deleted" CL_RESET " " CL_GREEN "(Birthdate)" CL_RESET ".\n");
  524. return true;
  525. }
  526. return false;
  527. }
  528. // CH: <0829>.W <char id>.L <birth date:YYMMDD>.6B
  529. int chclif_parse_char_delete2_accept(int fd, struct char_session_data* sd) {
  530. FIFOSD_CHECK(12)
  531. {
  532. char birthdate[8+1];
  533. uint32 char_id;
  534. char_id = RFIFOL(fd,2);
  535. ShowInfo(CL_RED "Request Char Deletion: " CL_GREEN "%d (%d)" CL_RESET "\n", sd->account_id, char_id);
  536. // construct "YY-MM-DD"
  537. birthdate[0] = RFIFOB(fd,6);
  538. birthdate[1] = RFIFOB(fd,7);
  539. birthdate[2] = '-';
  540. birthdate[3] = RFIFOB(fd,8);
  541. birthdate[4] = RFIFOB(fd,9);
  542. birthdate[5] = '-';
  543. birthdate[6] = RFIFOB(fd,10);
  544. birthdate[7] = RFIFOB(fd,11);
  545. birthdate[8] = 0;
  546. RFIFOSKIP(fd,12);
  547. // Only check for birthdate
  548. if (!chclif_delchar_check(sd, birthdate, CHAR_DEL_BIRTHDATE)) {
  549. chclif_char_delete2_accept_ack(fd, char_id, 5);
  550. return 1;
  551. }
  552. switch( char_delete(sd,char_id) ){
  553. // success
  554. case CHAR_DELETE_OK:
  555. chclif_char_delete2_accept_ack(fd, char_id, 1);
  556. break;
  557. // data error
  558. case CHAR_DELETE_DATABASE:
  559. // character not found
  560. case CHAR_DELETE_NOTFOUND:
  561. chclif_char_delete2_accept_ack(fd, char_id, 3);
  562. break;
  563. // in a party
  564. case CHAR_DELETE_PARTY:
  565. // in a guild
  566. case CHAR_DELETE_GUILD:
  567. // character level config restriction
  568. case CHAR_DELETE_BASELEVEL:
  569. chclif_char_delete2_accept_ack(fd, char_id, 2);
  570. break;
  571. // not queued or delay not yet passed
  572. case CHAR_DELETE_TIME:
  573. chclif_char_delete2_accept_ack(fd, char_id, 4);
  574. break;
  575. }
  576. }
  577. return 1;
  578. }
  579. // CH: <082b>.W <char id>.L
  580. int chclif_parse_char_delete2_cancel(int fd, struct char_session_data* sd) {
  581. FIFOSD_CHECK(6);
  582. uint32 char_id, i;
  583. char_id = RFIFOL(fd,2);
  584. RFIFOSKIP(fd,6);
  585. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
  586. if( i == MAX_CHARS )
  587. {// character not found
  588. chclif_char_delete2_cancel_ack(fd, char_id, 2);
  589. return 1;
  590. }
  591. // there is no need to check, whether or not the character was
  592. // queued for deletion, as the client prints an error message by
  593. // itself, if it was not the case (@see char_delete2_cancel_ack)
  594. if( SQL_SUCCESS != Sql_Query(sql_handle, "UPDATE `%s` SET `delete_date`='0' WHERE `char_id`='%d'", schema_config.char_db, char_id) )
  595. {
  596. Sql_ShowDebug(sql_handle);
  597. chclif_char_delete2_cancel_ack(fd, char_id, 2);
  598. return 1;
  599. }
  600. chclif_char_delete2_cancel_ack(fd, char_id, 1);
  601. return 1;
  602. }
  603. /*
  604. * Register a new mapserver into that char-serv
  605. * charserv can handle a MAX_SERVERS mapservs
  606. */
  607. int chclif_parse_maplogin(int fd){
  608. if (RFIFOREST(fd) < 60)
  609. return 0;
  610. else {
  611. int i;
  612. char* l_user = RFIFOCP(fd,2);
  613. char* l_pass = RFIFOCP(fd,26);
  614. l_user[23] = '\0';
  615. l_pass[23] = '\0';
  616. ARR_FIND( 0, ARRAYLENGTH(map_server), i, map_server[i].fd <= 0 );
  617. if( !global_core->is_running() ||
  618. i == ARRAYLENGTH(map_server) ||
  619. strcmp(l_user, charserv_config.userid) != 0 ||
  620. strcmp(l_pass, charserv_config.passwd) != 0 )
  621. {
  622. chmapif_connectack(fd, 3); //fail
  623. } else {
  624. chmapif_connectack(fd, 0); //success
  625. map_server[i].fd = fd;
  626. map_server[i].ip = ntohl(RFIFOL(fd,54));
  627. map_server[i].port = ntohs(RFIFOW(fd,58));
  628. map_server[i].users = 0;
  629. map_server[i].maps = {};
  630. session[fd]->func_parse = chmapif_parse;
  631. session[fd]->flag.server = 1;
  632. realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
  633. chmapif_init(fd);
  634. }
  635. RFIFOSKIP(fd,60);
  636. }
  637. return 0;
  638. }
  639. // 0065 <account id>.L <login id1>.L <login id2>.L <???>.W <sex>.B
  640. int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){
  641. if( RFIFOREST(fd) < 17 ) // request to connect
  642. return 0;
  643. else {
  644. uint32 account_id = RFIFOL(fd,2);
  645. uint32 login_id1 = RFIFOL(fd,6);
  646. uint32 login_id2 = RFIFOL(fd,10);
  647. int sex = RFIFOB(fd,16);
  648. RFIFOSKIP(fd,17);
  649. ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2);
  650. if (sd) {
  651. //Received again auth packet for already authentified account?? Discard it.
  652. //TODO: Perhaps log this as a hack attempt?
  653. //TODO: and perhaps send back a reply?
  654. ShowInfo("Already registered break\n");
  655. return 1;
  656. }
  657. CREATE(session[fd]->session_data, struct char_session_data, 1);
  658. sd = (struct char_session_data*)session[fd]->session_data;
  659. sd->account_id = account_id;
  660. sd->login_id1 = login_id1;
  661. sd->login_id2 = login_id2;
  662. sd->sex = sex;
  663. sd->auth = false; // not authed yet
  664. sd->pincode_correct = false; // not entered pincode correctly yet
  665. // send back account_id
  666. WFIFOHEAD(fd,4);
  667. WFIFOL(fd,0) = account_id;
  668. WFIFOSET(fd,4);
  669. if( !global_core->is_running() ){
  670. chclif_reject(fd, 0); // rejected from server
  671. return 1;
  672. }
  673. // search authentification
  674. std::shared_ptr<struct auth_node> node = util::umap_find( char_get_authdb(), account_id);
  675. if( node != nullptr &&
  676. node->account_id == account_id &&
  677. node->login_id1 == login_id1 &&
  678. node->login_id2 == login_id2 /*&&
  679. node->ip == ipl*/ )
  680. {// authentication found (coming from map server)
  681. char_get_authdb().erase(account_id);
  682. char_auth_ok(fd, sd);
  683. sd->pincode_correct = true; // already entered pincode correctly yet
  684. }
  685. else
  686. {// authentication not found (coming from login server)
  687. if (session_isValid(login_fd)) { // don't send request if no login-server
  688. WFIFOHEAD(login_fd,23);
  689. WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account
  690. WFIFOL(login_fd,2) = sd->account_id;
  691. WFIFOL(login_fd,6) = sd->login_id1;
  692. WFIFOL(login_fd,10) = sd->login_id2;
  693. WFIFOB(login_fd,14) = sd->sex;
  694. WFIFOL(login_fd,15) = htonl(ipl);
  695. WFIFOL(login_fd,19) = fd;
  696. WFIFOSET(login_fd,23);
  697. } else { // if no login-server, we must refuse connection
  698. chclif_reject(fd, 0); // rejected from server
  699. }
  700. }
  701. }
  702. return 1;
  703. }
  704. //struct PACKET_CH_CHARLIST_REQ { 0x0 short PacketType}
  705. int chclif_parse_req_charlist(int fd, struct char_session_data* sd){
  706. FIFOSD_CHECK(2);
  707. RFIFOSKIP(fd,2);
  708. chclif_mmo_send099d(fd,sd);
  709. return 1;
  710. }
  711. //Send player to map
  712. void chclif_send_map_data( int fd, std::shared_ptr<struct mmo_charstatus> cd, uint32 ipl, int map_server_index ){
  713. #if PACKETVER >= 20170315
  714. int cmd = 0xAC5;
  715. int size = 156;
  716. #else
  717. int cmd = 0x71;
  718. int size = 28;
  719. #endif
  720. WFIFOHEAD(fd,size);
  721. WFIFOW(fd,0) = cmd;
  722. WFIFOL(fd,2) = cd->char_id;
  723. mapindex_getmapname_ext( cd->last_point.map, WFIFOCP( fd, 6 ) );
  724. uint32 subnet_map_ip = char_lan_subnetcheck(ipl); // Advanced subnet check [LuzZza]
  725. WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : map_server[map_server_index].ip);
  726. WFIFOW(fd,26) = ntows(htons(map_server[map_server_index].port)); // [!] LE byte order here [!]
  727. #if PACKETVER >= 20170315
  728. memset(WFIFOP(fd, 28), 0, 128); // Unknown
  729. #endif
  730. #ifdef DEBUG
  731. ShowDebug("Sending the client (%d %d.%d.%d.%d) to map-server with ip %d.%d.%d.%d and port %hu\n",
  732. cd->account_id, CONVIP(ipl), CONVIP((subnet_map_ip) ? subnet_map_ip : map_server[map_server_index].ip),
  733. map_server[map_server_index].port);
  734. #endif
  735. WFIFOSET(fd,size);
  736. }
  737. int chclif_parse_select_accessible_map( int fd, struct char_session_data* sd, uint32 ipl ){
  738. #if PACKETVER >= 20100714
  739. struct PACKET_CH_SELECT_ACCESSIBLE_MAPNAME p;
  740. FIFOSD_CHECK( sizeof( p ) );
  741. memcpy( &p, RFIFOP( fd, 0 ), sizeof( p ) );
  742. RFIFOSKIP( fd, sizeof( p ) );
  743. char* data;
  744. // Check if the character exists and is not scheduled for deletion
  745. if( SQL_SUCCESS != Sql_Query( sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d' AND `delete_date` = 0", schema_config.char_db, sd->account_id, p.slot )
  746. || SQL_SUCCESS != Sql_NextRow( sql_handle )
  747. || SQL_SUCCESS != Sql_GetData( sql_handle, 0, &data, nullptr ) ){
  748. // Not found?? May be forged packet.
  749. Sql_ShowDebug( sql_handle );
  750. Sql_FreeResult( sql_handle );
  751. chclif_reject( fd, 0 ); // rejected from server
  752. return 1;
  753. }
  754. uint32 char_id = atoi( data );
  755. Sql_FreeResult( sql_handle );
  756. // Prevent select a char while retrieving guild bound items
  757. if( sd->flag&1 ){
  758. chclif_reject( fd, 0 ); // rejected from server
  759. return 1;
  760. }
  761. /* client doesn't let it get to this point if you're banned, so its a forged packet */
  762. if( sd->found_char[p.slot] == char_id && sd->unban_time[p.slot] > time(nullptr) ) {
  763. chclif_reject( fd, 0 ); // rejected from server
  764. return 1;
  765. }
  766. /* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */
  767. char_set_char_online( -2, char_id, sd->account_id );
  768. struct mmo_charstatus char_dat;
  769. if( !char_mmo_char_fromsql( char_id, &char_dat, true ) ) {
  770. /* failed? set it back offline */
  771. char_set_char_offline( char_id, sd->account_id );
  772. /* failed to load something. REJECT! */
  773. chclif_reject( fd, 0 ); /* jump off this boat */
  774. return 1;
  775. }
  776. // Have to switch over to the DB instance otherwise data won't propagate [Kevin]
  777. std::shared_ptr<struct mmo_charstatus> cd = util::umap_find( char_get_chardb(), char_id );
  778. if( charserv_config.log_char ){
  779. char esc_name[NAME_LENGTH*2+1];
  780. Sql_EscapeStringLen( sql_handle, esc_name, char_dat.name, strnlen( char_dat.name, NAME_LENGTH ) );
  781. if( SQL_ERROR == Sql_Query( sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", schema_config.charlog_db, sd->account_id, p.slot, esc_name ) ){
  782. Sql_ShowDebug( sql_handle );
  783. }
  784. }
  785. ShowInfo( "Selected char: (Account %d: %d - %s)\n", sd->account_id, p.slot, char_dat.name );
  786. // Check if there is really no mapserver for the last point where the player was
  787. int32 mapserver = char_search_mapserver( cd->last_point.map, -1, -1 );
  788. // It was not an unavailable map
  789. if( mapserver >= 0 ){
  790. chclif_reject( fd, 0 ); // rejected from server
  791. return 1;
  792. }
  793. if( static_cast<size_t>( p.mapnumber ) >= accessible_maps.size() ){
  794. chclif_reject( fd, 0 ); // rejected from server
  795. return 1;
  796. }
  797. s_point_str& accessible_map = accessible_maps[p.mapnumber];
  798. safestrncpy( cd->last_point.map, accessible_map.map, sizeof( cd->last_point.map ) );
  799. cd->last_point.x = accessible_map.x;
  800. cd->last_point.y = accessible_map.y;
  801. mapserver = char_search_mapserver( cd->last_point.map, -1, -1 );
  802. // No mapserver found for our accessible map
  803. if( mapserver < 0 ){
  804. chclif_reject( fd, 0 ); // rejected from server
  805. return 1;
  806. }
  807. int map_fd;
  808. // Send NEW auth packet [Kevin]
  809. // FIXME: is this case even possible? [ultramage]
  810. if( ( map_fd = map_server[mapserver].fd ) < 1 || session[map_fd] == nullptr ){
  811. ShowError( "parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, mapserver );
  812. map_server[mapserver] = {};
  813. map_server[mapserver].fd = -1;
  814. chclif_send_auth_result( fd, 1 ); // Send server closed.
  815. return 1;
  816. }
  817. chclif_send_map_data( fd, cd, ipl, mapserver );
  818. // create temporary auth entry
  819. std::shared_ptr<struct auth_node> node = std::make_shared<struct auth_node>();
  820. node->account_id = sd->account_id;
  821. node->char_id = cd->char_id;
  822. node->login_id1 = sd->login_id1;
  823. node->login_id2 = sd->login_id2;
  824. node->sex = sd->sex;
  825. node->expiration_time = sd->expiration_time;
  826. node->group_id = sd->group_id;
  827. node->ip = ipl;
  828. char_get_authdb()[node->account_id] = node;
  829. return 1;
  830. #else
  831. return 0;
  832. #endif
  833. }
  834. void chclif_accessible_maps( int fd ){
  835. #if PACKETVER >= 20100714
  836. PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME* p = reinterpret_cast<PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME*>( packet_buffer );
  837. p->packetType = HEADER_HC_NOTIFY_ACCESSIBLE_MAPNAME;
  838. p->packetLength = sizeof( *p );
  839. int count = 0;
  840. for( s_point_str& accessible_map : accessible_maps ){
  841. int32 mapserver = char_search_mapserver( accessible_map.map, -1, -1 );
  842. if( mapserver < 0 ){
  843. p->maps[count].status = 1;
  844. }else{
  845. p->maps[count].status = 0;
  846. }
  847. mapindex_getmapname_ext( accessible_map.map, p->maps[count].map );
  848. p->packetLength += sizeof( p->maps[0] );
  849. count++;
  850. }
  851. WFIFOHEAD( fd, p->packetLength );
  852. memcpy( WFIFOP( fd, 0 ), p, p->packetLength );
  853. WFIFOSET( fd, p->packetLength );
  854. #else
  855. chclif_send_auth_result( fd, 1 ); // 01 = Server closed
  856. #endif
  857. }
  858. int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
  859. FIFOSD_CHECK(3)
  860. {
  861. struct mmo_charstatus char_dat;
  862. char* data;
  863. uint32 char_id;
  864. int i, map_fd, server_id;
  865. int slot = RFIFOB(fd,2);
  866. RFIFOSKIP(fd,3);
  867. ARR_FIND( 0, ARRAYLENGTH(map_server), server_id, session_isValid(map_server[server_id].fd) && !map_server[server_id].maps.empty() );
  868. // Map-server not available, tell the client to wait (client wont close, char select will respawn)
  869. if (server_id == ARRAYLENGTH(map_server)) {
  870. chclif_accessible_maps( fd );
  871. return 1;
  872. }
  873. // Check if the character exists and is not scheduled for deletion
  874. if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d' AND `delete_date` = 0", schema_config.char_db, sd->account_id, slot)
  875. || SQL_SUCCESS != Sql_NextRow(sql_handle)
  876. || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, nullptr) )
  877. { //Not found?? May be forged packet.
  878. Sql_ShowDebug(sql_handle);
  879. Sql_FreeResult(sql_handle);
  880. chclif_reject(fd, 0); // rejected from server
  881. return 1;
  882. }
  883. char_id = atoi(data);
  884. Sql_FreeResult(sql_handle);
  885. // Prevent select a char while retrieving guild bound items
  886. if (sd->flag&1) {
  887. chclif_reject(fd, 0); // rejected from server
  888. return 1;
  889. }
  890. /* client doesn't let it get to this point if you're banned, so its a forged packet */
  891. if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(nullptr) ) {
  892. chclif_reject(fd, 0); // rejected from server
  893. return 1;
  894. }
  895. /* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */
  896. char_set_char_online(-2,char_id,sd->account_id);
  897. if( !char_mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */
  898. char_set_char_offline(char_id, sd->account_id);
  899. /* failed to load something. REJECT! */
  900. chclif_reject(fd, 0); /* jump off this boat */
  901. return 1;
  902. }
  903. //Have to switch over to the DB instance otherwise data won't propagate [Kevin]
  904. std::shared_ptr<struct mmo_charstatus> cd = util::umap_find( char_get_chardb(), char_id );
  905. if (charserv_config.log_char) {
  906. char esc_name[NAME_LENGTH*2+1];
  907. Sql_EscapeStringLen(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH));
  908. if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')",
  909. schema_config.charlog_db, sd->account_id, slot, esc_name) )
  910. Sql_ShowDebug(sql_handle);
  911. }
  912. ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name);
  913. // searching map server
  914. i = char_search_mapserver( cd->last_point.map, -1, -1 );
  915. // if map is not found, we check major cities
  916. if( i < 0 ){
  917. #if PACKETVER >= 20100714
  918. // Let the user select a map
  919. chclif_accessible_maps( fd );
  920. return 0;
  921. #else
  922. // Try to select a map for the user
  923. unsigned short j;
  924. //First check that there's actually a map server online.
  925. ARR_FIND( 0, ARRAYLENGTH(map_server), j, session_isValid(map_server[j].fd) && !map_server[j].maps.empty() );
  926. if (j == ARRAYLENGTH(map_server)) {
  927. ShowInfo("Connection Closed. No map servers available.\n");
  928. chclif_send_auth_result(fd,1); // 01 = Server closed
  929. return 1;
  930. }
  931. for( struct s_point_str& accessible_map : accessible_maps ){
  932. i = char_search_mapserver( accessible_map.map, -1, -1 );
  933. // Found a map-server for a map
  934. if( i >= 0 ){
  935. ShowWarning( "Unable to find map-server for '%s', sending to major city '%s'.\n", cd->last_point.map, accessible_map.map );
  936. memcpy( &cd->last_point, &accessible_map, sizeof( cd->last_point ) );
  937. break;
  938. }
  939. }
  940. if( i < 0 ){
  941. ShowInfo( "Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", cd->last_point.map );
  942. chclif_send_auth_result(fd,1); // 01 = Server closed
  943. return 1;
  944. }
  945. #endif
  946. }
  947. //Send NEW auth packet [Kevin]
  948. //FIXME: is this case even possible? [ultramage]
  949. if ((map_fd = map_server[i].fd) < 1 || session[map_fd] == nullptr)
  950. {
  951. ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
  952. map_server[i] = {};
  953. map_server[i].fd = -1;
  954. chclif_send_auth_result(fd,1); //Send server closed.
  955. return 1;
  956. }
  957. chclif_send_map_data( fd, cd, ipl, i );
  958. // create temporary auth entry
  959. std::shared_ptr<struct auth_node> node = std::make_shared<struct auth_node>();
  960. node->account_id = sd->account_id;
  961. node->char_id = cd->char_id;
  962. node->login_id1 = sd->login_id1;
  963. node->login_id2 = sd->login_id2;
  964. node->sex = sd->sex;
  965. node->expiration_time = sd->expiration_time;
  966. node->group_id = sd->group_id;
  967. node->ip = ipl;
  968. char_get_authdb()[node->account_id] = node;
  969. }
  970. return 1;
  971. }
  972. // S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W
  973. // S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <slot>.B <hair color>.W <hair style>.W
  974. // S 0a39 <name>.24B <slot>.B <hair color>.W <hair style>.W <starting job ID>.W <Unknown>.(W or 2 B's)??? <sex>.B
  975. int chclif_parse_createnewchar(int fd, struct char_session_data* sd,int cmd){
  976. if (cmd == 0xa39) FIFOSD_CHECK(36) //>=20151001
  977. else if (cmd == 0x970) FIFOSD_CHECK(31) //>=20120307
  978. else if (cmd == 0x67) FIFOSD_CHECK(37)
  979. else return 0;
  980. int char_id;
  981. if( (charserv_config.char_new)==0 ) //turn character creation on/off [Kevin]
  982. char_id = -2;
  983. else {
  984. char name[NAME_LENGTH];
  985. int str, agi, vit, int_, dex, luk;
  986. int slot;
  987. int hair_color;
  988. int hair_style;
  989. short start_job;
  990. int sex;
  991. #if PACKETVER >= 20151001
  992. // Sent values
  993. safestrncpy( name, RFIFOCP( fd, 2 ), NAME_LENGTH );
  994. slot = RFIFOB( fd, 26 );
  995. hair_color = RFIFOW( fd, 27 );
  996. hair_style = RFIFOW( fd, 29 );
  997. start_job = RFIFOW( fd, 31 );
  998. // Unknown RFIFOW( fd, 32 )
  999. sex = RFIFOB( fd, 35 );
  1000. // Default values
  1001. str = 1;
  1002. agi = 1;
  1003. vit = 1;
  1004. int_ = 1;
  1005. dex = 1;
  1006. luk = 1;
  1007. RFIFOSKIP( fd, 36 );
  1008. #elif PACKETVER >= 20120307
  1009. // Sent values
  1010. safestrncpy( name, RFIFOCP( fd, 2 ), NAME_LENGTH );
  1011. slot = RFIFOB( fd, 26 );
  1012. hair_color = RFIFOW( fd, 27 );
  1013. hair_style = RFIFOW( fd, 29 );
  1014. // Default values
  1015. str = 1;
  1016. agi = 1;
  1017. vit = 1;
  1018. int_ = 1;
  1019. dex = 1;
  1020. luk = 1;
  1021. start_job = JOB_NOVICE;
  1022. sex = sd->sex;
  1023. RFIFOSKIP( fd, 31 );
  1024. #else
  1025. // Sent values
  1026. safestrncpy( name, RFIFOCP( fd, 2 ), NAME_LENGTH );
  1027. str = RFIFOB( fd, 26 );
  1028. agi = RFIFOB( fd, 27 );
  1029. vit = RFIFOB( fd, 28 );
  1030. int_ = RFIFOB( fd, 29 );
  1031. dex = RFIFOB( fd, 30 );
  1032. luk = RFIFOB( fd, 31 );
  1033. slot = RFIFOB( fd, 32 );
  1034. hair_color = RFIFOW( fd, 33 );
  1035. hair_style = RFIFOW( fd, 35 );
  1036. // Default values
  1037. start_job = JOB_NOVICE;
  1038. sex = sd->sex;
  1039. RFIFOSKIP( fd, 37 );
  1040. #endif
  1041. char_id = char_make_new_char( sd, name, str, agi, vit, int_, dex, luk, slot, hair_color, hair_style, start_job, sex );
  1042. }
  1043. if (char_id < 0) {
  1044. // deny character creation
  1045. WFIFOHEAD(fd,3);
  1046. WFIFOW(fd,0) = 0x6e;
  1047. switch (char_id) {
  1048. // 'Charname already exists' (-1)
  1049. case -1: WFIFOB(fd,2) = 0x00; break;
  1050. // 'Char creation denied' (-2)
  1051. case -2: WFIFOB(fd,2) = 0xFF; break;
  1052. // 'You are underaged' (-3)
  1053. case -3: WFIFOB(fd,2) = 0x01; break;
  1054. // 'You are not elegible to open the Character Slot' (-4)
  1055. case -4: WFIFOB(fd,2) = 0x03; break;
  1056. /* Unused: 0x02 = Symbols in Character Names are forbidden [Ind]*/
  1057. }
  1058. WFIFOSET(fd,3);
  1059. } else {
  1060. int len;
  1061. // retrieve data
  1062. struct mmo_charstatus char_dat;
  1063. char_mmo_char_fromsql(char_id, &char_dat, false); //Only the short data is needed.
  1064. // send to player
  1065. WFIFOHEAD(fd,2+MAX_CHAR_BUF);
  1066. WFIFOW(fd,0) = HEADER_HC_ACCEPT_MAKECHAR;
  1067. len = 2 + char_mmo_char_tobuf(WFIFOP(fd,2), &char_dat);
  1068. WFIFOSET(fd,len);
  1069. // add new entry to the chars list
  1070. sd->found_char[char_dat.slot] = char_id;
  1071. }
  1072. return 1;
  1073. }
  1074. /**
  1075. * Inform client that his deletion request was refused
  1076. * 0x70 <ErrorCode>B HC_REFUSE_DELETECHAR
  1077. * @param fd
  1078. * @param ErrorCode
  1079. * 00 = Incorrect Email address
  1080. * 01 = Invalid Slot
  1081. * 02 = In a party or guild
  1082. */
  1083. void chclif_refuse_delchar(int fd, uint8 errCode){
  1084. WFIFOHEAD(fd,3);
  1085. WFIFOW(fd,0) = 0x70;
  1086. WFIFOB(fd,2) = errCode;
  1087. WFIFOSET(fd,3);
  1088. }
  1089. int chclif_parse_delchar(int fd,struct char_session_data* sd, int cmd){
  1090. if (cmd == 0x68) FIFOSD_CHECK(46)
  1091. else if (cmd == 0x1fb) FIFOSD_CHECK(56)
  1092. else return 0;
  1093. {
  1094. char email[40];
  1095. uint32 cid = RFIFOL(fd,2);
  1096. ShowInfo(CL_RED "Request Char Deletion: " CL_GREEN "%u (%u)" CL_RESET "\n", sd->account_id, cid);
  1097. memcpy(email, RFIFOP(fd,6), 40);
  1098. RFIFOSKIP(fd,( cmd == 0x68) ? 46 : 56);
  1099. if (!chclif_delchar_check(sd, email, charserv_config.char_config.char_del_option)) {
  1100. chclif_refuse_delchar(fd,0); // 00 = Incorrect Email address
  1101. return 1;
  1102. }
  1103. /* Delete character */
  1104. switch( char_delete(sd,cid) ){
  1105. case CHAR_DELETE_OK:
  1106. break;
  1107. case CHAR_DELETE_DATABASE:
  1108. case CHAR_DELETE_BASELEVEL:
  1109. case CHAR_DELETE_TIME:
  1110. chclif_refuse_delchar(fd, 0);
  1111. return 1;
  1112. case CHAR_DELETE_NOTFOUND:
  1113. chclif_refuse_delchar(fd, 1);
  1114. return 1;
  1115. case CHAR_DELETE_GUILD:
  1116. case CHAR_DELETE_PARTY:
  1117. chclif_refuse_delchar(fd, 2);
  1118. return 1;
  1119. }
  1120. /* Char successfully deleted.*/
  1121. WFIFOHEAD(fd,2);
  1122. WFIFOW(fd,0) = 0x6f;
  1123. WFIFOSET(fd,2);
  1124. }
  1125. return 1;
  1126. }
  1127. // R 0187 <account ID>.l
  1128. int chclif_parse_keepalive(int fd){
  1129. if (RFIFOREST(fd) < 6)
  1130. return 0;
  1131. //int aid = RFIFOL(fd,2);
  1132. RFIFOSKIP(fd,6);
  1133. return 1;
  1134. }
  1135. // Tells the client if the name was accepted or not
  1136. // 028e <result>.W (HC_ACK_IS_VALID_CHARNAME)
  1137. // result:
  1138. // 0 = name is not OK
  1139. // 1 = name is OK
  1140. void chclif_reqrename_response( int fd, struct char_session_data* sd, bool name_valid ){
  1141. WFIFOHEAD(fd, 4);
  1142. WFIFOW(fd, 0) = 0x28e;
  1143. WFIFOW(fd, 2) = name_valid;
  1144. WFIFOSET(fd, 4);
  1145. }
  1146. // Request for checking the new name on character renaming
  1147. // 028d <account ID>.l <char ID>.l <new name>.24B (CH_REQ_IS_VALID_CHARNAME)
  1148. int chclif_parse_reqrename( int fd, struct char_session_data* sd ){
  1149. FIFOSD_CHECK(34);
  1150. int i, cid, aid;
  1151. char name[NAME_LENGTH];
  1152. char esc_name[NAME_LENGTH*2+1];
  1153. aid = RFIFOL(fd,2);
  1154. cid = RFIFOL(fd,6);
  1155. safestrncpy(name, RFIFOCP(fd,10), NAME_LENGTH);
  1156. RFIFOSKIP(fd,34);
  1157. if( aid != sd->account_id )
  1158. return 1;
  1159. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  1160. if( i == MAX_CHARS )
  1161. return 1;
  1162. normalize_name(name,TRIM_CHARS);
  1163. Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  1164. if( !char_check_char_name(name,esc_name) ) {
  1165. i = 1;
  1166. safestrncpy(sd->new_name, name, NAME_LENGTH);
  1167. }
  1168. else
  1169. i = 0;
  1170. chclif_reqrename_response(fd, sd, i > 0);
  1171. return 1;
  1172. }
  1173. TIMER_FUNC(charblock_timer){
  1174. struct char_session_data* sd=nullptr;
  1175. int i=0;
  1176. ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == id);
  1177. if(sd == nullptr || sd->charblock_timer==INVALID_TIMER) //has disconected or was required to stop
  1178. return 0;
  1179. if (sd->charblock_timer != tid){
  1180. sd->charblock_timer = INVALID_TIMER;
  1181. return 0;
  1182. }
  1183. chclif_block_character(i,sd);
  1184. return 0;
  1185. }
  1186. /*
  1187. * 0x20d <PacketLength>.W <TAG_CHARACTER_BLOCK_INFO>24B (HC_BLOCK_CHARACTER)
  1188. * <GID>L <szExpireDate>20B (TAG_CHARACTER_BLOCK_INFO)
  1189. */
  1190. void chclif_block_character( int fd, struct char_session_data* sd){
  1191. int i=0, j=0, len=4;
  1192. time_t now = time(nullptr);
  1193. WFIFOHEAD(fd, 4+MAX_CHARS*24);
  1194. WFIFOW(fd, 0) = 0x20d;
  1195. for(i=0; i<MAX_CHARS; i++){
  1196. if(sd->found_char[i] == -1)
  1197. continue;
  1198. if(sd->unban_time[i]){
  1199. if( sd->unban_time[i] > now ) {
  1200. char szExpireDate[21];
  1201. WFIFOL(fd, 4+j*24) = sd->found_char[i];
  1202. timestamp2string(szExpireDate, 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S");
  1203. memcpy(WFIFOP(fd,8+j*24),szExpireDate,20);
  1204. }
  1205. else {
  1206. WFIFOL(fd, 4+j*24) = 0;
  1207. sd->unban_time[i] = 0;
  1208. if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `unban_time`='0' WHERE `char_id`='%d' LIMIT 1", schema_config.char_db, sd->found_char[i]) )
  1209. Sql_ShowDebug(sql_handle);
  1210. }
  1211. len+=24;
  1212. j++; //pkt list idx
  1213. }
  1214. }
  1215. WFIFOW(fd, 2) = len; //packet len
  1216. WFIFOSET(fd,len);
  1217. ARR_FIND(0, MAX_CHARS, i, sd->unban_time[i] > now); //sd->charslot only have productible char
  1218. if(i < MAX_CHARS ){
  1219. sd->charblock_timer = add_timer(
  1220. gettick() + 10000, // each 10s resend that list
  1221. charblock_timer, sd->account_id, 0);
  1222. }
  1223. }
  1224. // Sends the response to a rename request to the client.
  1225. // 0290 <result>.W (HC_ACK_CHANGE_CHARNAME)
  1226. // 08fd <result>.L (HC_ACK_CHANGE_CHARACTERNAME)
  1227. // result:
  1228. // 0: Successful
  1229. // 1: This character's name has already been changed. You cannot change a character's name more than once.
  1230. // 2: User information is not correct.
  1231. // 3: You have failed to change this character's name.
  1232. // 4: Another user is using this character name, so please select another one.
  1233. // 5: In order to change the character name, you must leave the guild.
  1234. // 6: In order to change the character name, you must leave the party.
  1235. // 7: Length exceeds the maximum size of the character name you want to change.
  1236. // 8: Name contains invalid characters. Character name change failed.
  1237. // 9: The name change is prohibited. Character name change failed.
  1238. // 10: Character name change failed, due an unknown error.
  1239. void chclif_rename_response(int fd, struct char_session_data* sd, int16 response) {
  1240. #if PACKETVER >= 20111101
  1241. WFIFOHEAD(fd, 6);
  1242. WFIFOW(fd, 0) = 0x8fd;
  1243. WFIFOL(fd, 2) = response;
  1244. WFIFOSET(fd, 6);
  1245. #else
  1246. WFIFOHEAD(fd, 4);
  1247. WFIFOW(fd, 0) = 0x290;
  1248. WFIFOW(fd, 2) = response;
  1249. WFIFOSET(fd, 4);
  1250. #endif
  1251. }
  1252. // Request to change a character name
  1253. // 028f <char_id>.L (CH_REQ_CHANGE_CHARNAME)
  1254. // 08fc <char_id>.L <new name>.24B (CH_REQ_CHANGE_CHARACTERNAME)
  1255. int chclif_parse_ackrename(int fd, struct char_session_data* sd){
  1256. #if PACKETVER >= 20111101
  1257. FIFOSD_CHECK(30)
  1258. {
  1259. int i, cid;
  1260. char name[NAME_LENGTH], esc_name[NAME_LENGTH * 2 + 1];
  1261. cid = RFIFOL(fd, 2);
  1262. safestrncpy(name, RFIFOCP(fd, 6), NAME_LENGTH);
  1263. RFIFOSKIP(fd, 30);
  1264. ARR_FIND(0, MAX_CHARS, i, sd->found_char[i] == cid);
  1265. if (i == MAX_CHARS)
  1266. return 1;
  1267. normalize_name(name, TRIM_CHARS);
  1268. Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  1269. safestrncpy(sd->new_name, name, NAME_LENGTH);
  1270. // Start the renaming process
  1271. i = char_rename_char_sql(sd, cid);
  1272. chclif_rename_response(fd, sd, i);
  1273. // If the renaming was successful, we need to resend the characters
  1274. if( i == 0 )
  1275. chclif_mmo_char_send(fd, sd);
  1276. return 1;
  1277. }
  1278. #else
  1279. FIFOSD_CHECK(6)
  1280. {
  1281. int i;
  1282. int cid = RFIFOL(fd,2);
  1283. RFIFOSKIP(fd,6);
  1284. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  1285. if( i == MAX_CHARS )
  1286. return 1;
  1287. i = char_rename_char_sql(sd, cid);
  1288. chclif_rename_response(fd, sd, i);
  1289. }
  1290. return 1;
  1291. #endif
  1292. }
  1293. int chclif_ack_captcha(int fd){
  1294. WFIFOHEAD(fd,5);
  1295. WFIFOW(fd,0) = 0x7e9;
  1296. WFIFOW(fd,2) = 5;
  1297. WFIFOB(fd,4) = 1;
  1298. WFIFOSET(fd,5);
  1299. return 1;
  1300. }
  1301. // R 06C <ErrorCode>B HEADER_HC_REFUSE_ENTER
  1302. void chclif_reject(int fd, uint8 errCode){
  1303. WFIFOHEAD(fd,3);
  1304. WFIFOW(fd,0) = 0x6c;
  1305. WFIFOB(fd,2) = errCode;// rejected from server
  1306. WFIFOSET(fd,3);
  1307. }
  1308. // R 07e5 <?>.w <aid>.l
  1309. int chclif_parse_reqcaptcha(int fd){
  1310. //FIFOSD_CHECK(8)
  1311. RFIFOSKIP(fd,8);
  1312. chclif_ack_captcha(fd);
  1313. return 1;
  1314. }
  1315. // R 07e7 <len>.w <aid>.l <code>.b10 <?>.b14
  1316. int chclif_parse_chkcaptcha(int fd){
  1317. //FIFOSD_CHECK(32)
  1318. RFIFOSKIP(fd,32);
  1319. chclif_ack_captcha(fd);
  1320. return 1;
  1321. }
  1322. /**
  1323. * Entry point from client to char-serv
  1324. * function that check incoming command then split it to correct handler.
  1325. * @param fd: file descriptor to parse, (link to client)
  1326. */
  1327. int chclif_parse(int fd) {
  1328. struct char_session_data* sd = (struct char_session_data*)session[fd]->session_data;
  1329. uint32 ipl = session[fd]->client_addr;
  1330. // disconnect any player if no login-server.
  1331. if(login_fd < 0)
  1332. set_eof(fd);
  1333. if(session[fd]->flag.eof) {
  1334. if( sd != nullptr && sd->auth ) { // already authed client
  1335. std::shared_ptr<struct online_char_data> data = util::umap_find( char_get_onlinedb(), sd->account_id );
  1336. if( data != nullptr && data->fd == fd ){
  1337. data->fd = -1;
  1338. }
  1339. // If it is not in any server, send it offline. [Skotlex]
  1340. if( data == nullptr || data->server == -1 ){
  1341. char_set_char_offline(-1,sd->account_id);
  1342. }
  1343. }
  1344. do_close(fd);
  1345. return 0;
  1346. }
  1347. while( RFIFOREST(fd) >= 2 ) {
  1348. int next = 1;
  1349. unsigned short cmd;
  1350. cmd = RFIFOW(fd,0);
  1351. #if PACKETVER_SUPPORTS_PINCODE
  1352. // If the pincode system is enabled
  1353. if( charserv_config.pincode_config.pincode_enabled ){
  1354. switch( cmd ){
  1355. // Connect of player
  1356. case 0x65:
  1357. // Client keep-alive packet (every 12 seconds)
  1358. case 0x187:
  1359. // Checks the entered pin
  1360. case 0x8b8:
  1361. // Request PIN change
  1362. case 0x8be:
  1363. // Request for PIN window
  1364. case 0x8c5:
  1365. // Request character list
  1366. case 0x9a1:
  1367. // Connect of map-server
  1368. case 0x2af8:
  1369. break;
  1370. // Before processing any other packets, do a few checks
  1371. default:
  1372. // To reach this block the client should have attained a session already
  1373. if( sd != nullptr ){
  1374. // If the pincode was entered correctly
  1375. if( sd->pincode_correct ){
  1376. break;
  1377. }
  1378. // If no pincode is set (yet)
  1379. if( strlen( sd->pincode ) <= 0 ){
  1380. break;
  1381. }
  1382. // The pincode was not entered correctly, yet the player (=bot) tried to send a different packet => Goodbye!
  1383. set_eof( fd );
  1384. return 0;
  1385. }else{
  1386. // Unknown packet received
  1387. ShowError( "chclif_parse: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", cmd, ip2str( ipl, nullptr ) );
  1388. set_eof( fd );
  1389. return 0;
  1390. }
  1391. }
  1392. }
  1393. #endif
  1394. switch( cmd ) {
  1395. case 0x65: next=chclif_parse_reqtoconnect(fd,sd,ipl); break;
  1396. // char select
  1397. case 0x66: next=chclif_parse_charselect(fd,sd,ipl); break;
  1398. // createnewchar
  1399. case 0xa39:
  1400. case 0x970:
  1401. case 0x67: next=chclif_parse_createnewchar(fd,sd,cmd); break;
  1402. // delete char
  1403. case 0x68: next=chclif_parse_delchar(fd,sd,cmd); break; //
  1404. case 0x1fb: next=chclif_parse_delchar(fd,sd,cmd); break; // 2004-04-19aSakexe+ langtype 12 char deletion packet
  1405. // client keep-alive packet (every 12 seconds)
  1406. case 0x187: next=chclif_parse_keepalive(fd); break;
  1407. // char rename
  1408. case 0x28d: next=chclif_parse_reqrename(fd,sd); break; //Check new desired name
  1409. case 0x28f: next=chclif_parse_ackrename(fd,sd); break; //Confirm change name.
  1410. case 0x8fc: next=chclif_parse_ackrename(fd,sd); break; //Rename request without confirmation
  1411. // captcha
  1412. case 0x7e5: next=chclif_parse_reqcaptcha(fd); break; // captcha code request (not implemented)
  1413. case 0x7e7: next=chclif_parse_chkcaptcha(fd); break; // captcha code check (not implemented)
  1414. // deletion timer request
  1415. case 0x827: next=chclif_parse_char_delete2_req(fd, sd); break;
  1416. // deletion accept request
  1417. case 0x829: next=chclif_parse_char_delete2_accept(fd, sd); break;
  1418. // deletion cancel request
  1419. case 0x82b: next=chclif_parse_char_delete2_cancel(fd, sd); break;
  1420. // login as map-server
  1421. case 0x2af8: chclif_parse_maplogin(fd); return 0; // avoid processing of followup packets here
  1422. #if PACKETVER_SUPPORTS_PINCODE
  1423. //pincode
  1424. case 0x8b8: next=chclif_parse_pincode_check( fd, sd ); break; // checks the entered pin
  1425. case 0x8c5: next=chclif_parse_reqpincode_window(fd,sd); break; // request for PIN window
  1426. case 0x8be: next=chclif_parse_pincode_change( fd, sd ); break; // pincode change request
  1427. case 0x8ba: next=chclif_parse_pincode_setnew( fd, sd ); break; // activate PIN system and set first PIN
  1428. #endif
  1429. // character movement request
  1430. case 0x8d4: next=chclif_parse_moveCharSlot(fd,sd); break;
  1431. case 0x9a1: next=chclif_parse_req_charlist(fd,sd); break;
  1432. case HEADER_CH_SELECT_ACCESSIBLE_MAPNAME:
  1433. next = chclif_parse_select_accessible_map( fd, sd, ipl );
  1434. break;
  1435. // unknown packet received
  1436. default:
  1437. ShowError( "chclif_parse: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", cmd, ip2str( ipl, nullptr ) );
  1438. set_eof(fd);
  1439. return 0;
  1440. }
  1441. if (next == 0)
  1442. return 0; // avoid processing of followup packets (prev was probably incomplete)
  1443. }
  1444. RFIFOFLUSH(fd);
  1445. return 0;
  1446. }