y_timers_impl.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. // Disable this version!
  65. static stock
  66. Alloc:YSI_g_sLastSlot = NO_ALLOC,
  67. Alloc:YSI_g_sFirstSlot = NO_ALLOC,
  68. YSI_g_sPlayerTimers = -1;
  69. public OnCodeInit()
  70. {
  71. P:1("hook Timers_OnScriptInit called");
  72. new
  73. pointer,
  74. time,
  75. idx,
  76. entry;
  77. while ((idx = AMX_GetPublicEntryPrefix(idx, entry, _A<@yT_>)))
  78. //while ((idx = AMX_GetPublicPointerSuffix(idx, pointer, _A<@yT_>)))
  79. {
  80. P:6("Timer_OnScriptInit: entry: %d", entry);
  81. #emit LREF.S.pri entry
  82. #emit STOR.S.pri pointer
  83. //YSI_g_sCurFunc = pointer;
  84. // Don't bother with the real name, call the function by address to get
  85. // the time the function runs for.
  86. P:7("Timer_OnScriptInit: pointer: %d", pointer);
  87. // Push the address of the current function.
  88. #emit PUSH.S pointer
  89. #emit PUSH.C 0xFFFFFFFF
  90. #emit PUSH.C 8
  91. #emit LCTRL 6
  92. #emit ADD.C 36
  93. #emit LCTRL 8
  94. #emit PUSH.pri
  95. #emit LOAD.S.pri pointer
  96. #emit SCTRL 6
  97. #emit STOR.S.pri time
  98. //YSI_g_sCurFunc = 0;
  99. P:7("Timer_OnScriptInit: time: %d", time);
  100. if (time != -1)
  101. {
  102. // Find all the functions with the same time. This is less
  103. // efficient than previous implementations (it is O(N^2)), but also
  104. // more robust as it won't fail no matter how many different times
  105. // there are - old ones relied on an array with a finite size.
  106. new
  107. pointer2,
  108. time2,
  109. idx2,
  110. total,
  111. pre;
  112. while ((idx2 = AMX_GetPublicPointerPrefix(idx2, pointer2, _A<@yT_>)))
  113. {
  114. // Call the functions a second time to guarantee getting
  115. #emit PUSH.C 0
  116. #emit PUSH.C 0xFFFFFFFF
  117. #emit PUSH.C 8
  118. #emit LCTRL 6
  119. #emit ADD.C 36
  120. #emit LCTRL 8
  121. #emit PUSH.pri
  122. #emit LOAD.S.pri pointer2
  123. #emit SCTRL 6
  124. #emit STOR.S.pri time2
  125. // Check if the new time is a FACTOR, SAME, or MULTIPLE of this
  126. // task, so we don't start different timers together.
  127. if (time2 == time || time / time2 * time2 == time || time2 / time * time == time2)
  128. {
  129. ++total;
  130. if (idx2 < idx)
  131. {
  132. ++pre;
  133. }
  134. }
  135. }
  136. P:7("Timer_OnScriptInit: total: %d, time: %d, pre: %d", total, time, pre);
  137. // Now we know what time this function has, how many others have
  138. // that time and how many have already been started.
  139. new
  140. buffer[32];
  141. entry += 4;
  142. #emit LREF.S.pri entry
  143. #emit STOR.S.pri pointer
  144. AMX_ReadString(AMX_BASE_ADDRESS + pointer, buffer);
  145. P:7("Timer_OnScriptInit: %s", unpack(buffer));
  146. // Get the time offset for the current call. This should mean that
  147. // all the functions are nicely spread out.
  148. O@(buffer, time * pre / total, 0, "ii", 1, -1);
  149. }
  150. }
  151. P:1("hook Timers_OnScriptInit ended");
  152. #if defined Timers_OnCodeInit
  153. Timers_OnCodeInit();
  154. #endif
  155. return 1;
  156. }
  157. #undef OnCodeInit
  158. #define OnCodeInit Timers_OnCodeInit
  159. #if defined Timers_OnCodeInit
  160. forward Timers_OnCodeInit();
  161. #endif
  162. HOOK__ OnPlayerConnect(playerid)
  163. {
  164. P:1("hook Timers_OnPlayerConnect called: %d", playerid);
  165. // Loop through all the per-player timers. Correctly finds them all from a
  166. // linked list hidden in static variables (which are really global).
  167. new
  168. cur = YSI_g_sPlayerTimers,
  169. data;
  170. while (cur != -1)
  171. {
  172. #emit LREF.S.pri cur
  173. #emit STOR.S.pri data
  174. P:6("Timers_OnPlayerConnect: func: %x", data);
  175. // Start this timer for this player.
  176. #emit PUSH.S playerid
  177. #emit PUSH.C 1
  178. // Push the parameter count (in bytes). This is actually passed to
  179. // native functions directly.
  180. #emit PUSH.C 8
  181. // Call the function currently in the list to trigger the repeating
  182. // timer. This involves getting the current "cip" address, modifying it
  183. // to get the return address then modifying "cip" to call the function.
  184. #emit LCTRL 6
  185. #emit ADD.C 36
  186. #emit LCTRL 8
  187. #emit PUSH.pri
  188. #emit LOAD.S.pri data
  189. #emit SCTRL 6
  190. // Returned, get the next list element.
  191. cur += 4;
  192. #emit LREF.S.pri cur
  193. #emit STOR.S.pri cur
  194. }
  195. P:1("hook Timers_OnPlayerConnect ended");
  196. return 1;
  197. }
  198. HOOK__ OnPlayerDisconnect(playerid, reason)
  199. {
  200. P:1("hook Timers_OnPlayerDisconnect called: %d, %d, playerid, reason");
  201. // Loop through all the per-player timers. Correctly finds them all from a
  202. // linked list hidden in static variables (which are really global).
  203. new
  204. cur = YSI_g_sPlayerTimers,
  205. data;
  206. while (cur != -1)
  207. {
  208. #emit LREF.S.pri cur
  209. #emit STOR.S.pri data
  210. P:6("Timers_OnPlayerDisconnect: func: %x", data);
  211. // End this timer for this player.
  212. #emit PUSH.S playerid
  213. #emit PUSH.C 0
  214. // Push the parameter count (in bytes). This is actually passed to
  215. // native functions directly.
  216. #emit PUSH.C 8
  217. // Call the function currently in the list to trigger the repeating
  218. // timer. This involves getting the current "cip" address, modifying it
  219. // to get the return address then modifying "cip" to call the function.
  220. #emit LCTRL 6
  221. #emit ADD.C 36
  222. #emit LCTRL 8
  223. #emit PUSH.pri
  224. #emit LOAD.S.pri data
  225. #emit SCTRL 6
  226. // Returned, get the next list element.
  227. cur += 4;
  228. #emit LREF.S.pri cur
  229. #emit STOR.S.pri cur
  230. }
  231. P:1("hook Timers_OnPlayerDisconnect ended");
  232. return 1;
  233. }
  234. stock _Timer_I(const func[], interval, action, &result)
  235. {
  236. P:3("_Timer_I called");
  237. switch (action)
  238. {
  239. case 0:
  240. {
  241. if (result != -1)
  242. {
  243. KillTimer(result),
  244. result =- 1;
  245. }
  246. }
  247. case 1:
  248. {
  249. if (result == -1)
  250. {
  251. result = O@(func, interval, 1);
  252. }
  253. }
  254. }
  255. return interval;
  256. }
  257. // Attempt to stop or start a task, possibly for a single player.
  258. stock _Timer_D(const func[], interval, const action, who, results[MAX_PLAYERS], a[2])
  259. {
  260. P:3("_Timer_D called");
  261. switch (action)
  262. {
  263. case -1:
  264. {
  265. if (who)
  266. {
  267. // Add this timer to the global linked list.
  268. a[0] = who;
  269. a[1] = YSI_g_sPlayerTimers;
  270. // Store the address of the global array.
  271. #emit LOAD.S.pri a
  272. #emit STOR.pri YSI_g_sPlayerTimers
  273. }
  274. }
  275. case 0:
  276. {
  277. // Stop the timer.
  278. if (who == -1)
  279. {
  280. FOREACH__ (who : Player)
  281. {
  282. if (results[who] != -1)
  283. {
  284. KillTimer(results[who]);
  285. results[who] = -1;
  286. }
  287. }
  288. }
  289. else if (results[who] != -1)
  290. {
  291. KillTimer(results[who]);
  292. results[who] = -1;
  293. }
  294. }
  295. case 1:
  296. {
  297. // Start the timer.
  298. if (who == -1)
  299. {
  300. FOREACH__ (who : Player)
  301. {
  302. if (results[who] == -1)
  303. {
  304. results[who] = O@(func, interval, true, "i", who);
  305. }
  306. }
  307. }
  308. else if (results[who] == -1)
  309. {
  310. results[who] = O@(func, interval, true, "i", who);
  311. }
  312. }
  313. }
  314. // No global interval for per-player timers.
  315. return -1;
  316. }
  317. static stock Alloc:Timer_GetSingleSlot(len)
  318. {
  319. // Allocates memory and secretly appends data to the start.
  320. P:4("Timer_GetSingleSlot called: %d", len);
  321. new
  322. Alloc:slot = malloc(len + 1);
  323. if (slot == NO_ALLOC)
  324. {
  325. return NO_ALLOC;
  326. }
  327. P:5("Timer_GetSingleSlot: %d, %d, %d", _:YSI_g_sFirstSlot, _:YSI_g_sLastSlot, _:slot);
  328. // Standard linked list.
  329. if (YSI_g_sFirstSlot == NO_ALLOC)
  330. {
  331. YSI_g_sFirstSlot = slot;
  332. }
  333. else
  334. {
  335. mset(YSI_g_sLastSlot, 0, _:slot);
  336. }
  337. YSI_g_sLastSlot = slot;
  338. mset(YSI_g_sLastSlot, 0, -1);
  339. return slot;// + Alloc:1;
  340. }
  341. // Allocate memory to store a string.
  342. stock _Timer_S(string:str[])
  343. {
  344. P:3("_Timer_S called");
  345. new
  346. len = strlen(str);
  347. if (len & 0x0F)
  348. {
  349. len = (len & ~0x0F) + 32;
  350. }
  351. new
  352. Alloc:slot = Timer_GetSingleSlot(len + 1);
  353. if (slot != NO_ALLOC)
  354. {
  355. msets(slot, 1, str);
  356. }
  357. P:5("str: %d", _:slot);
  358. return _:slot + 1;
  359. }
  360. // Allocate memory to store an array.
  361. stock _Timer_A(str[], len)
  362. {
  363. P:3("_Timer_A called");
  364. new
  365. Alloc:slot = Timer_GetSingleSlot(len);
  366. if (slot != NO_ALLOC)
  367. {
  368. mseta(slot, 1, str, len);
  369. }
  370. P:5("str: %d", _:slot);
  371. return _:slot + 1;
  372. }
  373. //stock
  374. // I@ = -1;
  375. // Create the timer setup.
  376. stock _Timer_C(tt, g)
  377. {
  378. P:3("_Timer_C called: %d, %d", tt, g);
  379. //P:3("_Timer_C called: %d", tt);
  380. // This is done here for convenience.
  381. I@ = -1;
  382. // Only repeating timers are freed like this.
  383. // UPDATE: Now all timers with array parameters, regardless of repeat status
  384. // are freed like this. Only timers with no malloc aren't.
  385. if (g)
  386. {
  387. new
  388. Alloc:slot = Timer_GetSingleSlot(1);
  389. P:5("_Timer_C: slot = %d", _:slot);
  390. if (slot == NO_ALLOC)
  391. {
  392. // Not a graceful fail!
  393. return 0;
  394. }
  395. mset(slot, 1, tt);
  396. // Just so it isn't a real timer ID (or possibly isn't).
  397. slot = ~YSI_g_sFirstSlot;// ^ Alloc:-1;
  398. YSI_g_sFirstSlot = NO_ALLOC;
  399. YSI_g_sLastSlot = NO_ALLOC;
  400. return _:slot;
  401. }
  402. // Reset these variables on all timers, including self-cleaning ones.
  403. YSI_g_sFirstSlot = NO_ALLOC;
  404. YSI_g_sLastSlot = NO_ALLOC;
  405. return tt;
  406. }
  407. // Free all timer resources.
  408. stock _Timer_F(slot)
  409. {
  410. P:3("_Timer_F called");
  411. // This is done here for convenience.
  412. if (slot & 0x80000000)
  413. {
  414. new
  415. next;
  416. slot = ~slot; //^= -1;
  417. for ( ; ; )
  418. {
  419. next = mget(Alloc:slot, 0);
  420. P:6("_Timer_F: slot = %d, next = %d", slot, next);
  421. // Out of stored strings and arrays.
  422. if (next == -1)
  423. {
  424. KillTimer(mget(Alloc:slot, 1));
  425. free(Alloc:slot);
  426. break;
  427. }
  428. free(Alloc:slot);
  429. slot = next;
  430. }
  431. }
  432. else
  433. {
  434. KillTimer(slot);
  435. }
  436. return 1;
  437. }
  438. stock _Timer_H(slot)
  439. {
  440. _Timer_F(~(slot - 1));
  441. }
  442. #define TASK__%0[%1](%2) @yT_%0(g,p);@yT_%0(g,p){static s=-1;return _Timer_I(#%0,%1,g,s);}%0();public%0()
  443. //
  444. #define PTASK__%0[%1](%2) @yT_%0(g,p);@yT_%0(g,p){static s[MAX_PLAYERS]={-1,...},a[2];return _Timer_D(#%0,%1,g,p,s,a);}%0(%2);public%0(%2)
  445. #define @yT_%0\32;%1(%2) @yT_%0%1(%2)
  446. //#define @_yT%0;\32%1(%2) @_yT%0%1(%2)
  447. #define PAUSE__%0; {J@=_:@Ym:@yT_%0(0,-1);}
  448. #define RESUME__%0; {J@=_:@Ym:@yT_%0(1,-1);}
  449. #define @Ym:%0[%1](%2,-1) %0(%2,%1)
  450. #if YSI_KEYWORD(stop)
  451. #define stop STOP__
  452. #endif
  453. #if YSI_KEYWORD(defer)
  454. #define defer DEFER__
  455. #endif
  456. #if YSI_KEYWORD(repeat)
  457. #define repeat REPEAT__
  458. #endif
  459. #if YSI_KEYWORD(task)
  460. #define task%0[%1]%3(%2) TASK__%0[%1]%3(%2)
  461. #endif
  462. #if YSI_KEYWORD(ptask)
  463. #define ptask%0[%1]%3(%2) PTASK__%0[%1]%3(%2)
  464. #endif
  465. #if YSI_KEYWORD(pause)
  466. #define pause PAUSE__
  467. #endif
  468. #if YSI_KEYWORD(resume)
  469. #define resume RESUME__
  470. #endif
  471. #if YSI_KEYWORD(timerfunc)
  472. #define timerfunc%0[%1]%3(%2) TIMER__%0[%1]%3(%2)
  473. #endif
  474. #if YSI_KEYWORD(timer)
  475. #define timer%0[%1]%3(%2) TIMER__%0[%1]%3(%2)
  476. #endif