Y_Less

y_timers 3

Jun 18th, 2011
673
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 12.44 KB | None | 0 0
  1. /*============================================================================*\
  2. *==============================================================================*
  3. ================================================================================
  4. ||||                                                                        ||||
  5. ||||                         HERE BEGINS VERSION 2!                         ||||
  6. ||||                                                                        ||||
  7. ================================================================================
  8. *==============================================================================*
  9. \*============================================================================*/
  10.  
  11. #include <YSI\internal\y_version>
  12. #include <YSI\y_amx>
  13. #include <YSI\internal\y_shortfunc>
  14. #include <YSI\y_malloc>
  15. #include <YSI\y_iterate>
  16. // This will be called twice and means there are two separate OnScriptInit
  17. // functions in this file.
  18. #include <YSI\y_hooks>
  19. #include <YSI\y_debug>
  20.  
  21. #define task%0[%1](%2) %0@yT_(g,p);public%0@yT_(g,p){static s=-1;return _Timer_I(#%0,%1,g,s);}%0();public%0()
  22.  
  23. #define ptask%0[%1](%2) stock %0_yT@=1;%0@yT_(g,p);public%0@yT_(g,p){static s[MAX_PLAYERS]={-1,...},a[2];return _Timer_D(#%0@_yT,%1,g,p,s,%0_yT@,a);}%0@_yT(p);public%0@_yT(p)if(%0_yT@)%0(p);%0(%2)
  24.  
  25. #define pause%0; {J@=_:@Ym:%0@yT_(0,-1);}
  26. #define resume%0; {J@=_:@Ym:%0@yT_(1,-1);}
  27. #define @Ym:%0:%1@yT_(%2,-1) %0@yT_(%2,%1)
  28.  
  29. static stock
  30.     Alloc:YSI_g_sLastSlot = NO_ALLOC,
  31.     Alloc:YSI_g_sFirstSlot = NO_ALLOC,
  32.     YSI_g_sPlayerTimers = -1;
  33.  
  34. hook OnScriptInit()
  35. {
  36.     P:1("hook Timers_OnScriptInit called");
  37.     new
  38.         pointer,
  39.         time,
  40.         idx,
  41.         entry;
  42.     while ((idx = AMX_GetPublicEntrySuffix(idx, entry, _A<@yT_>)))
  43.     //while ((idx = AMX_GetPublicPointerSuffix(idx, pointer, _A<@yT_>)))
  44.     {
  45.         P:6("Timer_OnScriptInit: entry: %d", entry);
  46.         #emit LREF.S.pri entry
  47.         #emit STOR.S.pri pointer
  48.         //YSI_g_sCurFunc = pointer;
  49.         // Don't bother with the real name, call the function by address to get
  50.         // the time the function runs for.
  51.         P:7("Timer_OnScriptInit: pointer: %d", pointer);
  52.         // Push the address of the current function.
  53.         #emit PUSH.S     pointer
  54.         #emit PUSH.C     0xFFFFFFFF
  55.         #emit PUSH.C     8
  56.         #emit LCTRL      6
  57.         #emit ADD.C      28
  58.         #emit PUSH.pri
  59.         #emit LOAD.S.pri pointer
  60.         #emit SCTRL      6
  61.         #emit STOR.S.pri time
  62.         //YSI_g_sCurFunc = 0;
  63.         P:7("Timer_OnScriptInit: time: %d", time);
  64.         if (time != -1)
  65.         {
  66.             // Find all the functions with the same time.  This is less
  67.             // efficient than previous implementations (it is O(N^2)), but also
  68.             // more robust as it won't fail no matter how many different times
  69.             // there are - old ones relied on an array with a finite size.
  70.             new
  71.                 pointer2,
  72.                 time2,
  73.                 idx2,
  74.                 total,
  75.                 pre;
  76.             while ((idx2 = AMX_GetPublicPointerSuffix(idx2, pointer2, _A<@yT_>)))
  77.             {
  78.                 // Call the functions a second time to guarantee getting
  79.                 #emit PUSH.C     0
  80.                 #emit PUSH.C     0xFFFFFFFF
  81.                 #emit PUSH.C     8
  82.                 #emit LCTRL      6
  83.                 #emit ADD.C      28
  84.                 #emit PUSH.pri
  85.                 #emit LOAD.S.pri pointer2
  86.                 #emit SCTRL      6
  87.                 #emit STOR.S.pri time2
  88.                 if (time2 == time)
  89.                 {
  90.                     ++total;
  91.                     if (idx2 < idx)
  92.                     {
  93.                         ++pre;
  94.                     }
  95.                 }
  96.             }
  97.             P:7("Timer_OnScriptInit: total: %d, time: %d, pre: %d", total, time, pre);
  98.             // Now we know what time this function has, how many others have
  99.             // that time and how many have already been started.
  100.             new
  101.                 buffer[32];
  102.             entry += 4;
  103.             #emit LREF.S.pri entry
  104.             #emit STOR.S.pri pointer
  105.             AMX_ReadString(AMX_Base + pointer, buffer);
  106.             P:7("Timer_OnScriptInit: %s", unpack(buffer));
  107.             // Get the time offset for the current call.  This should mean that
  108.             // all the functions are nicely spread out.
  109.             SetTimerEx(buffer, time * pre / total, 0, "ii", 1, -1);
  110.         }
  111.     }
  112.     P:1("hook Timers_OnScriptInit ended");
  113. }
  114.  
  115. hook OnPlayerConnect(playerid)
  116. {
  117.     P:1("hook Timers_OnPlayerConnect called: %d", playerid);
  118.     // Loop through all the per-player timers.  Correctly finds them all from a
  119.     // linked list hidden in static variables (which are really global).
  120.     new
  121.         cur = YSI_g_sPlayerTimers,
  122.         data;
  123.     while (cur != -1)
  124.     {
  125.         #emit LREF.S.pri  cur
  126.         #emit STOR.S.pri  data
  127.         P:6("Timers_OnPlayerConnect: func: %x", data);
  128.         // Start this timer for this player.
  129.         #emit PUSH.S     playerid
  130.         #emit PUSH.C     1
  131.         // Push the parameter count (in bytes).  This is actually passed to
  132.         // native functions directly.
  133.         #emit PUSH.C     8
  134.         // Call the function currently in the list to trigger the repeating
  135.         // timer.  This involves getting the current "cip" address, modifying it
  136.         // to get the return address then modifying "cip" to call the function.
  137.         #emit LCTRL      6
  138.         #emit ADD.C      28
  139.         #emit PUSH.pri
  140.         #emit LOAD.S.pri data
  141.         #emit SCTRL      6
  142.         // Returned, get the next list element.
  143.         cur += 4;
  144.         #emit LREF.S.pri  cur
  145.         #emit STOR.S.pri  cur
  146.     }
  147.     P:1("hook Timers_OnPlayerConnect ended");
  148. }
  149.  
  150. hook OnPlayerDisconnect(playerid, reason)
  151. {
  152.     P:1("hook Timers_OnPlayerDisconnect called: %d, %d, playerid, reason");
  153.     // Loop through all the per-player timers.  Correctly finds them all from a
  154.     // linked list hidden in static variables (which are really global).
  155.     new
  156.         cur = YSI_g_sPlayerTimers,
  157.         data;
  158.     while (cur != -1)
  159.     {
  160.         #emit LREF.S.pri  cur
  161.         #emit STOR.S.pri  data
  162.         P:6("Timers_OnPlayerDisconnect: func: %x", data);
  163.         // End this timer for this player.
  164.         #emit PUSH.S     playerid
  165.         #emit PUSH.C     0
  166.         // Push the parameter count (in bytes).  This is actually passed to
  167.         // native functions directly.
  168.         #emit PUSH.C     8
  169.         // Call the function currently in the list to trigger the repeating
  170.         // timer.  This involves getting the current "cip" address, modifying it
  171.         // to get the return address then modifying "cip" to call the function.
  172.         #emit LCTRL      6
  173.         #emit ADD.C      28
  174.         #emit PUSH.pri
  175.         #emit LOAD.S.pri data
  176.         #emit SCTRL      6
  177.         // Returned, get the next list element.
  178.         cur += 4;
  179.         #emit LREF.S.pri  cur
  180.         #emit STOR.S.pri  cur
  181.     }
  182.     P:1("hook Timers_OnPlayerDisconnect ended");
  183. }
  184.  
  185. stock _Timer_I(func[], interval, action, &result)
  186. {
  187.     P:3("_Timer_I called");
  188.     switch (action)
  189.     {
  190.         case 0:
  191.         {
  192.             if (result != -1)
  193.             {
  194.                 KillTimer(result),
  195.                 result =- 1;
  196.             }
  197.         }
  198.         case 1:
  199.         {
  200.             if (result == -1)
  201.             {
  202.                 result = SetTimer(func, interval, 1);
  203.             }
  204.         }
  205.     }
  206.     return interval;
  207. }
  208.  
  209. // Attempt to stop or start a task, possibly for a single player.
  210. stock _Timer_D(func[], interval, const action, who, results[MAX_PLAYERS], &pause, a[2])
  211. {
  212.     P:3("_Timer_D called");
  213.     switch (action)
  214.     {
  215.         case -1:
  216.         {
  217.             if (who)
  218.             {
  219.                 a[0] = who;
  220.                 a[1] = YSI_g_sPlayerTimers;
  221.                 // Store the address of the global array.
  222.                 #emit LOAD.S.pri  a
  223.                 #emit STOR.pri    YSI_g_sPlayerTimers
  224.             }
  225.         }
  226.         case 0:
  227.         {
  228.             // Stop the timer.
  229.             if (who == -1)
  230.             {
  231.                 pause = 0;
  232.             }
  233.             else if (results[who] != -1)
  234.             {
  235.                 KillTimer(results[who]);
  236.                 results[who] = -1;
  237.             }
  238.         }
  239.         case 1:
  240.         {
  241.             // Start the timer.
  242.             if (who == -1)
  243.             {
  244.                 pause = 1;
  245.             }
  246.             else if (results[who] == -1)
  247.             {
  248.                 results[who] = SetTimerEx(func, interval, true, "i", who);
  249.             }
  250.         }
  251.     }
  252.     // No global interval for per-player timers.
  253.     return -1;
  254. }
  255.  
  256. static stock Alloc:Timer_GetSingleSlot(len)
  257. {
  258.     // Allocates memory and secretly appends data to the start.
  259.     P:4("Timer_GetSingleSlot called: %d", len);
  260.     new
  261.         Alloc:slot = malloc(len + 1);
  262.     if (slot == NO_ALLOC)
  263.     {
  264.         return NO_ALLOC;
  265.     }
  266.     P:5("Timer_GetSingleSlot: %d, %d, %d", _:YSI_g_sFirstSlot, _:YSI_g_sLastSlot, _:slot);
  267.     // Standard linked list.
  268.     if (YSI_g_sFirstSlot == NO_ALLOC)
  269.     {
  270.         YSI_g_sFirstSlot = slot;
  271.     }
  272.     else
  273.     {
  274.         mset(YSI_g_sLastSlot, 0, _:slot);
  275.     }
  276.     YSI_g_sLastSlot = slot;
  277.     mset(YSI_g_sLastSlot, 0, -1);
  278.     return slot;// + Alloc:1;
  279. }
  280.  
  281. // Allocate memory to store a string.
  282. stock _Timer_S(string:str[])
  283. {
  284.     P:3("_Timer_S called");
  285.     new
  286.         len = strlen(str);
  287.     if (len & 0x0F)
  288.     {
  289.         len = (len & ~0x0F) + 32;
  290.     }
  291.     new
  292.         Alloc:slot = Timer_GetSingleSlot(len + 1);
  293.     if (slot != NO_ALLOC)
  294.     {
  295.         msets(slot, 1, str);
  296.     }
  297.     P:5("str: %d", _:slot);
  298.     return _:slot + 1;
  299. }
  300.  
  301. // Allocate memory to store an array.
  302. stock _Timer_A(str[], len)
  303. {
  304.     P:3("_Timer_A called");
  305.     new
  306.         Alloc:slot = Timer_GetSingleSlot(len);
  307.     if (slot != NO_ALLOC)
  308.     {
  309.         mseta(slot, 1, str, len);
  310.     }
  311.     P:5("str: %d", _:slot);
  312.     return _:slot + 1;
  313. }
  314.  
  315. stock
  316.     I@ = -1;
  317.  
  318. // Create the timer setup.
  319. stock _Timer_C(timer, g)
  320. {
  321.     P:3("_Timer_C called: %d, %d", timer, g);
  322.     //P:3("_Timer_C called: %d", timer);
  323.     // This is done here for convenience.
  324.     I@ = -1;
  325.     // Only repeating timers are freed like this.
  326.     // UPDATE: Now all timers with array parameters, regardless of repeat status
  327.     // are freed like this.  Only timers with no malloc aren't.
  328.     if (g)
  329.     {
  330.         new
  331.             Alloc:slot = Timer_GetSingleSlot(1);
  332.         P:5("_Timer_C: slot = %d", _:slot);
  333.         if (slot == NO_ALLOC)
  334.         {
  335.             // Not a graceful fail!
  336.             return 0;
  337.         }
  338.         mset(slot, 1, timer);
  339.         // Just so it isn't a real timer ID (or possibly isn't).
  340.         slot = ~YSI_g_sFirstSlot;// ^ Alloc:-1;
  341.         YSI_g_sFirstSlot = NO_ALLOC;
  342.         YSI_g_sLastSlot = NO_ALLOC;
  343.         return _:slot;
  344.     }
  345.     // Reset these variables on all timers, including self-cleaning ones.
  346.     YSI_g_sFirstSlot = NO_ALLOC;
  347.     YSI_g_sLastSlot = NO_ALLOC;
  348.     return timer;
  349. }
  350.  
  351. // Free all timer resources.
  352. stock _Timer_F(slot)
  353. {
  354.     P:3("_Timer_F called");
  355.     // This is done here for convenience.
  356.     if (slot & 0x80000000)
  357.     {
  358.         new
  359.             next;
  360.         slot = ~slot; //^= -1;
  361.         for ( ; ; )
  362.         {
  363.             next = mget(Alloc:slot, 0);
  364.             P:6("_Timer_F: slot = %d, next = %d", slot, next);
  365.             // Out of stored strings and arrays.
  366.             if (next == -1)
  367.             {
  368.                 KillTimer(mget(Alloc:slot, 1));
  369.                 free(Alloc:slot);
  370.                 break;
  371.             }
  372.             free(Alloc:slot);
  373.             slot = next;
  374.         }
  375.     }
  376.     else
  377.     {
  378.         KillTimer(slot);
  379.     }
  380.     return 1;
  381. }
  382.  
  383. // Free one timer resource.
  384. /*stock _Timer_H(slot)
  385. {
  386.     P:3("_Timer_H called");
  387.     // This is done here for convenience.
  388.     free(Alloc:(slot - 1));
  389. }*/
  390.  
  391. // Free one timer resource.  Now actually frees many, not just one.
  392. /*stock _Timer_H(...)
  393. {
  394.     P:3("_Timer_H called");
  395.     new
  396.         c = numargs();
  397.     while (c--)
  398.     {
  399.         // This is done here for convenience.
  400.         free(Alloc:(getarg(c) - 1));
  401.     }
  402. }*/
  403.  
  404. stock _Timer_H(slot)
  405. {
  406.     _Timer_F(~(slot - 1));
  407. }
  408.  
  409. // These are the tag-type definitions for the various possible parameter types.
  410. // Array-like definitions.
  411. #define @Yf:@Yg:@Yh:#%0#%1|||%3[%4]|||%5,%6;%8>%9||| @Ye:@Yw:#%0#%1|||%3[%4]|||%5,%6;%9|||
  412.  
  413. #define @Ye:@Yw:#%0#%1|||%2string:%3[%4]|||%5,%6;%9||| @Yf:@Yg:@Yh:#%0d#%1,_Timer_S(%3)|||%5|||%6;1>%9,@Yv&%3&|||
  414.  
  415. // This one has an extra parameter because arrays must always be followed by
  416. // their length.
  417. #define @Yw:#%0#%1|||%3[%4]|||%5,%6,%7;%9||| @Yf:@Yg:@Yh:#%0d#%1,_Timer_A(%3,%5)|||%5|||%6,%7;1>%9,@Yv&%3&|||
  418.  
  419. // Tag-like definitions.
  420. #define @Yg:@Yh:#%0#%1|||%2:%3|||%5,%6;%8>%9||| @Yf:@Yg:@Yh:#%0d#%1,%2:%3|||%5|||%6;%8>%9,%2:%3|||
  421.  
  422. // Others.
  423. #define @Yh:#%0#%1|||%3|||%5,%6;%8>%9||| @Yf:@Yg:@Yh:#%0d#%1,%3|||%5|||%6;%8>%9,%3|||
  424.  
  425. // Main entry point for defer type determination.
  426. #define _YT@CR:%0,%1)%8>%2||| @Yf:@Yg:@Yh:#i#,@Yx:J@|||%0|||%1;%8>%2|||
  427.  
  428. // Define for "defer" with timer, parameters and main function.
  429. #define timer%0[%1]%3(%2) stock%0_yT@(%2)return _Timer_C(O@(#%0@_yT,I@==-1?(%1):I@,J@,_YT@CR:%2,,)0>%0|||%0|||(%2)
  430.  
  431. // Expand additional parameters out to three functions after processing.
  432. //#define @Yx:%0||||||;%1,%2|||%4||| %0),1);@Yu:%1@_yT(%2);public @Yu:%1@_yT(%2){%4(%2);}%4
  433. #define @Yx:%0||||||;%8>%1,%2|||%4||| %0),%8);@Yj:%1@_yT(__r,%2);public @Yj:%1@_yT(__r,%2){%4(%2);}%4
  434.  
  435. // Can't believe I never thought of this before!  If only there was a way to
  436. // make it less generic than "id".
  437. #define id#,@Yx:J@,||||||;%8>%1,%2|||%4||| ),0);%1@_yT();public%1@_yT(){%4();}%4
  438.  
  439. // Remove excess "_Timer_G" and "_Timer_B".
  440. #define @Yj:%0{%1(%8&%2&%3)%9} @Yj:%0{%1(%8:YSI_gMallocMemory[%2]%3);@Yl(@Yk:%2)%9}
  441. //#define @Yu:%0{%1(%8&%2&%3)%9} @Yu:%0{%1(%8:YSI_gMallocMemory[%2]%3)%9}
  442. #define @Yv&%0& %0
  443.  
  444. //#define @Yk:%0);@Yl(@Yk:%1); %0,@Yk:%1);
  445. // I'm not entirely sure why this works in reverse order, but I'm glad it does!
  446. #define @Yk:%0);@Yl(@Yk:%1); @Yk:%0);
  447. #define @Yl if(!__r)_Timer_H
  448.  
  449. #define string:
  450.  
  451. #define defer%0(%1) (J@=0,Timer:%0_yT@(%1))
  452.  
  453. #define repeat%0(%1) (J@=1,Timer:%0_yT@(%1))
  454.  
  455. #define Timer:%0[%1]_yT@(%2) I@=%1,Timer:%0_yT@(%2)
  456.  
  457. #define stop%0; {_Timer_F(_:%0);}
Advertisement
Add Comment
Please, Sign In to add comment