You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

core.lua 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. --
  2. -- Minetest lurkcoin mod - Core
  3. --
  4. -- © 2019 by luk3yx
  5. --
  6. lurkcoin.version = 0
  7. lurkcoin.timeout = 10
  8. lurkcoin.exchange_rate = 1
  9. -- Do not allow other mods to modify this, or they may be able to bypass mod
  10. -- security restrictions!
  11. local baseurl = 'https://nz.xeroxirc.net:7000/v2'
  12. -- Get the username and API token
  13. lurkcoin.server_name = minetest.settings:get('lurkcoin.username')
  14. local token = minetest.settings:get('lurkcoin.token')
  15. -- Make sure lurkcoin.server_name exists
  16. if not lurkcoin.server_name then
  17. lurkcoin.server_name = '<unknown>'
  18. minetest.log('warning', 'lurkcoin has no server name set!')
  19. end
  20. -- Make sure the HTTP API exists
  21. local http = ...
  22. if not http then
  23. minetest.log('warning', 'lurkcoin is not allowed to use the HTTP API! ' ..
  24. 'Please add lurkcoin to secure.http_mods in minetest.conf.')
  25. end
  26. -- Download functions
  27. lurkcoin.user_agent = 'Minetest ' .. minetest.get_version().string ..
  28. ' (with lurkcoin mod v' .. tostring(lurkcoin.version) .. ')'
  29. local function get(url, data, callback)
  30. if not data then
  31. data = {}
  32. elseif not http then
  33. return callback({
  34. completed = true,
  35. succeeded = false,
  36. timeout = true,
  37. code = 500,
  38. data = 'ERROR: The lurkcoin mod is not in secure.http_mods!'
  39. })
  40. elseif not lurkcoin.server_name or not token then
  41. return callback({
  42. completed = true,
  43. succeeded = false,
  44. timeout = true,
  45. code = 401,
  46. data = 'ERROR: The lurkcoin mod does not have (correct) ' ..
  47. 'account credentials!'
  48. })
  49. end
  50. data.name = lurkcoin.server_name
  51. data.token = token
  52. -- Minetest eats any non-200 code.
  53. data.force_200 = '200'
  54. http.fetch({
  55. url = baseurl .. '/' .. url,
  56. timeout = lurkcoin.timeout,
  57. post_data = data,
  58. user_agent = lurkcoin.user_agent,
  59. }, function(res)
  60. if res.timeout then
  61. minetest.log('warning', '[lurkcoin] Could not connect to lurkcoin!')
  62. res.code = 500
  63. res.data = 'ERROR: Could not connect to lurkcoin!'
  64. elseif res.code == 401 or res.code == 418 then
  65. minetest.log('warning', '[lurkcoin] Invalid username or API token!')
  66. lurkcoin.server_name, token = '<unknown>', nil
  67. elseif res.code == 200 and res.data:sub(1, 7) == 'ERROR: ' then
  68. res.code = 501
  69. end
  70. local success, msg = pcall(callback, res)
  71. if not success then
  72. minetest.log('error', '[lurkcoin] Error: ' .. msg)
  73. end
  74. end)
  75. end
  76. -- Process incoming transactions.
  77. local handled_transactions = {}
  78. local function _sync(res)
  79. if res.code == 200 then
  80. local data = minetest.parse_json(res.data)
  81. -- Update the exchange rate
  82. assert(type(data.exchange_rate) == 'number')
  83. assert(data.exchange_rate == data.exchange_rate)
  84. lurkcoin.exchange_rate = data.exchange_rate
  85. -- Process any unprocessed transactions
  86. for _, t in ipairs(data.transactions) do
  87. assert(type(t[3]) == 'number')
  88. local id = minetest.serialize(t)
  89. if not handled_transactions[id] then
  90. handled_transactions[id] = true
  91. core.log('action', '[lurkcoin] ' .. t[4])
  92. lurkcoin.bank.add(t[2], t[3])
  93. end
  94. end
  95. -- Tell lurkcoin to remove processed transactions
  96. if #data.transactions > 0 then
  97. get('remove_transactions', {count = tostring(#data.transactions)},
  98. function(res)
  99. if res.code == 200 then
  100. for _, t in ipairs(data.transactions) do
  101. handled_transactions[minetest.serialize(t)] = nil
  102. end
  103. end
  104. end)
  105. end
  106. end
  107. end
  108. -- Periodically sync with lurkcoin.
  109. local function sync()
  110. get('get_transactions', {as_object = 'true'}, _sync)
  111. minetest.after(300, sync)
  112. end
  113. -- Start syncing once the game is loaded.
  114. minetest.after(0, sync)
  115. -- Get an exchange rate
  116. function lurkcoin.get_exchange_rate(amount, to, callback)
  117. assert(callback)
  118. amount = amount and tonumber(amount)
  119. if not amount or amount ~= amount then return callback(nil) end
  120. get('exchange_rates', {
  121. from = lurkcoin.server_name,
  122. to = to or 'lurkcoin',
  123. amount = tostring(amount),
  124. }, function(res)
  125. if res.code == 200 then
  126. local amount = tonumber(res.data)
  127. if amount == amount then return callback(amount) end
  128. end
  129. return callback(nil)
  130. end)
  131. end
  132. -- Pay a user (cross-server)
  133. function lurkcoin.pay(from, to, server, amount, callback)
  134. assert(type(amount) == 'number' and callback)
  135. -- Run lurkcoin.bank.pay() if this is not a cross-server transaction.
  136. if not server or server == '' or server:lower() ==
  137. lurkcoin.server_name:lower() then
  138. return callback(lurkcoin.bank.pay(from, to, amount))
  139. end
  140. -- Sanity checks
  141. amount = math.floor(amount * 100) / 100
  142. if not lurkcoin.bank.user_exists(from) then
  143. return callback(false, 'ERROR: The "from" user does not exist!')
  144. elseif amount ~= amount or amount <= 0 then
  145. return callback(false, 'ERROR: Invalid number!')
  146. elseif lurkcoin.bank.getbal(from) - amount < 0 then
  147. return callback(false, 'ERROR: You cannot afford to do that!')
  148. end
  149. if not lurkcoin.bank.subtract(from, amount, 'Transaction to "' .. to ..
  150. '" on server "' .. server .. '".') then
  151. return callback(false, 'ERROR: Transaction failed!')
  152. end
  153. -- Send the request
  154. return get('pay', {
  155. target = to,
  156. server = server,
  157. amount = tostring(amount),
  158. local_currency = 'true'
  159. }, function(res)
  160. if res.code ~= 200 then
  161. lurkcoin.bank.add(from, amount, 'Reverting failed transaction.')
  162. if res.data == 'ERROR: You cannot afford to do that!' then
  163. res.data = 'ERROR: This server cannot afford to do that!'
  164. end
  165. else
  166. minetest.log('action', '[lurkcoin] User ' .. from .. ' paid ' ..
  167. to .. ' (on server ' .. server .. ') ' .. tostring(amount) .. 'cr.')
  168. end
  169. return callback(res.code == 200, res.data or 'ERROR: Unknown error!')
  170. end)
  171. end
  172. -- Nuke lurkcoin.modpath
  173. lurkcoin.modpath = nil