GlobalEvent Anti-Dupe - Delete all items with duplicated serial from your database.

Discussion in 'GlobalEvents, Spells & CreatureEvents' started by Darkhaos, Aug 3, 2011.

  1. Haynz

    Haynz New Member

    Joined:
    Jan 7, 2011
    Messages:
    53
    Likes Received:
    1
    Best Answers:
    0
    Hello want to know where I can add that command on my item I want to have the serial.
    doItemSetAttribute (uid, "serial", generateSerial ())

    I thank
     
  2. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    Depens of the system you're using (Quest, shop....)
     
  3. Haynz

    Haynz New Member

    Joined:
    Jan 7, 2011
    Messages:
    53
    Likes Received:
    1
    Best Answers:
    0
    Look, I want to shop (Use Gesior)
    Could you help me?
    possible to add e msn
    [email protected]
     
  4. Haynz

    Haynz New Member

    Joined:
    Jan 7, 2011
    Messages:
    53
    Likes Received:
    1
    Best Answers:
    0
    Or it may be to separate item
     
  5. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    How do I add this to my Shop System?
    Items from Shop to have the Serial...
     
  6. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    Post your shop system here (The script that gives the item)

    Or edit your shop system with something like this
    Code (Lua):
    1.     local new_item = doCreateItemEx(itemtogive_id, itemtogive_count)
    2.     doItemSetAttribute(new_item, "serial", generateSerial())
    3.     doPlayerAddItemEx(cid, new_item)
     
  7. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    Code (Text):
    1. -- ### CONFIG ###
    2. -- message send to player by script "type" (types you can check in "global.lua")
    3. SHOP_MSG_TYPE = 19
    4. -- time (in seconds) between connections to SQL database by shop script
    5. SQL_interval = 5
    6. -- ### END OF CONFIG ###
    7. function onThink(interval, lastExecution)
    8. local result_plr = db.getResult("SELECT * FROM z_ots_comunication WHERE `type` = 'login';")
    9. if(result_plr:getID() ~= -1) then
    10. while(true) do
    11. id = tonumber(result_plr:getDataInt("id"))
    12. action = tostring(result_plr:getDataString("action"))
    13. delete = tonumber(result_plr:getDataInt("delete_it"))
    14. cid = getCreatureByName(tostring(result_plr:getDataString("name")))
    15. if isPlayer(cid) == TRUE then
    16. local itemtogive_id = tonumber(result_plr:getDataInt("param1"))
    17. local itemtogive_count = tonumber(result_plr:getDataInt("param2"))
    18. local container_id = tonumber(result_plr:getDataInt("param3"))
    19. local container_count = tonumber(result_plr:getDataInt("param4"))
    20. local add_item_type = tostring(result_plr:getDataString("param5"))
    21. local add_item_name = tostring(result_plr:getDataString("param6"))
    22. local received_item = 0
    23. local full_weight = 0
    24. if add_item_type == 'container' then
    25. container_weight = getItemWeightById(container_id, 1)
    26. if isItemRune(itemtogive_id) == TRUE then
    27. items_weight = container_count * getItemWeightById(itemtogive_id, 1)
    28. else
    29. items_weight = container_count * getItemWeightById(itemtogive_id, itemtogive_count)
    30. end
    31. full_weight = items_weight + container_weight
    32. else
    33. full_weight = getItemWeightById(itemtogive_id, itemtogive_count)
    34. if isItemRune(itemtogive_id) == TRUE then
    35. full_weight = getItemWeightById(itemtogive_id, 1)
    36. else
    37. full_weight = getItemWeightById(itemtogive_id, itemtogive_count)
    38. end
    39. end
    40. local free_cap = getPlayerFreeCap(cid)
    41. if full_weight <= free_cap then
    42. if add_item_type == 'container' then
    43. local new_container = doCreateItemEx(container_id, 1)
    44. local iter = 0
    45. while iter ~= container_count do
    46. doAddContainerItem(new_container, itemtogive_id, itemtogive_count)
    47. iter = iter + 1
    48. end
    49. received_item = doPlayerAddItemEx(cid, new_container)
    50. else
    51. local new_item = doCreateItemEx(itemtogive_id, itemtogive_count)
    52. received_item = doPlayerAddItemEx(cid, new_item)
    53. end
    54. if received_item == RETURNVALUE_NOERROR then
    55. doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, 'You received '.. add_item_name ..' from Thormenta Shop.')
    56. db.executeQuery("DELETE FROM `z_ots_comunication` WHERE `id` = " .. id .. ";")
    57. db.executeQuery("UPDATE `z_shop_history_item` SET `trans_state`='realized', `trans_real`=" .. os.time() .. " WHERE id = " .. id .. ";")
    58. else
    59. doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, ''.. add_item_name ..' from Thormenta Shop is waiting for you. Please make place for this item in your backpack/hands and wait about '.. SQL_interval ..' seconds to get it.')
    60. end
    61. else
    62. doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, ''.. add_item_name ..' from Thormenta Shop is waiting for you. It weight is '.. full_weight ..' oz., you have only '.. free_cap ..' oz. free capacity. Put some items in depot and wait about '.. SQL_interval ..' seconds to get it.')
    63. end
    64. end
    65. if not(result_plr:next()) then
    66. break
    67. end
    68. end
    69. result_plr:free()
    70. end
    71. return TRUE
    72. end
     
  8. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    Code (Lua):
    1. -- ### CONFIG ###
    2. -- message send to player by script "type" (types you can check in "global.lua")
    3. SHOP_MSG_TYPE = 19
    4. -- time (in seconds) between connections to SQL database by shop script
    5. SQL_interval = 5
    6. -- ### END OF CONFIG ###
    7. function onThink(interval, lastExecution)
    8.     local result_plr = db.getResult("SELECT * FROM z_ots_comunication WHERE `type` = 'login';")
    9.     if(result_plr:getID() ~= -1) then
    10.         while(true) do
    11.             id = tonumber(result_plr:getDataInt("id"))
    12.             action = tostring(result_plr:getDataString("action"))
    13.             delete = tonumber(result_plr:getDataInt("delete_it"))
    14.             cid = getCreatureByName(tostring(result_plr:getDataString("name")))
    15.             if isPlayer(cid) == TRUE then
    16.                 local itemtogive_id = tonumber(result_plr:getDataInt("param1"))
    17.                 local itemtogive_count = tonumber(result_plr:getDataInt("param2"))
    18.                 local container_id = tonumber(result_plr:getDataInt("param3"))
    19.                 local container_count = tonumber(result_plr:getDataInt("param4"))
    20.                 local add_item_type = tostring(result_plr:getDataString("param5"))
    21.                 local add_item_name = tostring(result_plr:getDataString("param6"))
    22.                 local received_item = 0
    23.                 local full_weight = 0
    24.                 if add_item_type == 'container' then
    25.                     container_weight = getItemWeightById(container_id, 1)
    26.                     if isItemRune(itemtogive_id) == TRUE then
    27.                         items_weight = container_count * getItemWeightById(itemtogive_id, 1)
    28.                     else
    29.                         items_weight = container_count * getItemWeightById(itemtogive_id, itemtogive_count)
    30.                     end
    31.                     full_weight = items_weight + container_weight
    32.                 else
    33.                     full_weight = getItemWeightById(itemtogive_id, itemtogive_count)
    34.                     if isItemRune(itemtogive_id) == TRUE then
    35.                         full_weight = getItemWeightById(itemtogive_id, 1)
    36.                     else
    37.                         full_weight = getItemWeightById(itemtogive_id, itemtogive_count)
    38.                     end
    39.                 end
    40.                 local free_cap = getPlayerFreeCap(cid)
    41.                 if full_weight <= free_cap then
    42.                     if add_item_type == 'container' then
    43.                         local new_container = doCreateItemEx(container_id, 1)
    44.                         local iter = 0
    45.                         while iter ~= container_count do
    46.                             doAddContainerItem(new_container, itemtogive_id, itemtogive_count)
    47.                             iter = iter + 1
    48.                         end
    49.                         received_item = doPlayerAddItemEx(cid, new_container)
    50.                     else
    51.                         local new_item = doCreateItemEx(itemtogive_id, itemtogive_count)
    52.                         doItemSetAttribute(new_item, "serial", generateSerial())
    53.                         received_item = doPlayerAddItemEx(cid, new_item)
    54.                     end
    55.                     if received_item == RETURNVALUE_NOERROR then
    56.                         doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, 'You received '.. add_item_name ..' from Thormenta Shop.')
    57.                         db.executeQuery("DELETE FROM `z_ots_comunication` WHERE `id` = " .. id .. ";")
    58.                         db.executeQuery("UPDATE `z_shop_history_item` SET `trans_state`='realized', `trans_real`=" .. os.time() .. " WHERE id = " .. id .. ";")
    59.                     else
    60.                         doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, ''.. add_item_name ..' from Thormenta Shop is waiting for you. Please make place for this item in your backpack/hands and wait about '.. SQL_interval ..' seconds to get it.')
    61.                     end
    62.                 else
    63.                     doPlayerSendTextMessage(cid, SHOP_MSG_TYPE, ''.. add_item_name ..' from Thormenta Shop is waiting for you. It weight is '.. full_weight ..' oz., you have only '.. free_cap ..' oz. free capacity. Put some items in depot and wait about '.. SQL_interval ..' seconds to get it.')
    64.                 end
    65.             end
    66.             if not(result_plr:next()) then
    67.                 break
    68.             end
    69.         end
    70.         result_plr:free()
    71.     end
    72.     return TRUE
    73. end
    For now it gives serial to items that isn't in containers.
     
  9. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    Thanks bro.. I just got a problem here..

    No console errors... nothing.

    I created a subtable (serial) at player_items. And when I buy the item no serial is generated ;/

    Could you add me on MSN or some chat tool?
    [email protected]

    Thanks.
     
  10. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    Serial is saved at items attributes, subtable 'attributes'
     
  11. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    Got it working! Thanks man!
     
  12. Kayan

    Kayan Well-Known Member

    Joined:
    Sep 19, 2007
    Messages:
    1,561
    Likes Received:
    36
    Best Answers:
    0
    it wouldn't works with rings and others itens that already have atributes, because it's static, always search GROUP BY SUBSTRING( CONVERT( attributesUSING latin1 ) FROM 18 )
     
  13. SnakevL

    SnakevL Member

    Joined:
    Jun 5, 2008
    Messages:
    67
    Likes Received:
    0
    Best Answers:
    0
    There's some way to it works with items that already have attributes like attack, defense, description, weight, etc?
     
  14. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    Not yet, i'l try to make it works on items with more than one attribute
     
  15. russogracie

    russogracie Member

    Joined:
    Sep 9, 2010
    Messages:
    205
    Likes Received:
    1
    Best Answers:
    0
    Darkhaos But, to play the item in a home or on the ground he lost the serial?

    I tested this and it happened, my doing something wrong or is it a bug?
     
  16. Optus

    Optus GeorgeB.

    Joined:
    Aug 30, 2011
    Messages:
    390
    Likes Received:
    12
    Best Answers:
    0
    Thanks
    +rep
     
  17. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    [22:28:09.416] mysql_real_query(): SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM player_items WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM player_items WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 1) - MYSQL ERROR: Lost connection to MySQL server during query (2013)

    Was using for a while... Now I got this error... Any idea?
     
  18. ShinCTL

    ShinCTL Member

    Joined:
    Apr 7, 2009
    Messages:
    35
    Likes Received:
    1
    Best Answers:
    0
    No support here anymore?

    K, thanks.
     
  19. BeniS

    BeniS Well-Known Member

    Joined:
    Aug 8, 2009
    Messages:
    1,849
    Likes Received:
    182
    Best Answers:
    0
    Made a few fixes to this:
    • Removes all duplicated items but 1 (instead of them all).
    • Fixed possible memory leaks with db connection (wasn't freeing queries).
    • Tidy a few things off, not going to create a new local inside the loops.
    • Some text format changes.

    Dunno if Darkhaos minds, but here's a tweaked version for anyone that wants the above features.

    Code (Lua):
    1.  
    2. --[[
    3.     Known issues:
    4.     -it wouldn't works with rings and others itens that already have atributes,
    5.     because it's static, always search GROUP BY SUBSTRING( CONVERT( attributesUSING latin1 ) FROM 18 )
    6.    
    7.     -This error can occur,
    8.     [22:28:09.416] mysql_real_query(): SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS
    9.     'track' FROM player_items WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN
    10.     (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM player_items WHERE
    11.     CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING (CONVERT(attributes USING latin1)
    12.     FROM 18) HAVING COUNT(*) > 1) - MYSQL ERROR: Lost connection to MySQL server during query (2013)
    13. ]]
    14.  
    15. function onStartup()
    16.    
    17.     tablesToCheck = {"player_items", "player_depotitems", "tile_items", {"player_items", {"player_depotitems", "tile_items"}}, {"player_depotitems", "tile_items"}}
    18.     local text, final = "", ""
    19.     local filex = "data/logs/duplicated.txt"
    20.     local f = io.open(filex, "a+")
    21.     local delete, delete2, query, query_ = nil, nil, nil, nil
    22.     local count, tmpCount = 0, 0
    23.    
    24.     for i = 1, table.maxn(tablesToCheck) do
    25.         if type(tablesToCheck[i]) == "string" then
    26.             query = db.getResult("SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM " .. tablesToCheck[i] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM " .. tablesToCheck[i] .. " WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 1)")
    27.             if query:getID() ~= -1 then
    28.                 tmpCount = query:getRows()
    29.                 while(true) do
    30.                     delete = db.executeQuery("DELETE FROM " .. tablesToCheck[i] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) = " .. db.escapeString(query:getDataString("track")) .. " LIMIT 1;")
    31.                     count = (delete and count + 1 or count)
    32.                     text = "[!] -> Deleting items with duplicated serial from '" .. tablesToCheck[i] .. "': [Player: " .. getPlayerNameByGUID(query:getDataInt("player_id")) .. ", Item: " .. query:getDataInt("itemtype") .. ", Count: " .. query:getDataInt("count") .. ", Serial: " .. query:getDataString("track") .."]... " .. (delete and "Success!" or "Failed!")
    33.                     final = final .. (final ~= "" and "\n" or "") .. text
    34.                     tmpCount = tmpCount - 1
    35.                     if (not query:next() or tmpCount <= 1) then break end
    36.                 end
    37.                 query:free()
    38.             end
    39.         else
    40.             if type(tablesToCheck[i][2]) == "string" then
    41.                 query = db.getResult("SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM " .. tablesToCheck[i][1] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM " .. tablesToCheck[i][2] .. " WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 0)")
    42.                 if query:getID() ~= -1 then
    43.                     tmpCount = query:getRows()
    44.                     while(true) do
    45.                         query_ = db.getResult("SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM " .. tablesToCheck[i][2] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM " .. tablesToCheck[i][1] .. " WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 0)")
    46.                         delete = db.executeQuery("DELETE FROM" .. tablesToCheck[i][1] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) = " .. db.escapeString(query:getDataString("track")) .. " LIMIT 1;")
    47.                         count = (delete and count + 1 or count)
    48.                         delete2 = db.executeQuery("DELETE FROM " .. tablesToCheck[i][2] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) = " .. db.escapeString(query:getDataString("track")) .. " LIMIT 1;")
    49.                         count = (delete2 and count + 1 or count)
    50.                         text = "[!] -> Deleting item with duplicated serial from '" .. tablesToCheck[i][1] .. "' [Player: " .. getPlayerNameByGUID(query:getDataInt("player_id")) .. ", Item: " .. query:getDataInt("itemtype") .. ", Count: " .. query:getDataInt("count") .. ", Serial: " .. query:getDataString("track") .."]... " .. (delete and "Success!" or "Failed!") ..
    51.                         "\n[!] -> Deleting item with duplicated serial from '" .. tablesToCheck[i][2] .. "' [Player: " .. getPlayerNameByGUID(query_:getDataInt("player_id")) .. ", Item: " .. query_:getDataInt("itemtype") .. ", Count: " .. query_:getDataInt("count") .. ", Serial: " .. query_:getDataString("track") .."]... " .. (delete and "Success!" or "Failed!")
    52.                         final = final .. (final ~= "" and "\n" or "") .. text
    53.                         tmpCount = tmpCount - 1
    54.                         if (query_:getID() ~= -1) then query_:free() end
    55.                         if (not query:next() or tmpCount <= 1) then break end
    56.                     end
    57.                     query:free()
    58.                 end
    59.             else
    60.                 for j = 1, #tablesToCheck[i][2] do
    61.                     query = db.getResult("SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM " .. tablesToCheck[i][1] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM " .. tablesToCheck[i][2][j] .. " WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 0)")
    62.                     if query:getID() ~= -1 then
    63.                         tmpCount = query:getRows()
    64.                         while(true) do
    65.                             query_ = db.getResult("SELECT *, SUBSTRING(CONVERT(attributes USING latin1) FROM 18) AS 'track' FROM " .. tablesToCheck[i][2][j] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) IN (SELECT SUBSTRING(CONVERT(attributes USING latin1) FROM 18) FROM " .. tablesToCheck[i][1] .. " WHERE CONVERT(attributes USING latin1) LIKE '%serial%' GROUP BY SUBSTRING(CONVERT(attributes USING latin1) FROM 18) HAVING COUNT(*) > 0)")
    66.                             delete = db.executeQuery("DELETE FROM " .. tablesToCheck[i][1] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) = " .. db.escapeString(query:getDataString("track")) .. " LIMIT 1;")
    67.                             count = (delete and count + 1 or count)
    68.                             delete2 = db.executeQuery("DELETE FROM " .. tablesToCheck[i][2][j] .. " WHERE SUBSTRING(CONVERT(attributes USING latin1) FROM 18) = " .. db.escapeString(query:getDataString("track")) .. " LIMIT 1;")
    69.                             count = (delete2 and count + 1 or count)
    70.                             text = "[!] -> Deleting item with duplicated serial from '" .. tablesToCheck[i][1] .. "' [Player: " .. getPlayerNameByGUID(query:getDataInt("player_id")) .. ", Item: " .. query:getDataInt("itemtype") .. ", Count: " .. query:getDataInt("count") .. ", Serial: " .. query:getDataString("track") .."]... " .. (delete and "Success!" or "Failed!") ..
    71.                             "\n[!] -> Deleting item with duplicated serial from '" .. tablesToCheck[i][2][j] .. "' [Player: " .. getPlayerNameByGUID(query_:getDataInt("player_id")) .. ", Item: " .. query_:getDataInt("itemtype") .. ", Count: " .. query_:getDataInt("count") .. ", Serial: " .. query_:getDataString("track") .."]... " .. (delete and "Success!" or "Failed!")
    72.                             final = final .. (final ~= "" and "\n" or "") .. text
    73.                             tmpCount = tmpCount - 1
    74.                             if (query_:getID() ~= -1) then query_:free() end
    75.                             if (not query:next() or tmpCount <= 1) then break end
    76.                         end
    77.                         query:free()
    78.                     end
    79.                 end
    80.             end
    81.         end
    82.     end
    83.     if f ~= nil then
    84.         print("["..os.date("%X", os.time())..".TMI] >> Item Tracker: " .. count .. " duplicated items have been deleted...")
    85.         f:write("[" .. os.date("%d %B %Y %X ", os.time()) .. "] >> [Item Tracker] " .. count .. " duplicated items have been deleted from the database.\n" .. (final == "" and "[!] -> No duplicated item was found in the database" or final) .. "\n\n")
    86.         f:close()
    87.     else
    88.         print("[["..os.date("%X", os.time())..".TMI] >> [Item Tracker] Cannot save info to file!")
    89.     end
    90.     return true
    91. end
    92.  
    Tested in 0.3.7 revs
     
    Last edited: Oct 8, 2011
  20. Darkhaos

    Darkhaos (:

    Joined:
    Apr 17, 2008
    Messages:
    1,922
    Likes Received:
    172
    Best Answers:
    0
    1. I made it removes all the items, because with this script you can't know which is the original and wich is the duplicated
    2. Freeing queries wion't fix that (Maybe fix it for some users only).

    Anyways thanks for these features that you added/modified.
     

Share This Page

Loading...