Dan10.inc 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. /**--------------------------------------------------------------------------**\
  2. ===================================
  3. y_users - Registration functions.
  4. ===================================
  5. Description:
  6. Provides access to a user system for registering and saving users.
  7. Legal:
  8. Version: MPL 1.1
  9. The contents of this file are subject to the Mozilla Public License Version
  10. 1.1 (the "License"); you may not use this file except in compliance with
  11. the License. You may obtain a copy of the License at
  12. http://www.mozilla.org/MPL/
  13. Software distributed under the License is distributed on an "AS IS" basis,
  14. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. for the specific language governing rights and limitations under the
  16. License.
  17. The Original Code is the YSI malloc include.
  18. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  19. Portions created by the Initial Developer are Copyright (C) 2011
  20. the Initial Developer. All Rights Reserved.
  21. Contributors:
  22. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  23. Thanks:
  24. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  25. ZeeX - Very productive conversations.
  26. koolk - IsPlayerinAreaEx code.
  27. TheAlpha - Danish translation.
  28. breadfish - German translation.
  29. Fireburn - Dutch translation.
  30. yom - French translation.
  31. 50p - Polish translation.
  32. Zamaroht - Spanish translation.
  33. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  34. for me to strive to better.
  35. Pixels^ - Running XScripters where the idea was born.
  36. Matite - Pestering me to release it and using it.
  37. Very special thanks to:
  38. Thiadmer - PAWN, whose limits continue to amaze me!
  39. Kye/Kalcor - SA:MP.
  40. SA:MP Team past, present and future - SA:MP.
  41. Version:
  42. 2.2
  43. Changelog:
  44. 02/02/13:
  45. Added bits and join timestamp to preload data.
  46. Added backwards compatible salting to the hash.
  47. 15/11/11:
  48. Added comments.
  49. Added language to the top-level player data (needed for login).
  50. 11/11/11:
  51. First version.
  52. Functions:
  53. Public
  54. -
  55. Core:
  56. -
  57. Stock:
  58. -
  59. Static:
  60. -
  61. Inline:
  62. -
  63. API:
  64. -
  65. Callbacks:
  66. -
  67. Definitions:
  68. -
  69. Enums:
  70. -
  71. Macros:
  72. -
  73. Tags:
  74. -
  75. Variables:
  76. Global:
  77. -
  78. Static:
  79. -
  80. Commands:
  81. -
  82. Compile options:
  83. -
  84. Operators:
  85. -
  86. \**--------------------------------------------------------------------------**/
  87. #if !defined Y_USERS_SQL_HOST
  88. #define Y_USERS_SQL_HOST GetUsersSQLHost()
  89. #endif
  90. #if !defined Y_USERS_SQL_READ_USER
  91. #define Y_USERS_SQL_READ_USER GetUsersSQLReadUser()
  92. #endif
  93. #if !defined Y_USERS_SQL_READ_PASS
  94. #define Y_USERS_SQL_READ_PASS GetUsersSQLReadPass()
  95. #endif
  96. #if !defined Y_USERS_SQL_DB
  97. #define Y_USERS_SQL_DB GetUsersSQLDB()
  98. #endif
  99. #if !defined Y_USERS_SQL_PORT
  100. #define Y_USERS_SQL_PORT GetUsersSQLPort()
  101. #endif
  102. #if !defined Y_USERS_SQL_WRITE_USER
  103. #define Y_USERS_SQL_WRITE_USER Y_USERS_SQL_READ_USER
  104. #endif
  105. #if !defined Y_USERS_SQL_WRITE_PASS
  106. #define Y_USERS_SQL_WRITE_PASS Y_USERS_SQL_READ_PASS
  107. #endif
  108. #if !defined Y_USERS_SQL_MAKE_USER
  109. #define Y_USERS_SQL_MAKE_USER Y_USERS_SQL_READ_USER
  110. #endif
  111. #if !defined Y_USERS_SQL_MAKE_PASS
  112. #define Y_USERS_SQL_MAKE_PASS Y_USERS_SQL_READ_PASS
  113. #endif
  114. loadtext core[ysi_players];
  115. enum E_USER_PRELOAD
  116. {
  117. E_USER_PRELOAD_YID,
  118. Language:E_USER_PRELOAD_LANG,
  119. E_USER_PRELOAD_PASS[MAX_PASSWORD_LENGTH + 1],
  120. E_USER_PRELOAD_BITS,
  121. /*E_USER_PRELOAD_PLAY_TIME,
  122. E_USER_PRELOAD_LAST_IP,
  123. E_USER_PRELOAD_LAST_LOGIN,*/
  124. E_USER_PRELOAD_DATE
  125. }
  126. static stock
  127. YSI_g_sCurrentYID,
  128. YSI_g_sModeNameLength,
  129. YSI_g_sSQLBuffer[2048],
  130. YSI_g_sSQLQuery[256],
  131. SQL:YSI_g_sPreloadHandle,
  132. SQL:YSI_g_sLoadHandle,
  133. SQL:YSI_g_sWriteHandle,
  134. YSI_g_sPlayerYID[MAX_PLAYERS] = {-2, ...},
  135. YSI_g_sPreloadData[MAX_PLAYERS][E_USER_PRELOAD];
  136. forward y_users_SQLLoaded(playerid, uid, Result:r);
  137. forward y_users_SQLCallback(id, Result:r);
  138. /**--------------------------------------------------------------------------**\
  139. <remarks>
  140. This code loads the settings for accessing your database from a file.
  141. </remarks>
  142. \**--------------------------------------------------------------------------**/
  143. #define GetUsersSQLHost() (Player_LoadDBSettings(),YSI_g_sUsersSQLHost)
  144. #define GetUsersSQLReadUser() (Player_LoadDBSettings(),YSI_g_sUsersSQLReadUser)
  145. #define GetUsersSQLReadPass() (Player_LoadDBSettings(),YSI_g_sUsersSQLReadPass)
  146. #define GetUsersSQLDB() (Player_LoadDBSettings(),YSI_g_sUsersSQLDB)
  147. #define GetUsersSQLPort() (Player_LoadDBSettings(),YSI_g_sUsersSQLPort)
  148. static stock
  149. YSI_g_sUsersSQLHost[16] = "localhost",
  150. YSI_g_sUsersSQLReadUser[16] = "y_users_make",
  151. YSI_g_sUsersSQLReadPass[16] = "y_users_make",
  152. YSI_g_sUsersSQLDB[16] = "y_users",
  153. YSI_g_sUsersSQLPort = 3306;
  154. INI:YSI[y_users](name[], value[])
  155. {
  156. INI_String("Host", YSI_g_sUsersSQLHost, sizeof (YSI_g_sUsersSQLHost));
  157. INI_String("User", YSI_g_sUsersSQLReadUser, sizeof (YSI_g_sUsersSQLReadUser));
  158. INI_String("Pass", YSI_g_sUsersSQLReadPass, sizeof (YSI_g_sUsersSQLReadPass));
  159. INI_String("DB", YSI_g_sUsersSQLDB, sizeof (YSI_g_sUsersSQLDB));
  160. INI_Int("Port", YSI_g_sUsersSQLPort);
  161. return 0;
  162. }
  163. static stock Player_LoadDBSettings()
  164. {
  165. static
  166. bool:sLoad = true;
  167. if (sLoad)
  168. {
  169. INI_Load("YSI.ini");
  170. sLoad = false;
  171. }
  172. }
  173. /**--------------------------------------------------------------------------**\
  174. <summary>Player_Reload</summary>
  175. <param name="playerid">Player who changed name.</param>
  176. <returns>
  177. -
  178. </returns>
  179. <remarks>
  180. Reload a player's basic data when they change name.
  181. </remarks>
  182. \**--------------------------------------------------------------------------**/
  183. stock Player_Reload(playerid)
  184. {
  185. new
  186. name[MAX_PLAYER_NAME];
  187. GetPlayerName(playerid, name, sizeof (name)),
  188. Player_Preload(name, YSI_g_sPreloadData[playerid]);
  189. }
  190. stock Player_SetPreload(playerid, data[E_USER_PRELOAD])
  191. {
  192. if (VALID_PLAYERID(playerid))
  193. {
  194. YSI_g_sPreloadData[playerid] = data;
  195. }
  196. }
  197. /**--------------------------------------------------------------------------**\
  198. <summary>Player_Preload</summary>
  199. <param name="playerid">Player who is logging in.</param>
  200. <returns>
  201. -
  202. </returns>
  203. <remarks>
  204. Loads a player's data to an array.
  205. </remarks>
  206. \**--------------------------------------------------------------------------**/
  207. stock Player_Preload(string:name[], ret[E_USER_PRELOAD])
  208. {
  209. // First, find the player's file. This should be the ONLY place where the
  210. // password is to be loaded.
  211. P:4("Player_Preload called: %s", name);
  212. ret[E_USER_PRELOAD_YID] = -1,
  213. ret[E_USER_PRELOAD_PASS] = '\0',
  214. ret[E_USER_PRELOAD_LANG] = NO_LANGUAGE,
  215. ret[E_USER_PRELOAD_BITS] = 0,
  216. ret[E_USER_PRELOAD_DATE] = 0;
  217. new
  218. safeName[MAX_PLAYER_NAME];
  219. sql_escape_string(YSI_g_sPreloadHandle, name, safeName),
  220. // I don't normally like "SELECT *" but it is appropriate here.
  221. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery), "SELECT yp.* FROM y_users_preload AS yp JOIN y_users_users WHERE name='%s';", safeName);
  222. new
  223. Result:r = sql_query(YSI_g_sPreloadHandle, YSI_g_sSQLQuery);
  224. if (sql_error(r))
  225. {
  226. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  227. return
  228. sql_error_string(r, YSI_g_sSQLQuery),
  229. sql_free_result(r),
  230. 0;
  231. }
  232. switch (sql_num_rows(r))
  233. {
  234. case 0:
  235. {
  236. ret[E_USER_PRELOAD_YID] = 0;
  237. }
  238. case 1:
  239. {
  240. // YID.
  241. sql_get_field(r, 0, YSI_g_sSQLQuery),
  242. ret[E_USER_PRELOAD_YID] = strval(YSI_g_sSQLQuery),
  243. // Hash.
  244. sql_get_field(r, 1, YSI_g_sSQLQuery),
  245. strcat(ret[E_USER_PRELOAD_PASS], YSI_g_sSQLQuery, MAX_PASSWORD_LENGTH + 1),
  246. // Language
  247. sql_get_field(r, 2, YSI_g_sSQLQuery),
  248. ret[E_USER_PRELOAD_LANG] = Langs_GetLanguage(YSI_g_sSQLQuery),
  249. // Load the 32 extra "bits".
  250. sql_get_field(r, 3, YSI_g_sSQLQuery),
  251. ret[E_USER_PRELOAD_BITS] = strval(YSI_g_sSQLQuery),
  252. // Load the user registration date (if they have one).
  253. sql_get_field(r, 4, YSI_g_sSQLQuery),
  254. ret[E_USER_PRELOAD_DATE] = strval(YSI_g_sSQLQuery);
  255. P:6("Player_Preload: %s %d %d %x %x", ret[E_USER_PRELOAD_PASS], ret[E_USER_PRELOAD_YID], _:ret[E_USER_PRELOAD_LANG], ret[E_USER_PRELOAD_BITS], ret[E_USER_PRELOAD_DATE]);
  256. // Load their total play time.
  257. // Load their last IP.
  258. // Load their last login.
  259. }
  260. default:
  261. {
  262. P:E("y_users found mutliple YID results for %s", name);
  263. }
  264. }
  265. return
  266. sql_free_result(r),
  267. (ret[E_USER_PRELOAD_YID] > 0);
  268. }
  269. stock Language:Player_GetPreloadLanguage(playerid)
  270. {
  271. return Language:YSI_g_sPreloadData[playerid][E_USER_PRELOAD_LANG];
  272. }
  273. stock Player_GetPreloadBits(playerid)
  274. {
  275. return YSI_g_sPreloadData[playerid][E_USER_PRELOAD_BITS];
  276. }
  277. static remotefunc void:_Player_SetPreloadBits(playerid, bits)
  278. {
  279. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_BITS] = bits;
  280. }
  281. stock Player_SetPreloadBits(playerid, bits)
  282. {
  283. broadcastfunc _Player_SetPreloadBits(playerid, bits);
  284. Player_RewritePreload(playerid);
  285. }
  286. stock Player_GetPreloadDate(playerid)
  287. {
  288. if (0 <= YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] < 1234567890)
  289. {
  290. return -1;
  291. }
  292. return YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE];
  293. }
  294. stock Player_IsRegistered(playerid)
  295. {
  296. return YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID] > 0;
  297. }
  298. hook OnScriptInit()
  299. {
  300. sql_debug(LOG_NONE, LOG_ALL);
  301. #define SQL_CLEANUP(%0) if (sql_error((%0))){sql_error_string((%0),error),P:E("SQL Error: %s",error);return sql_free_result((%0)),sql_disconnect(mkH),0;}sql_free_result((%0))
  302. YSI_g_sModeNameLength = strlen(MODE_NAME),
  303. YSI_g_sPreloadHandle = sql_connect(SQL_HANDLER_MYSQL, Y_USERS_SQL_HOST, Y_USERS_SQL_READ_USER, Y_USERS_SQL_READ_PASS, Y_USERS_SQL_DB),
  304. P:5("Users_OnScriptInit: YSI_g_sPreloadHandle = %d", _:YSI_g_sPreloadHandle);
  305. YSI_g_sLoadHandle = sql_connect(SQL_HANDLER_MYSQL, Y_USERS_SQL_HOST, Y_USERS_SQL_READ_USER, Y_USERS_SQL_READ_PASS, Y_USERS_SQL_DB),
  306. P:5("Users_OnScriptInit: YSI_g_sLoadHandle = %d", _:YSI_g_sLoadHandle);
  307. YSI_g_sWriteHandle = sql_connect(SQL_HANDLER_MYSQL, Y_USERS_SQL_HOST, Y_USERS_SQL_WRITE_USER, Y_USERS_SQL_WRITE_PASS, Y_USERS_SQL_DB);
  308. P:5("Users_OnScriptInit: YSI_g_sWriteHandle = %d", _:YSI_g_sWriteHandle);
  309. // There are two main tables.
  310. new
  311. SQL:mkH = sql_connect(SQL_HANDLER_MYSQL, Y_USERS_SQL_HOST, Y_USERS_SQL_MAKE_USER, Y_USERS_SQL_MAKE_PASS, Y_USERS_SQL_DB),
  312. error[64],
  313. // Create the table for storing users.
  314. Result:r = sql_query(mkH,
  315. "CREATE TABLE IF NOT EXISTS y_users_preload (" \
  316. "yid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY," \
  317. "password_hash CHAR(128), language CHAR(2)," \
  318. "options INT UNSIGNED, registered INT UNSIGNED," \
  319. "play_time INT UNSIGNED, last_ip INT UNSIGNED," \
  320. "last_seen DATETIME" \
  321. ") ENGINE InnoDB;");
  322. P:5("Users_OnScriptInit: mkH = %d", _:mkH);
  323. // "options" was "BIT(32)".
  324. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  325. SQL_CLEANUP(r);
  326. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  327. // Create the table for storing tags.
  328. r = sql_query(mkH,
  329. "CREATE TABLE IF NOT EXISTS y_users_modes (" \
  330. "tagid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY," \
  331. "mode VARCHAR(32) NOT NULL," \
  332. "tag VARCHAR(32)," \
  333. "INDEX (mode) USING HASH," \
  334. "UNIQUE KEY (mode, tag)" \
  335. ") ENGINE InnoDB;");
  336. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  337. SQL_CLEANUP(r);
  338. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  339. // Create the table to map names to YIDs.
  340. r = sql_query(mkH,
  341. "CREATE TABLE IF NOT EXISTS y_users_users (" \
  342. "name VARCHAR(24) NOT NULL PRIMARY KEY," \
  343. "yid INT UNSIGNED," \
  344. "CONSTRAINT FOREIGN KEY (yid)" \
  345. "REFERENCES y_users_preload (yid) ON DELETE CASCADE," \
  346. "INDEX (name) USING HASH" \
  347. ") ENGINE InnoDB;");
  348. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  349. SQL_CLEANUP(r);
  350. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  351. // Create the table for storing key/value pairs.
  352. r = sql_query(mkH,
  353. "CREATE TABLE IF NOT EXISTS y_users_keystore (" \
  354. "yid INT UNSIGNED NOT NULL," \
  355. "tagid INT UNSIGNED NOT NULL," \
  356. "vkey VARCHAR(32) NOT NULL," \
  357. "value VARCHAR(196)," \
  358. "CONSTRAINT FOREIGN KEY (tagid) REFERENCES y_users_modes (tagid) ON DELETE CASCADE," \
  359. "CONSTRAINT FOREIGN KEY (yid) REFERENCES y_users_preload (yid) ON DELETE CASCADE," \
  360. "UNIQUE KEY (yid, tagid, vkey), INDEX (yid)" \
  361. ") ENGINE InnoDB;");
  362. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  363. SQL_CLEANUP(r);
  364. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  365. // Create the table to map names to IPs.
  366. r = sql_query(mkH,
  367. "CREATE TABLE IF NOT EXISTS y_users_name_ips (" \
  368. "row BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY," \
  369. "name VARCHAR(24) NOT NULL," \
  370. "one_ip INT UNSIGNED," \
  371. "validated BOOL," \
  372. "registered DATETIME," \
  373. "leave_time DATETIME" \
  374. ") ENGINE InnoDB;");
  375. // "validated" means that this name/IP combination logged in and was
  376. // confirmed to be a valid connecting user, not someone trying to spoof a
  377. // login.
  378. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  379. SQL_CLEANUP(r);
  380. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  381. // Create the table to map YIDs to IPs.
  382. r = sql_query(mkH,
  383. "CREATE TABLE IF NOT EXISTS y_users_yid_ips (" \
  384. "row BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY," \
  385. "yid INT UNSIGNED NOT NULL," \
  386. "one_ip INT UNSIGNED," \
  387. "registered DATETIME," \
  388. "leave_time DATETIME" \
  389. ") ENGINE InnoDB;");
  390. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  391. SQL_CLEANUP(r);
  392. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  393. // Create the table to store failed logins.
  394. r = sql_query(mkH,
  395. "CREATE TABLE IF NOT EXISTS y_users_fails (" \
  396. "row BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY," \
  397. "try_yid INT UNSIGNED NOT NULL," \
  398. "from_name VARCHAR(24)," \
  399. "from_ip INT UNSIGNED," \
  400. "validated BOOL," \
  401. "attempt_time DATETIME" \
  402. ") ENGINE InnoDB;");
  403. #pragma tabsize 0 // BAD BAD BAD! HACK HACK HACK!
  404. SQL_CLEANUP(r);
  405. #pragma tabsize 4 // RESTORE FROM THE UGLINESS!
  406. sql_disconnect(mkH);
  407. return 1;
  408. #undef SQL_CLEANUP
  409. }
  410. hook OnScriptExit()
  411. {
  412. // Loop through all players.
  413. foreach (new i : Player)
  414. {
  415. Player_DoDisconnect(i);
  416. }
  417. sql_disconnect(YSI_g_sPreloadHandle),
  418. sql_disconnect(YSI_g_sLoadHandle),
  419. sql_disconnect(YSI_g_sWriteHandle);
  420. }
  421. hook OnPlayerConnect(playerid)
  422. {
  423. P:1("Users_OnPlayerConnect called: %d", playerid);
  424. new
  425. name[MAX_PLAYER_NAME];
  426. GetPlayerName(playerid, name, sizeof (name)),
  427. YSI_g_sPlayerYID[playerid] = 0,
  428. // -1 means unknown.
  429. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID] = -1,
  430. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS] = '\0',
  431. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_LANG] = NO_LANGUAGE,
  432. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_BITS] = 0,
  433. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] = 0;
  434. broadcastfunc _Player_IsLoggedIn(playerid);
  435. if (existproperty(8, YSIM_LOG_IN))
  436. {
  437. new
  438. uid = getproperty(8, YSIM_LOG_IN);
  439. P:5("Users_OnPlayerConnect: Exists %d", uid);
  440. // We need the preload data even if they're already logged in.
  441. if (uid)
  442. {
  443. Player_Preload(name, YSI_g_sPreloadData[playerid]);
  444. if (uid > 0)
  445. {
  446. // This DOES NOT use "broadcastfunc" as it's local only.
  447. Player_DoLogin(playerid, uid);
  448. }
  449. }
  450. deleteproperty(8, YSIM_LOG_IN);
  451. }
  452. else
  453. {
  454. new
  455. safeName[MAX_PLAYER_NAME],
  456. ip = GetIP(playerid);
  457. // Write this connection.
  458. sql_escape_string(YSI_g_sWriteHandle, name, safeName),
  459. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  460. "INSERT INTO y_users_name_ips VALUES (DEFAULT, '%s', 0x%04x%04x, 0, NOW(), 0)",
  461. safeName,
  462. ip >>> 16,
  463. ip & 0xFFFF),
  464. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  465. P:5("Users_OnPlayerConnect: Doesn't exist");
  466. Player_Preload(name, YSI_g_sPreloadData[playerid]);
  467. P:5("Users_OnPlayerConnect: Done Preload");
  468. // Can do checking in here to see if they just rejoined.
  469. }
  470. return 1;
  471. }
  472. /**--------------------------------------------------------------------------**\
  473. <summary>_Player_IsLoggedIn</summary>
  474. <param name="playerid">Player to check.</param>
  475. <returns>
  476. -1 - Registered but not logged in.
  477. 0 - Not registered.
  478. 1+ - Logged in, and their YID.
  479. </returns>
  480. <remarks>
  481. This is a remote function called in existing scripts when a new script
  482. starts. If the player is logged in it returns their YID. If a player is
  483. registered but not logged in it returns "-1". Otherwise it returns 0.
  484. </remarks>
  485. \**--------------------------------------------------------------------------**/
  486. static remotefunc void:_Player_IsLoggedIn(playerid)
  487. {
  488. P:4("_Player_IsLoggedIn called: %d %d", playerid, YSI_g_sPlayerYID[playerid]);
  489. if (YSI_g_sPlayerYID[playerid] > 0)
  490. {
  491. setproperty(8, YSIM_LOG_IN, YSI_g_sPlayerYID[playerid]);
  492. }
  493. else if (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID] > 0)
  494. {
  495. setproperty(8, YSIM_LOG_IN, -1);
  496. }
  497. else
  498. {
  499. setproperty(8, YSIM_LOG_IN, 0);
  500. }
  501. }
  502. /**--------------------------------------------------------------------------**\
  503. <summary>OnPlayerDisconnect</summary>
  504. <param name="playerid">Player who left.</param>
  505. <param name="reason">Why they left.</param>
  506. <returns>
  507. -
  508. </returns>
  509. <remarks>
  510. Logs the player out if they're logged in.
  511. </remarks>
  512. \**--------------------------------------------------------------------------**/
  513. hook OnPlayerDisconnect(playerid, reason)
  514. {
  515. Player_DoDisconnect(playerid);
  516. }
  517. static stock Player_DoDisconnect(playerid)
  518. {
  519. new
  520. yid = YSI_g_sPlayerYID[playerid];
  521. P:2("Users_OnPlayerDisconnect called: %d %d %d", playerid, yid);
  522. if (yid > 0)
  523. {
  524. // DO NOT broadcastfunc this in case it's just because of one script being
  525. // unloaded, not the player actually leaving (and thus do everything in
  526. // different scripts separately).
  527. Player_SaveData(playerid, true);
  528. new
  529. name[MAX_PLAYER_NAME],
  530. ip = GetIP(playerid);
  531. GetPlayerName(playerid, name, sizeof (name)),
  532. // Write this connection.
  533. sql_escape_string(YSI_g_sWriteHandle, name, name),
  534. // Save the online time for this NAME.
  535. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  536. "UPDATE y_users_name_ips SET leave_time=NOW() WHERE name='%s' ORDER BY row DESC LIMIT 1;",
  537. name),
  538. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1),
  539. // Save the total online time for this YID.
  540. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  541. "UPDATE y_users_preload SET last_ip=0x%04x%04x, last_seen=NOW(), play_time=play_time+(" \
  542. "SELECT (NOW()-leave_time) FROM y_users_yid_ips WHERE yid=%d ORDER BY row DESC LIMIT 1)" \
  543. "WHERE yid=%d;",
  544. ip >>> 16, ip & 0xFFFF, yid, yid),
  545. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1),
  546. // Save the current online time for this YID.
  547. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  548. "UPDATE y_users_yid_ips SET leave_time=NOW() WHERE yid=%d ORDER BY row DESC LIMIT 1;",
  549. yid),
  550. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  551. }
  552. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID] = -1,
  553. YSI_g_sPlayerYID[playerid] = 0;
  554. }
  555. stock bool:Player_IsLoggedIn(playerid)
  556. {
  557. // -2 should never be an issue, but if it is...
  558. return YSI_g_sPlayerYID[playerid] > 0;
  559. }
  560. stock Player_GetYID(playerid)
  561. {
  562. return YSI_g_sPlayerYID[playerid];
  563. }
  564. /**--------------------------------------------------------------------------**\
  565. <summary>Player_TryLogin</summary>
  566. <param name="playerid">Player who is logging in.</param>
  567. <param name="password[]">Password they entered.</param>
  568. <param name="f">Show the failed to login message?</param>
  569. <returns>
  570. -
  571. </returns>
  572. <remarks>
  573. Tries to log in a player - hashes and checks their password and if it's
  574. right calls the core login code. It doesn't matter WHICH script does this
  575. as they ALL get called and ALL track the login status of a player.
  576. </remarks>
  577. \**--------------------------------------------------------------------------**/
  578. global Player_TryLogin(playerid, string:password[])
  579. {
  580. P:2("Player_TryLogin start");
  581. if (Player_IsLoggedIn(playerid))
  582. {
  583. // They are already logged in.
  584. Text_Send(playerid, $YSI_LOGIN_ALREADY);
  585. return 1;
  586. }
  587. new
  588. hash[MAX_PASSWORD_LENGTH + 8 + 1];
  589. Player_HashPass(password, hash);
  590. format(hash[MAX_PASSWORD_LENGTH], sizeof (hash) - MAX_PASSWORD_LENGTH, "%04x%04x", YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] >>> 16, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] & 0xFFFF);
  591. Player_HashPass(hash, hash);
  592. switch (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID])
  593. {
  594. case -1:
  595. {
  596. Text_Send(playerid, $YSI_LOGIN_INDERR);
  597. }
  598. case 0:
  599. {
  600. Text_Send(playerid, $YSI_LOGIN_NOTF);
  601. }
  602. default:
  603. {
  604. // Match the password.
  605. if (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS][0] && !strcmp(YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS], hash, false, MAX_PASSWORD_LENGTH))
  606. {
  607. // Wipe the password from memory.
  608. memset(YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS], 0, MAX_PASSWORD_LENGTH);
  609. // Extract the uid and call in to the login code.
  610. Langs_SetPlayerLanguage(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_LANG]);
  611. broadcastfunc Player_DoLogin(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID]);
  612. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  613. new
  614. name[MAX_PLAYER_NAME],
  615. safeName[MAX_PLAYER_NAME],
  616. ip = GetIP(playerid);
  617. GetPlayerName(playerid, name, sizeof (name)),
  618. // Write this connection.
  619. sql_escape_string(YSI_g_sWriteHandle, name, safeName),
  620. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  621. "INSERT INTO y_users_yid_ips VALUES (DEFAULT, %d, 0x%04x%04x, NOW(), NOW())",
  622. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID],
  623. ip >>> 16,
  624. ip & 0xFFFF),
  625. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  626. return 1;
  627. }
  628. else
  629. {
  630. Text_Send(playerid, $YSI_LOGIN_WRONG);
  631. // Record the failed login in the database.
  632. new
  633. ip = GetIP(playerid);
  634. GetPlayerName(playerid, hash, sizeof (hash)),
  635. sql_escape_string(YSI_g_sWriteHandle, hash, hash),
  636. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  637. "INSERT INTO y_users_fails VALUES (DEFAULT, %d, '%s', 0x%04x%04x, 0, NOW())",
  638. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID],
  639. hash,
  640. ip >>> 16,
  641. ip & 0xFFFF),
  642. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  643. }
  644. }
  645. }
  646. return 0;
  647. }
  648. /**--------------------------------------------------------------------------**\
  649. <summary>Player_ForceLogin</summary>
  650. <param name="playerid">Player who is logging in.</param>
  651. <returns>
  652. -
  653. </returns>
  654. <remarks>
  655. Like "Player_TryLogin" but doesn't take a password so always works.
  656. </remarks>
  657. \**--------------------------------------------------------------------------**/
  658. global Player_ForceLogin(playerid)
  659. {
  660. P:2("Player_TryLogin start");
  661. if (Player_IsLoggedIn(playerid))
  662. {
  663. // They are already logged in.
  664. Text_Send(playerid, $YSI_LOGIN_ALREADY);
  665. return 1;
  666. }
  667. switch (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID])
  668. {
  669. case -1:
  670. {
  671. Text_Send(playerid, $YSI_LOGIN_INDERR);
  672. }
  673. case 0:
  674. {
  675. Text_Send(playerid, $YSI_LOGIN_NOTF);
  676. }
  677. default:
  678. {
  679. // Extract the uid and call in to the login code.
  680. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS] = '\0';
  681. Langs_SetPlayerLanguage(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_LANG]);
  682. broadcastfunc Player_DoLogin(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID]);
  683. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  684. new
  685. name[MAX_PLAYER_NAME],
  686. safeName[MAX_PLAYER_NAME],
  687. ip = GetIP(playerid);
  688. GetPlayerName(playerid, name, sizeof (name)),
  689. // Write this connection.
  690. sql_escape_string(YSI_g_sWriteHandle, name, safeName),
  691. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  692. "INSERT INTO y_users_yid_ips VALUES (DEFAULT, %d, 0x%04x%04x, NOW(), NOW())",
  693. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID],
  694. ip >>> 16,
  695. ip & 0xFFFF),
  696. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  697. return 1;
  698. }
  699. }
  700. return 0;
  701. }
  702. remotefunc void:Player_DoLogin(playerid, uid)
  703. {
  704. // Called when a player logs in - either locally (new script) or globally
  705. // (actually only just logged in).
  706. YSI_g_sPlayerYID[playerid] = uid;
  707. // We don't need to specify how to join the tables because the definitions
  708. // use a FOREIGN KEY CONSTRAINT to map them to each other.
  709. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery), "SELECT tag, vkey, value FROM y_users_modes JOIN y_users_keystore WHERE yid=%d && mode='" #MODE_NAME "';", uid);
  710. sql_query(YSI_g_sLoadHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLLoaded", "iir", playerid, uid);
  711. //UPDATE y_users_name_ips SET validated=1 WHERE name='%s' && one_ip=0x%04x%04x && leave_time=0
  712. //format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery), "SELECT tag, vkey, value FROM y_users_modes JOIN y_users_keystore WHERE yid=%d && mode='" #MODE_NAME "';", uid);
  713. //sql_query(YSI_g_sLoadHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLLoaded", "iir", playerid, uid);
  714. }
  715. public y_users_SQLLoaded(playerid, uid, Result:r)
  716. {
  717. static
  718. func[32] = "@yU_",
  719. name[32];
  720. if (sql_error(r))
  721. {
  722. sql_error_string(r, YSI_g_sSQLQuery),
  723. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  724. return
  725. sql_free_result(r),
  726. 0;
  727. }
  728. // Finally, they've logged in.
  729. call OnPlayerLogin(playerid, uid);
  730. // Loop through all the results.
  731. new
  732. rows = sql_num_rows(r);
  733. for (new row = 0; row != rows; ++row)
  734. {
  735. // Load all the data in a YSI-compatible way.
  736. sql_next_row(r, row),
  737. sql_get_field(r, 0, func[4]),
  738. sql_get_field(r, 1, name),
  739. sql_get_field(r, 2, YSI_g_sSQLQuery),
  740. CallLocalFunction(func, "iss", playerid, name, YSI_g_sSQLQuery);
  741. }
  742. return
  743. sql_free_result(r),
  744. 1;
  745. }
  746. public y_users_SQLCallback(id, Result:r)
  747. {
  748. if (sql_error(r))
  749. {
  750. if (id == 3 && sql_error(r) == 1452) goto y_users_SQLCallback_done;
  751. sql_error_string(r, YSI_g_sSQLQuery),
  752. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  753. return
  754. sql_free_result(r),
  755. 0;
  756. }
  757. y_users_SQLCallback_done:
  758. return
  759. sql_free_result(r),
  760. 1;
  761. }
  762. /**--------------------------------------------------------------------------**\
  763. <summary>Player_RemoveEntry</summary>
  764. <param name="name[]">Item to remove.</param>
  765. <returns>
  766. -
  767. </returns>
  768. <remarks>
  769. Wrapper for Player_AddToBuffer for removing data.
  770. </remarks>
  771. \**--------------------------------------------------------------------------**/
  772. stock Player_RemoveEntry(name[])
  773. {
  774. // First, flush the main buffer so we can add anything we might want to
  775. // remove.
  776. Player_FlushData();
  777. sql_escape_string(YSI_g_sWriteHandle, name, YSI_g_sSQLQuery),
  778. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery), "DELETE FROM y_users_keystore WHERE yid=%d && tagid=@T && vkey='%s';", YSI_g_sCurrentYID, YSI_g_sSQLQuery),
  779. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 1);
  780. }
  781. /**--------------------------------------------------------------------------**\
  782. <summary>Player_WriteString</summary>
  783. <param name="name[]">Data name.</param>
  784. <param name="data[]">Data.</param>
  785. <returns>
  786. -
  787. </returns>
  788. <remarks>
  789. Wrapper for Player_AddToBuffer for strings.
  790. </remarks>
  791. \**--------------------------------------------------------------------------**/
  792. stock Player_WriteString(name[], data[])
  793. {
  794. static
  795. sPart[196 + 32 + 10 + 10 + 10];
  796. format(sPart, sizeof (YSI_g_sSQLBuffer), "(%d,@T,'", YSI_g_sCurrentYID);
  797. sql_escape_string(YSI_g_sWriteHandle, name, sPart[strlen(sPart)])
  798. strcat(sPart, "','")
  799. sql_escape_string(YSI_g_sWriteHandle, data, sPart[strlen(sPart)])
  800. strcat(sPart, "')")
  801. // Already has some query buffered.
  802. if (YSI_g_sSQLBuffer[0])
  803. {
  804. if (strlen(YSI_g_sSQLBuffer) < sizeof (YSI_g_sSQLBuffer) - 44 - 2 - strlen(sPart))
  805. {
  806. // Enough space for the new values and the standard suffix.
  807. strcat(YSI_g_sSQLBuffer, ",");
  808. goto Player_WriteString_run;
  809. }
  810. strcat(YSI_g_sSQLBuffer, "ON DUPLICATE KEY UPDATE value=values(value);");
  811. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLBuffer, QUERY_THREADED, "y_users_SQLCallback", "ir", 3);
  812. }
  813. YSI_g_sSQLBuffer = "INSERT INTO y_users_keystore(yid,tagid,vkey,value)VALUES";
  814. Player_WriteString_run:
  815. strcat(YSI_g_sSQLBuffer, sPart);
  816. }
  817. stock Player_WriteArray(const name[], data[], len)
  818. {
  819. // INI_WriteArray(YSI_g_sPlayerWriteFile, name, data, len);
  820. return 1;
  821. }
  822. /**--------------------------------------------------------------------------**\
  823. <summary>Player_WriteInt</summary>
  824. <param name="name[]">Data name.</param>
  825. <param name="data">Integer data.</param>
  826. <returns>
  827. -
  828. </returns>
  829. <remarks>
  830. Wrapper for Player_AddToBuffer for integers.
  831. </remarks>
  832. \**--------------------------------------------------------------------------**/
  833. stock Player_WriteInt(name[], data)
  834. {
  835. static
  836. sVal[12];
  837. strval(sVal, data);
  838. Player_WriteString(name, sVal);
  839. }
  840. /**--------------------------------------------------------------------------**\
  841. <summary>Player_WriteHex</summary>
  842. <param name="name[]">Data name.</param>
  843. <param name="data">Hex data.</param>
  844. <returns>
  845. -
  846. </returns>
  847. <remarks>
  848. Wrapper for Player_AddToBuffer for integers to be written as hex values.
  849. </remarks>
  850. \**--------------------------------------------------------------------------**/
  851. stock Player_WriteHex(name[], data)
  852. {
  853. static
  854. sVal[11];
  855. format(sVal, sizeof (sVal), "0x%04x%04x", data >>> 16, data & 0xFFFF);
  856. Player_WriteString(name, data);
  857. }
  858. /**--------------------------------------------------------------------------**\
  859. <summary>Player_WriteBin</summary>
  860. <param name="name[]">Data name.</param>
  861. <param name="data">Binary data.</param>
  862. <returns>
  863. -
  864. </returns>
  865. <remarks>
  866. Wrapper for Player_AddToBuffer for integers to be written as binary values.
  867. </remarks>
  868. \**--------------------------------------------------------------------------**/
  869. stock Player_WriteBin(name[], data)
  870. {
  871. static const
  872. sc_values[] = !"0000" "0001" "0010" "0011" "0100" "0101" "0110" "0111" "1000" "1001" "1010" "1011" "1100" "1101" "1110" "1111";
  873. static
  874. str[35];
  875. new
  876. i = 10;
  877. do
  878. {
  879. str[--i] = sc_values[data & 0x0F];
  880. data >>>= 4;
  881. }
  882. while (data);
  883. // Convert the coalesced values to individual values.
  884. strunpack(str[i], str[i], 33);
  885. str[--i] = 'b';
  886. str[--i] = '0';
  887. Player_WriteString(name, str);
  888. }
  889. /**--------------------------------------------------------------------------**\
  890. <summary>Player_WriteBool</summary>
  891. <param name="name[]">Data name.</param>
  892. <param name="data">Boolean data.</param>
  893. <returns>
  894. -
  895. </returns>
  896. <remarks>
  897. Wrapper for Player_AddToBuffer for booleans.
  898. </remarks>
  899. \**--------------------------------------------------------------------------**/
  900. stock Player_WriteBool(name[], bool:data)
  901. {
  902. Player_WriteString(name, data ? ("true") : ("false"));
  903. }
  904. /**--------------------------------------------------------------------------**\
  905. <summary>Player_WriteFloat</summary>
  906. <param name="name[]">Data name.</param>
  907. <param name="Float:data">Float data.</param>
  908. <param name="accuracy">number of decimal places to write.</param>
  909. <returns>
  910. -
  911. </returns>
  912. <remarks>
  913. Wrapper for Player_AddToBuffer for floats. Uses custom code instead of
  914. format() as it's actually faster for something simple like this.
  915. </remarks>
  916. \**--------------------------------------------------------------------------**/
  917. stock Player_WriteFloat(name[], Float:data, accuracy = 6)
  918. {
  919. static
  920. str[16];
  921. format(str, sizeof (str), "%.*f", accuracy, data);
  922. Player_WriteString(name, str);
  923. }
  924. static stock Player_FlushData()
  925. {
  926. if (YSI_g_sSQLBuffer[0])
  927. {
  928. new
  929. len = strlen(YSI_g_sSQLBuffer) + 44;
  930. if (len >= sizeof (YSI_g_sSQLBuffer))
  931. {
  932. P:E("Could not save y_users buffer (should not happen).");
  933. return 0;
  934. }
  935. strcat(YSI_g_sSQLBuffer, "ON DUPLICATE KEY UPDATE value=values(value);");
  936. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLBuffer, QUERY_THREADED, "y_users_SQLCallback", "ir", 3),
  937. YSI_g_sSQLBuffer[0] = '\0';
  938. }
  939. return 1;
  940. }
  941. stock Player_SetTag(tag[])
  942. {
  943. Player_FlushData();
  944. sql_escape_string(YSI_g_sWriteHandle, tag, YSI_g_sSQLQuery),
  945. // Run the second query. Use "INSERT IGNORE" to add the tag only if it
  946. // doesn't exist already. The length check here is wrong, but making it
  947. // correct would take more effort and we've already checked the length.
  948. // This stores the tagid in to a global MySQL variable.
  949. format(YSI_g_sSQLBuffer, sizeof (YSI_g_sSQLBuffer),
  950. "INSERT IGNORE INTO y_users_modes(tag,mode)VALUES('%s','" #MODE_NAME "');" \
  951. "SELECT @T:=tagid FROM y_users_modes WHERE tag='%s'&&mode='" #MODE_NAME "';",
  952. YSI_g_sSQLQuery, YSI_g_sSQLQuery),
  953. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLBuffer, QUERY_THREADED, "y_users_SQLCallback", "ir", 4),
  954. YSI_g_sSQLBuffer[0] = '\0';
  955. }
  956. stock Player_DeleteTag(tag[])
  957. {
  958. // Uses "CASCADE ON DELETE".
  959. sql_escape_string(YSI_g_sWriteHandle, tag, YSI_g_sSQLQuery),
  960. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery), "DELETE FROM y_users_modes WHERE tag='%s' && mode='" #MODE_NAME "';", YSI_g_sSQLQuery),
  961. sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery, QUERY_THREADED, "y_users_SQLCallback", "ir", 5);
  962. // Any future data written to this tag will just fail the constraint.
  963. }
  964. stock Player_SaveData(playerid, bool:logout = false)
  965. {
  966. // Just in case they decide to save a player's data in the middle of another
  967. // player's data being saved...
  968. new
  969. stk = YSI_g_sCurrentYID;
  970. if (YSI_g_sPlayerYID[playerid] > 0)
  971. {
  972. // Flush any old data.
  973. Player_FlushData();
  974. // New player's data.
  975. YSI_g_sCurrentYID = YSI_g_sPlayerYID[playerid];
  976. Player_SetTag("");
  977. CallLocalFunction("OnSavePlayerData", "ii", playerid, YSI_g_sCurrentYID);
  978. if (logout)
  979. {
  980. Player_SetTag("");
  981. call OnPlayerLogout(playerid, YSI_g_sCurrentYID);
  982. }
  983. // Flush new data.
  984. Player_FlushData();
  985. }
  986. YSI_g_sCurrentYID = stk;
  987. }
  988. /**--------------------------------------------------------------------------**\
  989. <summary>Player_HashPass</summary>
  990. <param name="pass[]">Data to hash.</param>
  991. <returns>
  992. -
  993. </returns>
  994. <remarks>
  995. Based on my Dad's hash system but slightly modifed. Updated for reverse
  996. compatability with other login systems. Needs more code for Whirlpool.
  997. </remarks>
  998. \**--------------------------------------------------------------------------**/
  999. static stock Player_HashPass(pass[], target[])
  1000. {
  1001. #if defined PP_ADLER32
  1002. new
  1003. s1 = 1,
  1004. s2 = 0,
  1005. i,
  1006. You_REALLY_shouldnt_use_Adler32;
  1007. while (pass[i])
  1008. {
  1009. s1 = (s1 + pass[i++]) % 65521,
  1010. s2 = (s2 + s1) % 65521;
  1011. }
  1012. format(target, sizeof (target), "%" #MAX_PASSWORD_LENGTH "d", (s2 << 16) + s1);
  1013. #elseif defined PP_MD5 && defined MD5_Hash
  1014. new
  1015. You_REALLY_shouldnt_use_MD5;
  1016. strcpy(target, MD5_Hash(pass, strlen(pass)));
  1017. #elseif defined PP_SHA1
  1018. #error SHA1 unsupported.
  1019. #elseif defined PP_YSI
  1020. static
  1021. charset[] = "A,UbRgdnS#|rT_%5+ZvEK¬NF<9¦IH[(C)2O07 Y-Less]$Qw^?/om4;@'8k£Pp.c{&l\\3zay>DfxV:WXjuG6*!1\"i~=Mh`JB}qt",
  1022. css = 99;
  1023. new
  1024. //target[MAX_PASSWORD_LENGTH + 1],
  1025. j,
  1026. sum = j,
  1027. tmp = 0,
  1028. i,
  1029. mod;
  1030. j = strlen(pass);
  1031. for (i = 0; i < MAX_PASSWORD_LENGTH || i < j; i++)
  1032. {
  1033. mod = i % MAX_PASSWORD_LENGTH,
  1034. tmp = (i >= j) ? charset[(7 * i) % css] : pass[i],
  1035. sum = (sum + chrfind(tmp, charset) + 1) % css,
  1036. target[mod] = charset[(sum + target[mod]) % css];
  1037. }
  1038. target[MAX_PASSWORD_LENGTH] = '\0';
  1039. //return target;
  1040. #elseif defined WP_Hash
  1041. WP_Hash(target, MAX_PASSWORD_LENGTH + 1, pass);
  1042. #else
  1043. #error Whirlpool (or other) hash not found.
  1044. #endif
  1045. }
  1046. // Hooray for bizare bugs! I think this is because the function above is
  1047. // secretly a macro with "if/else" and a block statement, not a real function.
  1048. // Only now it isn't, but honestly I'm no longer sure what this was fixing or if
  1049. // it is still needed or not. Even if it isn't there's no point removing it as
  1050. // it was clearly important at some point in the past!
  1051. stock Player_SomeWeirdBugFix()
  1052. {
  1053. }
  1054. /**--------------------------------------------------------------------------**\
  1055. <summary>Player_TryRegister</summary>
  1056. <param name="playerid">Player who is registering.</param>
  1057. <param name="string:password[]">The password they entered.</param>
  1058. <returns>
  1059. -
  1060. </returns>
  1061. <remarks>
  1062. Register the player with the given password if there is no-one else with the
  1063. name already. Or log them in if the username and password match an existing
  1064. account. Note that there is no "Player_ForceRegister" as it would do the
  1065. same thing with no less parameters (a password MUST be given to write in the
  1066. file).
  1067. </remarks>
  1068. \**--------------------------------------------------------------------------**/
  1069. global Player_TryRegister(playerid, string:password[])
  1070. {
  1071. P:2("Player_TryRegister called");
  1072. if (Player_IsLoggedIn(playerid))
  1073. {
  1074. // They are already logged in.
  1075. Text_Send(playerid, $YSI_LOGIN_ALREADY);
  1076. return 1;
  1077. }
  1078. new
  1079. hash[MAX_PASSWORD_LENGTH + 1 + 8];
  1080. Player_HashPass(password, hash);
  1081. switch (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID])
  1082. {
  1083. case -1:
  1084. {
  1085. Text_Send(playerid, $YSI_LOGIN_INDERR);
  1086. return 0;
  1087. }
  1088. case 0:
  1089. {
  1090. }
  1091. default:
  1092. {
  1093. // Get the salt.
  1094. format(hash[MAX_PASSWORD_LENGTH], sizeof (hash) - MAX_PASSWORD_LENGTH, "%04x%04x", YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] >>> 16, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] & 0xFFFF);
  1095. Player_HashPass(hash, hash);
  1096. // Match the password.
  1097. if (!strcmp(YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS], hash, false, MAX_PASSWORD_LENGTH) && YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS][0])
  1098. {
  1099. // Extract the uid and call in to the login code.
  1100. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS] = '\0';
  1101. Langs_SetPlayerLanguage(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_LANG]);
  1102. broadcastfunc Player_DoLogin(playerid, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID]);
  1103. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  1104. return 1;
  1105. }
  1106. else
  1107. {
  1108. Text_Send(playerid, $YSI_REG_TAKEN);
  1109. return 0;
  1110. }
  1111. }
  1112. }
  1113. // Flush pending updates as we need to use "YSI_g_sSQLBuffer", mainly
  1114. // (entirely) because the hash pushes us well over the 256 limit for using
  1115. // "YSI_g_sSQLQuery" so we need the larger buffer.
  1116. Player_FlushData();
  1117. new
  1118. name[MAX_PLAYER_NAME + 1];
  1119. GetPlayerName(playerid, name, sizeof (name)),
  1120. sql_escape_string(YSI_g_sWriteHandle, name, name);
  1121. // Hash the password a second time.
  1122. new
  1123. d = gettime(),
  1124. date[9];
  1125. format(date, sizeof (date), "%04x%04x", d >>> 16, d & 0xFFFF),
  1126. strcat(hash, date),
  1127. Player_HashPass(hash, hash),
  1128. // Create a new YID, then associate this name with it.
  1129. format(YSI_g_sSQLBuffer, sizeof (YSI_g_sSQLBuffer),
  1130. "INSERT INTO y_users_preload VALUES (DEFAULT, '%s', '%s', 0, 0x%s, 0, 0, 0);" \
  1131. "SELECT @Y:=LAST_INSERT_ID();" \
  1132. "INSERT INTO y_users_users VALUES ('%s', @Y);" \
  1133. "SELECT @Y;",
  1134. hash, Langs_GetCode(Langs_GetPlayerLanguage(playerid)), date, name);
  1135. new
  1136. Result:r = sql_query(YSI_g_sWriteHandle, YSI_g_sSQLBuffer);
  1137. YSI_g_sSQLBuffer[0] = '\0';
  1138. if (sql_error(r))
  1139. {
  1140. sql_error_string(r, YSI_g_sSQLQuery),
  1141. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  1142. return
  1143. Text_Send(playerid, $YSI_ADDU_INDER2),
  1144. sql_free_result(r),
  1145. 0;
  1146. }
  1147. sql_get_field(r, 0, name),
  1148. sql_free_result(r),
  1149. broadcastfunc Player_DoLogin(playerid, strval(name)),
  1150. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  1151. return 1;
  1152. }
  1153. #endinput
  1154. global Player_ChangePassword(playerid, string:password[])
  1155. {
  1156. #pragma unused password
  1157. new
  1158. uid = Player_GetYID(playerid);
  1159. if (uid <= 0)
  1160. {
  1161. return 0;
  1162. }
  1163. // Create the new password.
  1164. new
  1165. hash[MAX_PASSWORD_LENGTH + 8 + 1];
  1166. Player_HashPass(password, hash);
  1167. format(hash[MAX_PASSWORD_LENGTH], sizeof (hash) - MAX_PASSWORD_LENGTH, "%04x%04x", YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] >>> 16, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] & 0xFFFF);
  1168. Player_HashPass(hash, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS]);
  1169. // Save it.
  1170. Player_RewritePreload(playerid);
  1171. // Wipe it.
  1172. memset(YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS], 0, MAX_PASSWORD_LENGTH);
  1173. return 1;
  1174. }
  1175. /**--------------------------------------------------------------------------**\
  1176. <summary>Player_RewritePreload</summary>
  1177. <param name="playerid">Player whose data should be saved.</param>
  1178. <returns>
  1179. -
  1180. </returns>
  1181. <remarks>
  1182. When a player's preload data is modifed (new bit data or changed password),
  1183. it needs to be written back out to file.
  1184. </remarks>
  1185. \**--------------------------------------------------------------------------**/
  1186. static stock Player_RewritePreload(playerid)
  1187. {
  1188. if (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID] <= 0)
  1189. {
  1190. return 0;
  1191. }
  1192. // First, find the player's file. This should be the ONLY place where the
  1193. // password is to be loaded.
  1194. new
  1195. name[MAX_PLAYER_NAME + 1];
  1196. GetPlayerName(playerid, name, sizeof (name));
  1197. P:4("Player_RewritePreload called: %s", name);
  1198. #if _YSI_PLUGINS_MYSQL == 7
  1199. // Right then, lets get coding...
  1200. new
  1201. query[110];
  1202. mysql_format(YSI_g_sMySQL, query, "SELECT `uid`, `language`, `hash` FROM `y_users_register` WHERE `name` = '%e' LIMIT 1", name);
  1203. mysql_function_query(YSI_g_sMySQL, query, true, "_Player_Preload", "ii", );
  1204. #else
  1205. new
  1206. namelen = strlen(name),
  1207. filename[] = USER_FILE_PATH "ind_X.YSI",
  1208. File:fIndex;
  1209. Player_GetIndexFile(filename, name[0]);
  1210. fIndex = fopen(filename, io_readwrite);
  1211. if (fIndex)
  1212. {
  1213. P:5("Player_RewritePreload: fIndex OK");
  1214. new
  1215. line[INDEX_DATA_LINE_LENGTH],
  1216. len;
  1217. while ((len = fread(fIndex, line)))
  1218. {
  1219. P:6("Player_RewritePreload: while");
  1220. // Check if the line is the right length (could be one of three
  1221. // lengths depending on newlines). Skip blanks.
  1222. if (len < INDEX_DATA_LINE_LENGTH - 3)
  1223. {
  1224. continue;
  1225. }
  1226. P:6("Player_RewritePreload: Not len");
  1227. // Check the name on the line.
  1228. if (!strcmp(line[MAX_INDEX_LENGTH + 1], name, false, namelen) && line[MAX_INDEX_LENGTH + 1 + namelen] == ' ')
  1229. {
  1230. P:6("Player_RewritePreload: checked name");
  1231. fseek(fIndex, -len, seek_current);
  1232. format(line, sizeof (line),
  1233. "%0" #MAX_INDEX_LENGTH "d %" #MAX_PLAYER_NAME "s %" #MAX_PASSWORD_LENGTH "s %02s %04x%04x %04x%04x" INI_NEW_LINE,
  1234. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID],
  1235. name,
  1236. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_PASS],
  1237. Langs_GetCode(Langs_GetPlayerLanguage(playerid)),
  1238. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_BITS] >>> 16, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_BITS] & 0xFFFF,
  1239. YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] >>> 16, YSI_g_sPreloadData[playerid][E_USER_PRELOAD_DATE] & 0xFFFF);
  1240. P:5("Player_RewritePreload: Writing: %s", line);
  1241. fwrite(fIndex, line);
  1242. fclose(fIndex);
  1243. return 1;
  1244. }
  1245. }
  1246. fclose(fIndex);
  1247. }
  1248. else if (fexist(filename))
  1249. {
  1250. P:E("Error reading index %c.", filename[0]);
  1251. return 0;
  1252. }
  1253. //ret[E_USER_PRELOAD_YID] = 0;
  1254. #endif
  1255. return 0;
  1256. }
  1257. /**--------------------------------------------------------------------------**\
  1258. <summary>Player_TryGroup</summary>
  1259. <param name="playerid">Player who is joining a group.</param>
  1260. <param name="string:other[]">A player name already in the group.</param>
  1261. <param name="string:password[]">The password of the group.</param>
  1262. <returns>
  1263. -
  1264. </returns>
  1265. <remarks>
  1266. Links a player with an existing player such that they share all stats.
  1267. </remarks>
  1268. \**--------------------------------------------------------------------------**/
  1269. global Player_TryGroup(playerid, string:other[], string:password[])
  1270. {
  1271. P:2("Player_TryGroup called");
  1272. if (Player_IsLoggedIn(playerid))
  1273. {
  1274. // They are already logged in.
  1275. Text_Send(playerid, $YSI_LOGIN_ALREADY);
  1276. return 1;
  1277. }
  1278. switch (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID])
  1279. {
  1280. case -1:
  1281. {
  1282. Text_Send(playerid, $YSI_LOGIN_INDERR);
  1283. return 0;
  1284. }
  1285. case 0:
  1286. {
  1287. }
  1288. default:
  1289. {
  1290. Text_Send(playerid, $YSI_REG_TAKEN);
  1291. return 0;
  1292. }
  1293. }
  1294. // Check if the new data matches the old.
  1295. new
  1296. ret[E_USER_PRELOAD];
  1297. Player_Preload(other, ret);
  1298. switch (ret[E_USER_PRELOAD_YID])
  1299. {
  1300. case -1:
  1301. {
  1302. Text_Send(playerid, $YSI_LOGIN_INDERR);
  1303. }
  1304. case 0:
  1305. {
  1306. Text_Send(playerid, $YSI_LOGIN_NOTF);
  1307. }
  1308. default:
  1309. {
  1310. new
  1311. hash[MAX_PASSWORD_LENGTH + 1 + 8],
  1312. d = ret[E_USER_PRELOAD_DATE],
  1313. date[9];
  1314. // Hash the password twice, second time with the salt.
  1315. Player_HashPass(password, hash),
  1316. format(date, sizeof (date), "%04x%04x", d >>> 16, d & 0xFFFF),
  1317. strcat(hash, date),
  1318. Player_HashPass(hash, hash);
  1319. // Match the password.
  1320. if (!strcmp(ret[E_USER_PRELOAD_PASS], hash, false, MAX_PASSWORD_LENGTH) && ret[E_USER_PRELOAD_PASS][0])
  1321. {
  1322. new
  1323. name[MAX_PLAYER_NAME + 1];
  1324. GetPlayerName(playerid, name, sizeof (name)),
  1325. sql_escape_string(YSI_g_sWriteHandle, name, name),
  1326. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  1327. "INSERT INTO y_users_users VALUES ('%s', %d);",
  1328. name, ret[E_USER_PRELOAD_YID]);
  1329. new
  1330. Result:r = sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery);
  1331. if (sql_error(r))
  1332. {
  1333. sql_error_string(r, YSI_g_sSQLQuery),
  1334. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  1335. return
  1336. Text_Send(playerid, $YSI_ADDU_INDER2),
  1337. sql_free_result(r),
  1338. 0;
  1339. }
  1340. sql_free_result(r),
  1341. // Copy over the preload data.
  1342. YSI_g_sPreloadData[playerid] = ret,
  1343. broadcastfunc Player_DoLogin(playerid, ret[E_USER_PRELOAD_YID]),
  1344. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  1345. }
  1346. else
  1347. {
  1348. Text_Send(playerid, $YSI_LOGIN_WRONG);
  1349. return 0;
  1350. }
  1351. }
  1352. }
  1353. return 1;
  1354. }
  1355. /**--------------------------------------------------------------------------**\
  1356. <summary>Player_ForceGroup</summary>
  1357. <param name="playerid">Player who is joining a group.</param>
  1358. <param name="string:other[]">A player name already in the group.</param>
  1359. <returns>
  1360. -
  1361. </returns>
  1362. <remarks>
  1363. Like "Player_TryGroup", but doesn't take a password and instead just uses
  1364. the password of the old player (hashed).
  1365. </remarks>
  1366. \**--------------------------------------------------------------------------**/
  1367. global Player_ForceGroup(playerid, string:other[])
  1368. {
  1369. P:2("Player_ForceGroup called");
  1370. if (Player_IsLoggedIn(playerid))
  1371. {
  1372. // They are already logged in.
  1373. Text_Send(playerid, $YSI_LOGIN_ALREADY);
  1374. return 1;
  1375. }
  1376. switch (YSI_g_sPreloadData[playerid][E_USER_PRELOAD_YID])
  1377. {
  1378. case -1:
  1379. {
  1380. Text_Send(playerid, $YSI_LOGIN_INDERR);
  1381. return 0;
  1382. }
  1383. case 0:
  1384. {
  1385. }
  1386. default:
  1387. {
  1388. Text_Send(playerid, $YSI_REG_TAKEN);
  1389. return 0;
  1390. }
  1391. }
  1392. // Check if the new data matches the old.
  1393. new
  1394. ret[E_USER_PRELOAD];
  1395. Player_Preload(other, ret);
  1396. switch (ret[E_USER_PRELOAD_YID])
  1397. {
  1398. case -1:
  1399. {
  1400. Text_Send(playerid, $YSI_LOGIN_INDERR);
  1401. }
  1402. case 0:
  1403. {
  1404. Text_Send(playerid, $YSI_LOGIN_NOTF);
  1405. }
  1406. default:
  1407. {
  1408. new
  1409. name[MAX_PLAYER_NAME + 1];
  1410. GetPlayerName(playerid, name, sizeof (name)),
  1411. sql_escape_string(YSI_g_sWriteHandle, name, name),
  1412. format(YSI_g_sSQLQuery, sizeof (YSI_g_sSQLQuery),
  1413. "INSERT INTO y_users_users VALUES ('%s', %d);",
  1414. name, ret[E_USER_PRELOAD_YID]);
  1415. new
  1416. Result:r = sql_query(YSI_g_sWriteHandle, YSI_g_sSQLQuery);
  1417. if (sql_error(r))
  1418. {
  1419. sql_error_string(r, YSI_g_sSQLQuery),
  1420. P:E("SQL Error: %s", YSI_g_sSQLQuery);
  1421. return
  1422. Text_Send(playerid, $YSI_ADDU_INDER2),
  1423. sql_free_result(r),
  1424. 0;
  1425. }
  1426. sql_free_result(r),
  1427. // Copy over the preload data.
  1428. YSI_g_sPreloadData[playerid] = ret,
  1429. broadcastfunc Player_DoLogin(playerid, ret[E_USER_PRELOAD_YID]),
  1430. Text_Send(playerid, $YSI_LOGIN_LOGIN);
  1431. }
  1432. }
  1433. return 1;
  1434. }