Browse Source

Initial commit

development
luk3yx 9 months ago
commit
3ca3303756
16 changed files with 971 additions and 0 deletions
  1. +21
    -0
      LICENSE.md
  2. +60
    -0
      README.md
  3. +357
    -0
      atm-core.lua
  4. +28
    -0
      atm-nodes.lua
  5. +257
    -0
      bank.lua
  6. +173
    -0
      core.lua
  7. +7
    -0
      depends.txt
  8. +31
    -0
      init.lua
  9. +33
    -0
      minibank.lua
  10. +2
    -0
      mod.conf
  11. +2
    -0
      settingtypes.txt
  12. BIN
      textures/lurkcoin_atm_side.png
  13. BIN
      textures/lurkcoin_atm_top.png
  14. BIN
      textures/lurkcoin_error.png
  15. BIN
      textures/lurkcoin_processing.png
  16. BIN
      textures/lurkcoin_success.png

+ 21
- 0
LICENSE.md View File

@@ -0,0 +1,21 @@
# The MIT License (MIT)

Copyright © 2019 by luk3yx

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 60
- 0
README.md View File

@@ -0,0 +1,60 @@
# The official Minetest lurkcoin mod

Adds (cross-server) ATMs to Minetest.

**This mod is currently in beta**, *and this README probably makes no sense.*

## How to use/install

After installing, you will need to add `lurkcoin` to `secure.http_mods` in
minetest.conf to allow cross-server transactions. The minetest.conf settings
`lurkcoin.username` and `lurkcoin.token` must be set to the username and token
you get from the lurkcoin website, and should not contain leading or trailing
spaces.

## API

The following lurkcoin-specific functions and variables exist:

- `lurkcoin.exchange_rate`: The exchange rate, *do not modify this!*
- `lurkcoin.pay(from, to, server, amount, callback)`: Makes `from` pay someone
`amount`cr, and calls `callback` on success/faliure. `callback` should have two
parameters, `success` and `msg`.
- `lurkcoin.server_name`: The account name used to log into lurkcoin.

### Bank API

This mod creates a "universal"™ bank API.

The below functions (except `getbal` and `pay`) return `true` on success and
`false` on failure.

- `lurkcoin.bank.getbal(name)`: Gets a balance
- `lurkcoin.bank.setbal(name, balance, reason)`: Sets a balance
- `lurkcoin.bank.user_exists(name)`: Checks if a user exists.
- `lurkcoin.bank.changebal(name, amount, reason)`: Changes a balance (use
either positive or negative numbers)
- `lurkcoin.bank.add(name, amount, reason)`: Adds `amount` to `name`'s balance.
Will return false if `amount` is below `0`.
- `lurkcoin.bank.subtract(name, amount, reason)`: Subtracts `amount` from
`name`'s balance. Will return false if `amount` is below `0`.
- `lurkcoin.bank.pay(from, to, amount)`: Makes user `from` pay a user
`amount`cr. This will return two values, `success` (true/false) and
`message` (a string).

#### Changing the currently used bank

By default, this mod will check for a few bank mods, and if it doesn't find any
will use the built-in (≈34 line) `minibank.lua`. `minibank` does not store any
transaction history and will disregard the `reason` parameter when updating
balances.

To change the currently used bank, use `lurkcoin.change_bank(bank)`, where
`bank` is a table containing at least `getbal`, `setbal` and `user_exists`.
`getbal` should not create users on balance checking, and if it returns `nil`
for non-existent users, `user_exists` is not required. Any other functions
implemented (except `pay`) must have the same return values as described in this
documentation, and are entirely optional (they will be automatically created if
they are not included). Any missing functions are "filled in" automatically.

*By default, payments are rounded down to the nearest 0.01cr.*

+ 357
- 0
atm-core.lua View File

@@ -0,0 +1,357 @@
--
-- Minetest lurkcoin mod - ATMs
--
-- © 2019 by luk3yx
--

local open_atms = {}

local function e(text)
return minetest.formspec_escape(tostring(text))
end

local formspecs = {}

-- 0.4 compatibility
lurkcoin.formspec_prepend = ''
if minetest.get_modpath('default') and rawget(_G, 'default') and
default.gui_bg and default.gui_bg_img and default.gui_slots then
lurkcoin.formspec_prepend = default.gui_bg .. default.gui_bg_img ..
default.gui_slots
end

-- The formspec code is based on something random I did in 2017(?) for
-- lurkcoinV1, formspecs are weird and I somehow got it right™ then.
local function get_formspec(name, page, params)
-- The formspec template
local formspec = 'size[8,9;]' .. lurkcoin.formspec_prepend ..
'label[0.5,1.75;Your balance: ' ..
e(lurkcoin.bank.getbal(name)) .. 'cr.]' ..
'image_button[2,0.55;4,0.5;default_dirt.png^\\[colorize:#343434;y;' ..
'Welcome to a ' .. e(lurkcoin.server_name) .. ' ATM!]' ..
'label[0.5,2.25;Exchange rate: \xc2\xa41.00 is equal to ' ..
e(lurkcoin.exchange_rate) .. 'cr.]' ..
'image_button[1.75,1.05;4.5,0.5;default_dirt.png^\\[colorize:' ..
'#343434;y; Your account: ' .. e(name) .. ']' ..
'image[0.5,0.5;1,1;default_mese_crystal.png]' ..
'image[6.5,0.5;1,1;default_mese_crystal.png]'

-- Get the page formspec
local page = formspecs[page] or formspecs.main
if type(page) == 'string' then
formspec = formspec .. page
elseif type(page) == 'function' then
formspec = formspec .. page(name, params)
end

-- Return the generated formspec
return formspec
end

-- The formspecs (depending on installed mods)
local withdrawls = false

-- Payment screen
function formspecs.pay(name, fields)
fields = fields or {}
local formspec =
'field[0.8,3.5;7,1;user;User to pay;' .. e(fields.user or '') .. ']' ..
'field_close_on_enter[user;false]' ..
'field[0.8,4.8;7,1;server;Server the user is on;' ..
e(fields.server or lurkcoin.server_name) .. ']' ..
'field_close_on_enter[server;false]' ..
'field[0.8,6.15;7,1;amount;Amount to pay (in cr);' ..
e(fields.amount or '5.00') .. ']' ..
'field_close_on_enter[amount;false]'

if fields._err then
formspec = formspec ..
'label[0.5,7;' .. e(fields._err) .. ']' ..
'button[0.5,8;3.5,1;payuser;Cancel]' ..
'button[4,8;3.5,1;paysubmit;Try again]'
elseif fields.paysubmit then
formspec = formspec ..
'label[0.5,7;Please confirm the above values.'
if fields.server ~= lurkcoin.server_name then
local r = (tonumber(fields.amount) or 0) / lurkcoin.exchange_rate
r = tostring(math.floor(r * 100) / 100)
formspec = formspec ..
'\n' .. fields.amount .. 'cr is equal to \xc2\xa4' .. r .. '.'
end
formspec = formspec .. ']' ..
'button[0.5,8;3.5,1;payuser;Cancel]' ..
'button[4,8;3.5,1;payconfirm;Confirm and pay]'
else
formspec = formspec ..
'button[0.5,7;7,1;paysubmit;Pay the user!]'

if withdrawls then
formspec = formspec ..
'button[0.5,8;3.5,1;home;Go back]' ..
'button_exit[4,8;3.5,1;quit;Quit]'
else
formspec = formspec ..
'button_exit[0.5,8;7,1;quit;Quit]'
end
end
return formspec
end

-- The main formspec
if minetest.get_modpath('currency') then
formspecs.main =
'button[0.5,3.25;7,1;deposit;Deposit money]' ..
'field[0.8,4.85;6,1;withdraw;Withdraw money (in Mg);5.00]' ..
'field_close_on_enter[withdraw;false]' ..
'button[6.5,4.55;1,1;wd;Next >]' ..
'field[0.8,6.1;6,1;user;Pay someone;]' ..
'field_close_on_enter[user;false]' ..
'button[6.5,5.8;1,1;payuser;Next >]' ..
'button_exit[0.5,8;7,1;quit;Quit]'

-- Create a withdrawls list with available currency.
withdrawls = {
[0.05] = 'currency:minegeld_cent_5',
[0.10] = 'currency:minegeld_cent_10',
[0.25] = 'currency:minegeld_cent_25',
[1] = 'currency:minegeld',
[5] = 'currency:minegeld_5',
[10] = 'currency:minegeld_10',
[20] = 'currency:minegeld_20', -- The original mod now has 20Mg notes.
[50] = 'currency:minegeld_50',
[100] = 'currency:minegeld_100',
}

-- Remove non-registered notes
for id, note in pairs(withdrawls) do
if not minetest.registered_items[note] then
withdrawls[id] = nil
end
end
assert(#withdrawls > 0, 'The "currency" mod did not register any notes!')

-- Create a detached inventory for depositing
local inv = minetest.create_detached_inventory('lurkcoin:atm_deposit', {
on_put = function(inv, listname, index, stack, player)
local name = stack:get_name()
if name:sub(1, 17) == 'currency:minegeld' then
local m = name:sub(19)
if #m > 0 then
if m:sub(1, 5) == 'cent_' then
m = tonumber(m:sub(6))
if m then m = m / 100 end
else
m = tonumber(m)
end
else
m = 1
end

if type(m) == 'number' and m == m then
m = m * stack:get_count()
local pname = player:get_player_name()
if not lurkcoin.bank.add(pname, m, 'Deposit') then
player:get_inventory():add_item('main', stack)
end
core.log('action', 'Player ' .. pname .. ' deposts ' ..
stack:to_string() .. ' into an ATM.')
end
end
inv:set_list(listname, {})
lurkcoin.show_atm(player:get_player_name(), 'deposit')
end,

allow_put = function(inv, listname, index, stack, player)
local name = stack:get_name()
if name:sub(1, 17) == 'currency:minegeld' then
return stack:get_count()
else
return 0
end
end,

allow_take = function() return 0 end,
})
inv:set_size("lurkcoin", 1)

-- Depositing
formspecs.deposit =
'list[current_player;main;0,4.85;8,1;]' ..
'list[current_player;main;0,6.08;8,3;8]' ..
'list[detached:lurkcoin:atm_deposit;lurkcoin;3.5,3;1,1;]' ..
'listring[]' ..
'image_button[0,3;3.5,1;default_dirt.png^\\[colorize:#343434;' ..
'ignore;Deposit money here →]' ..
'button[5.25,3;2,1;home;Finish]'
else
-- When there is no physical currency, the only thing you can do is pay
-- others.
formspecs.main = formspecs.pay
end

-- "Transaction accepted" screen
function formspecs.success(name, text)
return 'image[2.1,3;4.5,4.5;lurkcoin_success.png]' ..
'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
'ignore;' .. e(text or 'Transaction sent!') .. ']' ..
'button[0.5,8;3.5,1;home;Go back]' ..
'button_exit[4,8;3.5,1;quit;Quit]'
end

-- "Transaction failed" screen
function formspecs.error(name, text)
return 'image[2.1,3;4.5,4.5;lurkcoin_error.png]' ..
'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
'ignore;' .. e(text or 'An error has occurred!') .. ']' ..
'button[0.5,8;3.5,1;home;Go back]' ..
'button_exit[4,8;3.5,1;quit;Quit]'
end

-- Processing screen
formspecs.processing =
'image[2.1,3;4.5,4.5;lurkcoin_processing.png]' ..
'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
'ignore;Processing your transaction...]' ..
'image_button[1,8;6,1;default_dirt.png^\\[colorize:#343434;' ..
'ignore;This should only take a few seconds.]'

-- A wrapper function
function lurkcoin.show_atm(name, page, params)
minetest.show_formspec(name, 'lurkcoin:atm', get_formspec(name,
page, params))
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == 'lurkcoin:atm' then
local name = player:get_player_name()

if withdrawls then
if fields.deposit then
return lurkcoin.show_atm(name, 'deposit')
elseif fields.wd then
local amount = tonumber(fields.withdraw)
if not amount or amount ~= amount or amount <= 0 then
return lurkcoin.show_atm(name, 'error',
'ERROR: Invalid number!')
end
local bal = lurkcoin.bank.getbal(name)
if amount > bal then
return lurkcoin.show_atm(name, 'error',
'ERROR: You cannot afford to do that!')
end

local note = false
for id, item in pairs(withdrawls) do
if (not note or id > note) and
math.floor(amount / id) * id == amount then
local def = minetest.registered_items[item]
if def and amount / id <= (def.stack_max or 99) then
note = id
end
end
end

if not note then
return lurkcoin.show_atm(name, 'error',
'ERROR: I cannot store that amount of\nmoney in a ' ..
'single stack of notes/coins!')
end

local stack = ItemStack({
name = withdrawls[note],
count = amount / note,
})
local inv = player:get_inventory()
if not inv:room_for_item('main', stack) then
return lurkcoin.show_atm(name, 'error',
'ERROR: You do not have enough inventory space!')
end

lurkcoin.bank.subtract(name, amount)
inv:add_item('main', stack)

return lurkcoin.show_atm(name, 'success')
end
end

if fields.payconfirm and open_atms[name] and open_atms[name].pay then
local data = open_atms[name]
for k, v in pairs(data) do
if type(v) == 'number' then fields[k] = tonumber(fields[k]) end
if k ~= 'pay' and fields[k] ~= v then
if k ~= 'amount' then
k = 'target ' .. k
end
fields._err = 'The ' .. k ..
' was modified before pressing confirm!'
lurkcoin.show_atm(name, 'pay', fields)
return
end
end
lurkcoin.show_atm(name, 'processing')
lurkcoin.pay(name, data.user, data.server, data.amount,
function(success, msg)
local page = success and 'success' or 'error'
return lurkcoin.show_atm(name, page, msg)
end
)
elseif fields.payuser or fields.paysubmit or fields.payconfirm then
if fields.paysubmit then
local amount = tonumber(fields.amount)
if not amount or amount ~= amount or amount <= 0 then
fields._err = 'Invalid number!'
lurkcoin.show_atm(name, 'pay', fields)
return
elseif lurkcoin.bank.getbal(name) - amount < 0 then
fields._err = 'You cannot afford to do that!'
lurkcoin.show_atm(name, 'pay', fields)
return
elseif fields.server then
fields.server = fields.server:gsub('^ *(.-) *$', '%1')
end

if fields.user then
fields.user = fields.user:gsub('^ *(.-) *$', '%1')
end

if not fields.server or fields.server == '' then
fields.server = lurkcoin.server_name
end

open_atms[name] = {
pay = true,
user = fields.user or '',
server = fields.server,
amount = amount,
}
if open_atms[name].user == '' or (
fields.server == lurkcoin.server_name and
not lurkcoin.bank.user_exists(open_atms[name].user)
) then
fields._err = 'That user does not exist!'
lurkcoin.show_atm(name, 'pay', fields)
return
end
end
fields._err = nil
lurkcoin.show_atm(name, 'pay', fields)
elseif fields.home then
lurkcoin.show_atm(name, 'main')
elseif fields.quit then
open_atms[name] = nil
end
end
end)

minetest.register_on_leaveplayer(function(player)
-- TODO: I forgot what one register_on_leaveplayer uses and am too lazy to
-- check.
local name
if type(player) == 'string' then
name = player
else
name = player:get_player_name()
end

open_atms[name] = nil
end)

+ 28
- 0
atm-nodes.lua View File

@@ -0,0 +1,28 @@
--
-- Minetest lurkcoin mod - ATM nodes
--
-- © 2019 by luk3yx
--

minetest.register_node('lurkcoin:atm', {
description = 'ATM',
groups = {cracky = 1},
tiles = {'lurkcoin_atm_side.png', 'lurkcoin_atm_side.png',
'lurkcoin_atm_side.png', 'lurkcoin_atm_side.png',
'lurkcoin_atm_side.png', 'lurkcoin_atm_side.png^lurkcoin_atm_top.png'},

paramtype2 = 'facedir',

on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string('infotext', 'ATM')
meta:set_string('version', tostring(lurkcoin.version))
if meta.mark_as_private then
meta:mark_as_private('version')
end
end,

on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
return lurkcoin.show_atm(clicker:get_player_name())
end,
})

+ 257
- 0
bank.lua View File

@@ -0,0 +1,257 @@
--
-- Minetest lurkcoin mod - Bank
--
-- © 2019 by luk3yx
--

-- Add a function to automatically change the bank
function lurkcoin.change_bank(bank)
-- Sanity checks
assert(type(bank) == 'table')
assert(bank.getbal and bank.setbal)

-- Set lurkcoin.bank to the new bank
lurkcoin.bank = bank
bank = nil

-- Get the current mod name
if not lurkcoin.bank.mod then
lurkcoin.bank.mod = minetest.get_current_modname() or '???'
end

-- Make sure "getbal" has a consistent return result and add user_exists if
-- it doesn't.
if type(lurkcoin.bank.getbal('\xc2\xa4 Fake user')) ~= 'number' then
local getbal = lurkcoin.bank.getbal
if not lurkcoin.bank.user_exists then
function lurkcoin.bank.user_exists(name)
return getbal(name) and true or false
end
end

function lurkcoin.bank.getbal(name)
return getbal(name) or 0
end
end

assert(lurkcoin.bank.user_exists)

-- Make sure "setbal" has a consistent return value.
do
local setbal = lurkcoin.bank.setbal
function lurkcoin.bank.setbal(name, amount, reason)
if type(amount) ~= 'number' or amount ~= amount then
return false
end

local success = setbal(name, amount, reason)
if success == nil then
return true
end
return success
end
end

-- Make sure "changebal" exists
if not lurkcoin.bank.changebal then
if lurkcoin.bank.add and lurkcoin.bank.subtract then
function lurkcoin.bank.changebal(name, amount, reason)
if amount == 0 then
return true
elseif type(amount) ~= 'number' or amount ~= amount then
return false
end

if amount > 0 then
return lurkcoin.bank.add(name, amount, reason)
else
return lurkcoin.bank.subtract(name, amount, reason)
end
end
else
function lurkcoin.bank.changebal(name, amount, reason)
if amount == 0 then
return true
elseif type(amount) ~= 'number' or amount ~= amount then
print('Not a number')
return false
end

local resulting_bal = lurkcoin.bank.getbal(name) + amount
if resulting_bal < 0 then
print('Cannot afford to do that')
return false
end

return lurkcoin.bank.setbal(name, resulting_bal, reason)
end
end
end

-- Make sure "add" exists
if not lurkcoin.bank.add then
function lurkcoin.bank.add(name, amount, reason)
if amount < 0 then
return false
end

return lurkcoin.bank.changebal(name, amount, reason)
end
end

-- Make sure "subtract" exists
if not lurkcoin.bank.subtract then
function lurkcoin.bank.subtract(name, amount, reason)
if amount < 0 then
return false
end

return lurkcoin.bank.changebal(name, 0 - amount, reason)
end
end

-- Make sure "pay" exists, and if so, make sure it has consistent return
-- values.
if lurkcoin.bank.pay then
local pay = lurkcoin.bank.pay
function lurkcoin.bank.pay(from, to, amount)
if type(amount) ~= 'number' or amount ~= amount then
return false, 'Invalid number!'
end

local success, msg = pay(from, to, amount)
if success or success == nil then
return true, msg or 'Transaction sent!'
else
return false, msg or 'Error processing transaction!'
end
end
else
function lurkcoin.bank.pay(from, to, amount)
if type(amount) == 'number' then
amount = math.floor(amount * 100) / 100
end

if not lurkcoin.bank.user_exists(from) or not
lurkcoin.bank.user_exists(to) then
return false, 'The specified user does not exist!'
elseif type(amount) ~= 'number' or amount ~= amount or
amount <= 0 then
return false, 'Invalid number!'
elseif lurkcoin.bank.getbal(from) - amount < 0 then
return false, 'You cannot afford to do that!'
end

local success = false
if lurkcoin.bank.subtract(from, amount, 'Transaction to ' ..
to) then
success = lurkcoin.bank.add(to, amount, 'Transaction from ' ..
from)

-- Revert failed transactions
if not success and success ~= nil then
lurkcoin.bank.add(from, amount,
'Reverting failed transaction.')
end
end

if success or success == nil then
minetest.log('action', '[lurkcoin] User ' .. from .. ' paid ' ..
to .. ' ' .. tostring(amount) .. 'cr.')
return true, 'Transaction sent!'
end
return false, 'Error processing transaction!'
end
end
end

-- Built-in mod integrations
-- These should be in alphabetical order (except ones that use the "money"
-- global).

if minetest.get_modpath('atm') and rawget(_G, 'atm') and atm.balance then
-- ATM mod from https://git.gpcf.eu/?p=atm.git;a=tree
-- Add (really) basic wrapper functions that should be replaced in
-- change_bank().
lurkcoin.change_bank({
mod = 'atm',
getbal = function(name)
return atm.balance[name]
end,
setbal = function(name, bal)
atm.balance[name] = bal
end
})
elseif minetest.get_modpath('bank_accounts') and rawget(_G, 'accounts') and
accounts.balance and accounts.pin and accounts.credit then
-- https://github.com/Tmanyo/bank_accounts

lurkcoin.change_bank({
mod = 'bank_accounts',
getbal = function(name)
return accounts.balance[name]
end,
setbal = function(name, bal)
accounts.balance[name] = bal
end
})
elseif minetest.get_modpath('economy') and rawget(_G, 'economy') and
economy.balance and economy.accountlog then
-- https://github.com/orwell96/economy
-- economy.moneyof() automatically adds non-existent entries, therefore it
-- is not used by default.

lurkcoin.change_bank({
mod = 'economy',
getbal = function(name)
return economy.balance[name]
end,
setbal = function(name, amount, reason)
local difference = amount - economy.balance[name]
economy.balance[name] = amount
local symbol
if difference >= 0 then
symbol = '+'
else
symbol = '-'
difference = 0 - difference
end
if not economy.accountlog[name] then
economy.accountlog[name] = {}
end
if not reason then
reason = 'Transaction on ' .. os.date('%Y-%m-%d %H:%M:%S')
end
table.insert(economy.accountlog[name], 1, {
action = reason,
amount = symbol .. ' ' .. tostring(difference)
})
end,
})
elseif rawget(_G, 'money') then
-- Mods that (incorrectly) use the "money" global variable.

if minetest.get_modpath('economy') and money.get_money then
lurkcoin.change_bank({
mod = 'economy',
user_exists = money.exist,
getbal = money.get_money,
setbal = money.set_money,
})
elseif minetest.get_modpath('money2') and money.get then
-- money.add and money.dec have a different return value system.
lurkcoin.change_bank({
mod = 'money2',
user_exists = money.has_credit,
getbal = money.get,
setbal = function(...)
money.set(...)
return true
end,
pay = function(from, to, amount)
local err = money.transfer(from, to, amount)
return not err, err
end
})
end
end

+ 173
- 0
core.lua View File

@@ -0,0 +1,173 @@
--
-- Minetest lurkcoin mod - Core
--
-- © 2019 by luk3yx
--

lurkcoin.version = 0
lurkcoin.timeout = 10
lurkcoin.exchange_rate = 1

-- Do not allow other mods to modify this, or they may be able to bypass mod
-- security restrictions!
local baseurl = 'https://nz.xeroxirc.net:7000/v2'

-- Get the username and API token
lurkcoin.server_name = minetest.settings:get('lurkcoin.username')
local token = minetest.settings:get('lurkcoin.token')

-- Make sure the HTTP API exists
local http = ...
if not http then
minetest.log('warning', 'lurkcoin is not allowed to use the HTTP API! ' ..
'Please add lurkcoin to secure.http_mods in minetest.conf.')
end

-- Download functions
lurkcoin.user_agent = 'Minetest ' .. minetest.get_version().string ..
' (with lurkcoin mod v' .. tostring(lurkcoin.version) .. ')'
local function get(url, data, callback)
if not data then
data = {}
elseif not http then
return callback({
completed = true,
succeeded = false,
timeout = true,
code = 500,
data = 'ERROR: The lurkcoin mod is not in secure.http_mods!'
})
elseif not data.name or not data.token then
return callback({
completed = true,
succeeded = false,
timeout = true,
code = 401,
data = 'ERROR: The lurkcoin mod does not have (correct) ' ..
'account credentials!'
})
end

data.name = lurkcoin.server_name
data.token = token

-- Minetest eats any non-200 code.
data.force_200 = '200'

http.fetch({
url = baseurl .. '/' .. url,
timeout = lurkcoin.timeout,
post_data = data,
user_agent = lurkcoin.user_agent,
}, function(res)
if res.timeout then
minetest.log('warning', '[lurkcoin] Could not connect to lurkcoin!')
res.code = 500
res.data = 'ERROR: Could not connect to lurkcoin!'
elseif res.code == 401 or res.code == 418 then
minetest.log('warning', '[lurkcoin] Invalid username or API token!')
lurkcoin.server_name, token = '', nil
elseif res.code == 200 and res.data:sub(1, 7) == 'ERROR: ' then
res.code = 501
end

local success, msg = pcall(callback, res)
if not success then
minetest.log('error', '[lurkcoin] Error: ' .. msg)
end
end)
end

-- Process incoming transactions.
local handled_transactions = {}

local function _sync(res)
if res.code == 200 then
local data = minetest.parse_json(res.data)

-- Update the exchange rate
assert(type(data.exchange_rate) == 'number')
assert(data.exchange_rate == data.exchange_rate)
lurkcoin.exchange_rate = data.exchange_rate

-- Process any unprocessed transactions
for _, t in ipairs(data.transactions) do
assert(type(t[3]) == 'number')
local id = minetest.serialize(t)
if not handled_transactions[id] then
handled_transactions[id] = true
core.log('action', '[lurkcoin] ' .. t[4])
lurkcoin.bank.add(t[2], t[3])
end
end

-- Tell lurkcoin to remove processed transactions
if #data.transactions > 0 then
get('remove_transactions', {count = tostring(#data.transactions)},
function(res)
if res.code == 200 then
for _, t in ipairs(data.transactions) do
handled_transactions[minetest.serialize(t)] = nil
end
end
end)
end
end
end

-- Periodically sync with lurkcoin.
local function sync()
get('get_transactions', {as_object = 'true'}, _sync)
minetest.after(300, sync)
end

-- Start syncing once the game is loaded.
minetest.after(0, sync)

-- Pay a user (cross-server)
function lurkcoin.pay(from, to, server, amount, callback)
assert(type(amount) == 'number' and callback)

-- Run lurkcoin.bank.pay() if this is not a cross-server transaction.
if not server or server == '' or server:lower() ==
lurkcoin.server_name:lower() then
return callback(lurkcoin.bank.pay(from, to, amount))
end

-- Sanity checks
amount = math.floor(amount * 100) / 100
if not lurkcoin.bank.user_exists(from) then
return callback(false, 'ERROR: The "from" user does not exist!')
elseif amount ~= amount or amount <= 0 then
return callback(false, 'ERROR: Invalid number!')
elseif lurkcoin.bank.getbal(from) - amount < 0 then
return callback(false, 'ERROR: You cannot afford to do that!')
end

if not lurkcoin.bank.subtract(from, amount, 'Transaction to "' .. to ..
'" on server "' .. server .. '".') then
return callback(false, 'ERROR: Transaction failed!')
end

-- Send the request
return get('pay', {
target = to,
server = server,
amount = tostring(amount / lurkcoin.exchange_rate)
}, function(res)
if res.code ~= 200 then
lurkcoin.bank.add(from, amount, 'Reverting failed transaction.')
if res.data == 'ERROR: You cannot afford to do that!' then
res.data = 'ERROR: This server cannot afford to do that!'
end
else
minetest.log('action', '[lurkcoin] User ' .. from .. ' paid ' ..
to .. ' (on server ' .. server .. ') ' .. tostring(amount) .. 'cr.')
end

return callback(res.code == 200, res.data or 'ERROR: Unknown error!')
end)
end

-- Nuke lurkcoin.modpath
lurkcoin.modpath = nil

+ 7
- 0
depends.txt View File

@@ -0,0 +1,7 @@
atm?
bank_accounts?
currency?
default?
economy?
money?
money2?

+ 31
- 0
init.lua View File

@@ -0,0 +1,31 @@
--
-- Minetest lurkcoin mod
--
-- © 2019 by luk3yx
--

lurkcoin = {}

local modpath = minetest.get_modpath('lurkcoin')

-- Load bank.lua
dofile(modpath .. '/bank.lua')

-- Load the minibank submod thing if required
if not lurkcoin.bank then
dofile(modpath .. '/minibank.lua')
end

-- Request the HTTP(S) API and load core.lua.
local http = minetest.request_http_api()
assert(loadfile(modpath .. '/core.lua'))(http)

-- Load the ATM core code
dofile(modpath .. '/atm-core.lua')

-- Load the ATM blocks
dofile(modpath .. '/atm-nodes.lua')

-- Display loaded message
minetest.log('action', '[lurkcoin] Loaded on server "' ..
lurkcoin.server_name .. '".')

+ 33
- 0
minibank.lua View File

@@ -0,0 +1,33 @@
--
-- Minetest minibank (sub)mod - A very simple economy mod alternative
--
-- © 2019 by luk3yx
--

local storage = minetest.get_mod_storage()

-- Use mod storage for getbal() and setbal().
-- Store as strings because of weird bugs when using floats.
local function getbal(name)
return tonumber(storage:get_string('minibank-' .. name))
end

local function setbal(name, balance)
storage:set_string('minibank-' .. name, tostring(balance))
end

-- Create an empty account for new players
minetest.register_on_joinplayer(function(player)
if lurkcoin.bank.mod ~= 'lurkcoin' then return end

local name = player:get_player_name()
if not lurkcoin.bank.user_exists(name) then
setbal(name, 0)
end
end)

-- Let bank.lua add everything else.
lurkcoin.change_bank({
getbal = getbal,
setbal = setbal
})

+ 2
- 0
mod.conf View File

@@ -0,0 +1,2 @@
name = lurkcoin
depends = atm?,bank_accounts?,currency?,default?,economy?,money?,money2?

+ 2
- 0
settingtypes.txt View File

@@ -0,0 +1,2 @@
lurkcoin.username (lurkcoin username) string
lurkcoin.token (lurkcoin API token) string

BIN
textures/lurkcoin_atm_side.png View File

Before After
Width: 16  |  Height: 16  |  Size: 660B

BIN
textures/lurkcoin_atm_top.png View File

Before After
Width: 64  |  Height: 64  |  Size: 817B

BIN
textures/lurkcoin_error.png View File

Before After
Width: 32  |  Height: 32  |  Size: 694B

BIN
textures/lurkcoin_processing.png View File

Before After
Width: 32  |  Height: 32  |  Size: 274B

BIN
textures/lurkcoin_success.png View File

Before After
Width: 32  |  Height: 32  |  Size: 781B

Loading…
Cancel
Save