Y_Less

y_uvar

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