y_uvar.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. #if defined _INC_y_uvar
  2. #endinput
  3. #endif
  4. #define _INC_y_uvar
  5. /**
  6. * <library name="y_uvar">
  7. * <section>
  8. * Description
  9. * </section>
  10. * Declares data to be automatically saved and loaded on a per-player basis.
  11. * <section>
  12. * Version
  13. * </section>
  14. * 0.1.3
  15. * </library>
  16. *//** *//*
  17. Legal:
  18. Version: MPL 1.1
  19. The contents of this file are subject to the Mozilla Public License Version
  20. 1.1 the "License"; you may not use this file except in compliance with
  21. the License. You may obtain a copy of the License at
  22. http://www.mozilla.org/MPL/
  23. Software distributed under the License is distributed on an "AS IS" basis,
  24. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  25. for the specific language governing rights and limitations under the
  26. License.
  27. The Original Code is the YSI framework.
  28. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  29. Portions created by the Initial Developer are Copyright C 2011
  30. the Initial Developer. All Rights Reserved.
  31. Contributors:
  32. Y_Less
  33. koolk
  34. JoeBullet/Google63
  35. g_aSlice/Slice
  36. Misiur
  37. samphunter
  38. tianmeta
  39. maddinat0r
  40. spacemud
  41. Crayder
  42. Dayvison
  43. Ahmad45123
  44. Zeex
  45. irinel1996
  46. Yiin-
  47. Chaprnks
  48. Konstantinos
  49. Masterchen09
  50. Southclaws
  51. PatchwerkQWER
  52. m0k1
  53. paulommu
  54. udan111
  55. Thanks:
  56. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  57. ZeeX - Very productive conversations.
  58. koolk - IsPlayerinAreaEx code.
  59. TheAlpha - Danish translation.
  60. breadfish - German translation.
  61. Fireburn - Dutch translation.
  62. yom - French translation.
  63. 50p - Polish translation.
  64. Zamaroht - Spanish translation.
  65. Los - Portuguese translation.
  66. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  67. me to strive to better.
  68. Pixels^ - Running XScripters where the idea was born.
  69. Matite - Pestering me to release it and using it.
  70. Very special thanks to:
  71. Thiadmer - PAWN, whose limits continue to amaze me!
  72. Kye/Kalcor - SA:MP.
  73. SA:MP Team past, present and future - SA:MP.
  74. Optional plugins:
  75. Gamer_Z - GPS.
  76. Incognito - Streamer.
  77. Me - sscanf2, fixes2, Whirlpool.
  78. */
  79. // y_uvars
  80. #include "..\YSI_Internal\y_version"
  81. #include "..\YSI_Storage\y_amx"
  82. #include "..\YSI_Core\y_debug"
  83. #include "..\YSI_Core\y_utils"
  84. #include "..\YSI_Players\y_users"
  85. #include "..\YSI_Storage\y_ini"
  86. #include "..\YSI_Coding\y_hooks"
  87. #include "..\YSI_Internal\y_stripnumbers"
  88. // Third "uvar" version.
  89. #define _YU@LE@E%0>
  90. #define _YU@LT@E%0> ;
  91. // Needs two levels of indirection to strip the excess commas (,%0,%1).
  92. #define _YU@LO(,%0,%1,%2) %0@yA_();public %0@yA_(){N@(#....#%0,_:%1,_:%2,STRIP_NUMBERS:%0[0]|||%2:0|||);}
  93. #define _YU@LE%0[%1][%3]%2> _YU@LO(%0,%1,%3) _YU@LE%2>
  94. // Recursive local default string definition.
  95. #define _YU@LJ(,%0,%1,%2) %0[%1][%2]
  96. #define _YU@LT%0[%1][%3]%2> ,_YU@LJ(%0,%1,%3)_YU@LT%2>
  97. #define _YU@LA%0[%1][%3]%2> _YU@LJ(%0,%1,%3)_YU@LT%2>
  98. // Entry point for the loaders. The structure of stored pvar data is:
  99. //
  100. // [0] - Pointer to next pvar in list (-1 for end).
  101. // [1] - Pointer to data.
  102. // [2] - Number of players.
  103. // [3] - Size of enum.
  104. // [4] - Start of name.
  105. //
  106. // It is VERY important to note that using "%0[0][0]" when calling "N@" instead
  107. // of "%0" gives a DIFFERENT address - we get the address of the first data
  108. // element in the array, not the address of the start of the array pointer table
  109. // which is used to index multi-dimensional arrays when the size is not known
  110. // (which in this case it is). This makes calculating offsets later possible.
  111. #define uvar%0[%8][%1]%2; stock _YU@LA,%0[%8][%1]%2@E>_YU@LE,%0[%8][%1]%2@E>
  112. //%0@yA_();public%0@yA_()N@(_:%0,#....#%0 _YA@LT %1,@E|||);
  113. // This is a structure defining the data stored on the enum structure.
  114. /*enum E_USERS_FAKE_DATA
  115. {
  116. E_USERS_FAKE_DATA_NEXT,
  117. E_USERS_FAKE_DATA_DATA,
  118. E_USERS_FAKE_DATA_LEN,
  119. E_USERS_FAKE_DATA_STR[2]
  120. }*/
  121. static stock
  122. YSI_g_sFirstUVarData = -1,
  123. // These three variables are used to speed up data loading through caching.
  124. YSI_g_sLastName[32] = "\1\0",
  125. YSI_g_sLastAddr,
  126. YSI_g_sLastPlayers,
  127. YSI_g_sLastSize;
  128. forward _y_uvar_include_@();
  129. public _y_uvar_include_@()
  130. {
  131. memset("", 0, 0);
  132. Player_WriteArray("", "", 0);
  133. }
  134. static stock Uvar_FindData(const name[], data[])
  135. {
  136. // This function gets passed an empty string so that we can use "data" as a
  137. // string, while secretly changing the pointer in AMX code.
  138. new
  139. p = YSI_g_sFirstUVarData;
  140. while (p != -1)
  141. {
  142. // Modify our data pointer.
  143. #emit LOAD.S.pri p
  144. #emit STOR.S.pri data
  145. if (!strcmp(data[4], name))
  146. {
  147. strcpy(YSI_g_sLastName, name);
  148. YSI_g_sLastSize = data[3];
  149. YSI_g_sLastPlayers = data[2];
  150. YSI_g_sLastAddr = data[1];
  151. //printf("found %s, %d, %d, %d", YSI_g_sLastName, YSI_g_sLastSize, YSI_g_sLastPlayers, YSI_g_sLastAddr);
  152. return;
  153. }
  154. p = data[0];
  155. }
  156. YSI_g_sLastAddr = -1;
  157. }
  158. forward OnUserData[y_uvar](playerid, name[], value[]);
  159. public OnUserData[y_uvar](playerid, name[], value[])
  160. {
  161. // See what the name of the loaded data was.
  162. new
  163. pos = strfind(name, "-");
  164. if (pos == -1)
  165. {
  166. if (strcmp(name, YSI_g_sLastName))
  167. {
  168. // Find the data.
  169. Uvar_FindData(name, "");
  170. }
  171. if (YSI_g_sLastAddr == -1)
  172. {
  173. return;
  174. }
  175. // Check that the data is the right size.
  176. P:C(if (strval(value) != YSI_g_sLastSize) P:E("uvar data changed in %s", YSI_g_sLastName););
  177. }
  178. else
  179. {
  180. // Get the position in the array of this data.
  181. //printf("call pos 0");
  182. name[pos] = '\0';
  183. pos = strval(name[pos + 1]) * ((MAX_INI_ENTRY_TEXT - 1) / 16 * 3);
  184. if (strcmp(name[2], YSI_g_sLastName, false))
  185. {
  186. // Find the data.
  187. Uvar_FindData(name[2], "");
  188. }
  189. if (YSI_g_sLastAddr == -1)
  190. {
  191. return;
  192. }
  193. // Get the offset in the array for this player.
  194. if (playerid < YSI_g_sLastPlayers)
  195. {
  196. new
  197. len = strlen(value),
  198. idx;
  199. pos += YSI_g_sLastSize * playerid;
  200. // Save this pointer to an array variable for simplicity.
  201. #emit LOAD.pri YSI_g_sLastAddr
  202. #emit STOR.S.pri name
  203. // "pos" holds the offset of this data. "value" always holds a
  204. // whole number of cells worth of data.
  205. while (idx + 16 <= len)
  206. {
  207. // Do the large chunks.
  208. name[pos++] = ((value[idx + 0] - '>') << 26)
  209. | ((value[idx + 1] - '>') << 20)
  210. | ((value[idx + 2] - '>') << 14)
  211. | ((value[idx + 3] - '>') << 8)
  212. | ((value[idx + 4] - '>') << 2)
  213. | ((value[idx + 5] - '>') >> 4);
  214. // Second cell.
  215. name[pos++] = ((value[idx + 5] - '>') << 28)
  216. | ((value[idx + 6] - '>') << 22)
  217. | ((value[idx + 7] - '>') << 16)
  218. | ((value[idx + 8] - '>') << 10)
  219. | ((value[idx + 9] - '>') << 4)
  220. | ((value[idx + 10] - '>') >> 2);
  221. // Third cell.
  222. name[pos++] = ((value[idx + 10] - '>') << 30)
  223. | ((value[idx + 11] - '>') << 24)
  224. | ((value[idx + 12] - '>') << 18)
  225. | ((value[idx + 13] - '>') << 12)
  226. | ((value[idx + 14] - '>') << 6)
  227. | ((value[idx + 15] - '>') >> 0);
  228. // 16 characters are used to encode 3 cells (12 bytes) by only
  229. // saving 6 bits per character to ensure that they are always
  230. // valid characters. 7 bits may be easier, but would mean the
  231. // encoding fit less well to small numbers of cells.
  232. idx += 16;
  233. }
  234. if (idx + 6 <= len)
  235. {
  236. // Save any few extra bytes.
  237. name[pos++] = ((value[idx + 0] - '>') << 26)
  238. | ((value[idx + 1] - '>') << 20)
  239. | ((value[idx + 2] - '>') << 14)
  240. | ((value[idx + 3] - '>') << 8)
  241. | ((value[idx + 4] - '>') << 2)
  242. | ((value[idx + 5] - '>') >> 4);
  243. if (idx + 11 <= len)
  244. {
  245. name[pos++] = ((value[idx + 5] - '>') << 28)
  246. | ((value[idx + 6] - '>') << 22)
  247. | ((value[idx + 7] - '>') << 16)
  248. | ((value[idx + 8] - '>') << 10)
  249. | ((value[idx + 9] - '>') << 4)
  250. | ((value[idx + 10] - '>') >> 2);
  251. }
  252. }
  253. }
  254. }
  255. }
  256. /*-------------------------------------------------------------------------*//**
  257. * <param name="val">Handle to the PAWN data array.</param>
  258. * <param name="vardata">Handle to the memory location in which to store info.</param>
  259. * <param name="">Array slot size information.</param>
  260. * <remarks>
  261. * This function modifies "vardata" well beyond its original limits to contain
  262. * information on the structure of the enum used to define "val". This code
  263. * uses the name and size information passed in the additional parameters as
  264. * strings, and makes assumptions about how the compiler lays out memory to
  265. * combine all the passed strings in to one big string in what could be ROM,
  266. * but in SA:MP isn't. This takes a human readable(ish) description of the
  267. * array elements and converts it in to a much simpler to read format for the
  268. * computer to use later when loading and storing data.
  269. *
  270. * The description above is no longer the case. This code now just saves the
  271. * size of the data, the number of players in the array, the address of the
  272. * data, a pointer to another data set and the name of this data. This is by
  273. * far much simpler than the old version.
  274. * </remarks>
  275. *//*------------------------------------------------------------------------**/
  276. stock N@(volatile const vardata[], playerCount, dataSize, &pointer)
  277. {
  278. new
  279. sAddr;
  280. // Store the basic data, including linked-list pointers and a pointer to the
  281. // location at which the data is stored.
  282. #emit LOAD.S.pri vardata
  283. #emit STOR.S.pri sAddr
  284. printf("", YSI_g_sFirstUVarData);
  285. #emit LOAD.pri YSI_g_sFirstUVarData
  286. #emit SREF.S.pri sAddr
  287. YSI_g_sFirstUVarData = sAddr;
  288. sAddr += 4;
  289. #emit LOAD.S.pri pointer
  290. #emit SREF.S.pri sAddr
  291. sAddr += 4;
  292. #emit LOAD.S.pri playerCount
  293. #emit SREF.S.pri sAddr
  294. sAddr += 4;
  295. #emit LOAD.S.pri dataSize
  296. #emit SREF.S.pri sAddr
  297. P:5("N@: %d %d %d %d %s", vardata[0], vardata[1], vardata[2], vardata[3], vardata[4]);
  298. P:5("N@: %d", YSI_g_sFirstUVarData);
  299. }
  300. hook OnScriptInit()
  301. {
  302. // List them all.
  303. YSI_g_sFirstUVarData = -1;
  304. // Call all @yA_ functions to get all required data.
  305. new
  306. idx,
  307. buffer;
  308. while ((idx = AMX_GetPublicPointerSuffix(idx, buffer, _A<@yA_>)))
  309. {
  310. #emit PUSH.C 0
  311. #emit LCTRL 6
  312. #emit ADD.C 28
  313. #emit PUSH.pri
  314. #emit LOAD.S.pri buffer
  315. #emit SCTRL 6
  316. }
  317. }
  318. /*public OnPlayerLogout(playerid, uid)
  319. {
  320. #if defined Uvar_OnPlayerLogout
  321. Uvar_OnPlayerLogout(playerid, uid);
  322. #endif
  323. Uvar_DoSavePlayer(playerid);
  324. }
  325. #if defined _ALS_OnPlayerLogout
  326. #undef OnPlayerLogout
  327. #else
  328. #define _ALS_OnPlayerLogout
  329. #endif
  330. #if defined Uvar_OnPlayerLogout
  331. forward Uvar_OnPlayerLogout(playerid, uid);
  332. #endif
  333. #define OnPlayerLogout(%0) Uvar_OnPlayerLogout(%0)*/
  334. /*public OnPlayerSaved(playerid, uid)
  335. {
  336. #if defined Uvar_OnPlayerSaved
  337. Uvar_OnPlayerSaved(playerid, uid);
  338. #endif
  339. Uvar_DoSavePlayer(playerid);
  340. }
  341. #if defined _ALS_OnPlayerSaved
  342. #undef OnPlayerSaved
  343. #else
  344. #define _ALS_OnPlayerSaved
  345. #endif
  346. #if defined Uvar_OnPlayerSaved
  347. forward Uvar_OnPlayerSaved(playerid, uid);
  348. #endif
  349. #define OnPlayerSaved(%0) Uvar_OnPlayerSaved(%0)*/
  350. stock Uvar_Save(playerid)
  351. {
  352. return _Player_ForceSave(playerid);
  353. }
  354. stock _Uvar_DoSavePlayer(playerid)
  355. {
  356. // Loop through all the player data items and write them to a file.
  357. Player_SetTag("y_uvar");
  358. new
  359. p = YSI_g_sFirstUVarData,
  360. temp;
  361. while (p != -1)
  362. {
  363. // DO NOT CHANGE THE CODE BELOW HERE!!!
  364. // Call a function sort of. This allows us to push an arbitrary address
  365. // as an array to a function.
  366. #emit LOAD.S.pri p
  367. // Get the max players.
  368. #emit ADD.C 8
  369. #emit STOR.S.pri temp
  370. #emit LREF.S.pri temp
  371. #emit STOR.S.pri temp
  372. if (playerid < temp)
  373. {
  374. // Get the data size.
  375. #emit LOAD.S.pri p
  376. #emit ADD.C 12
  377. #emit STOR.S.pri temp
  378. #emit LREF.S.pri temp
  379. #emit PUSH.pri
  380. // Get the data offset.
  381. #emit LOAD.S.alt playerid
  382. #emit SMUL
  383. #emit SMUL.C 4
  384. #emit MOVE.alt
  385. // Get the data pointer.
  386. #emit LOAD.S.pri p
  387. #emit ADD.C 4
  388. #emit STOR.S.pri temp
  389. #emit LREF.S.pri temp
  390. #emit ADD
  391. #emit PUSH.pri
  392. // Get the function name.
  393. #emit LOAD.S.pri p
  394. #emit ADD.C 16
  395. #emit PUSH.pri
  396. // Save the next pointer.
  397. #emit LREF.S.pri p
  398. #emit STOR.S.pri p
  399. // Now push the size of data put on the stack.
  400. #emit PUSH.C 12
  401. // Now get the return address and push it.
  402. #emit LCTRL 6
  403. #emit ADD.C 28
  404. #emit PUSH.pri
  405. // Call "Player_WriteArray" directly.
  406. #emit CONST.pri Player_WriteArray
  407. #emit SCTRL 6
  408. // DO NOT CHANGE THE CODE ABOVE HERE!!!
  409. }
  410. }
  411. }
  412. hook OnPlayerConnect(playerid)
  413. {
  414. P:1("hook Users_OnPlayerConnect called: %i", playerid);
  415. new
  416. p = YSI_g_sFirstUVarData,
  417. temp;
  418. while (p != -1)
  419. {
  420. // DO NOT CHANGE THE CODE BELOW HERE!!!
  421. // Call a function sort of. This allows us to push an arbitrary address
  422. // as an array to a function.
  423. #emit LOAD.S.pri p
  424. // Get the max players.
  425. #emit ADD.C 8
  426. #emit STOR.S.pri temp
  427. #emit LREF.S.pri temp
  428. #emit STOR.S.pri temp
  429. if (playerid < temp)
  430. {
  431. // It might be faster here to just use "FILL"...
  432. // Get the data enum size.
  433. //#emit PUSH.C 8
  434. #emit LOAD.S.pri p
  435. #emit ADD.C 12
  436. #emit STOR.S.pri temp
  437. #emit LREF.S.pri temp
  438. #emit PUSH.pri
  439. // Set them all to "0".
  440. #emit PUSH.C 0
  441. // Get the data offset.
  442. #emit LOAD.S.alt playerid
  443. #emit SMUL
  444. #emit SMUL.C 4
  445. #emit MOVE.alt
  446. // Get the data pointer.
  447. #emit LOAD.S.pri p
  448. #emit ADD.C 4
  449. #emit STOR.S.pri temp
  450. #emit LREF.S.pri temp
  451. #emit ADD
  452. #emit PUSH.pri
  453. // Save the next pointer.
  454. #emit LREF.S.pri p
  455. #emit STOR.S.pri p
  456. // Now push the size of data put on the stack.
  457. #emit PUSH.C 12
  458. // Now get the return address and push it.
  459. #emit LCTRL 6
  460. #emit ADD.C 28
  461. #emit PUSH.pri
  462. // Call "memset" directly.
  463. #emit CONST.pri memset
  464. #emit SCTRL 6
  465. // DO NOT CHANGE THE CODE ABOVE HERE!!!
  466. }
  467. }
  468. }