-- functions.lua (Corrected for Deadlock issue during interest calculation) local world_path = minetest.get_worldpath() local file_path = world_path .. "/accounts" function normalize_and_tonumber(str) if not str then return nil end str = str:gsub(",", ".") return tonumber(str) end local function read_accounts_file() local f = io.open(file_path, "r") if not f then return { balance = {}, pin = {}, credit = {}, history = {} } end local data = f:read("*a") f:close() if data == "" or data == nil then return { balance = {}, pin = {}, credit = {}, history = {} } end return minetest.deserialize(data) or { balance = {}, pin = {}, credit = {}, history = {} } end -- CORRECTED: The save function now accepts an optional parameter to bypass the lock. local function save_accounts_file(data, bypass_lock) -- If the interest script is running AND the lock bypass is NOT active, block the save. if not bypass_lock and bank_accounts and bank_accounts.is_calculating_interest then minetest.log("warning", "[bank_accounts] Save operation blocked: Daily interest calculation is in progress.") return false -- Signal failure end local f, err = io.open(file_path, "w") if not f then minetest.log("error", "[bank_accounts] Could not open accounts file for writing: " .. tostring(err)) return false end f:write(minetest.serialize(data)) f:close() return true end local function log_transaction(data, player_name, account_type, amount, new_total, trans_type, purpose, other_party) if not data.history then data.history = {} end if not data.history[player_name] then data.history[player_name] = {} end local transaction = { timestamp = os.time(), type = trans_type or "unknown", account = account_type, amount = amount, new_total = new_total, purpose = purpose or "", other = other_party or "" } table.insert(data.history[player_name], 1, transaction) while #data.history[player_name] > 200 do table.remove(data.history[player_name]) end end -- On first run, bypass lock to create file safely. do local f = io.open(file_path, "r") if not f then save_accounts_file({ balance = {}, pin = {}, credit = {}, history = {} }, true) else f:close() end end --- API --- function bank_accounts.get_account_data(player_name) if not player_name then return read_accounts_file() end local data = read_accounts_file() if not data.history then data.history = {} end return { balance = data.balance[player_name] or 0, pin = data.pin[player_name] or "0000", credit = data.credit[player_name] or 0, history = data.history[player_name] or {}, } end function bank_accounts.get_all_data() return read_accounts_file() end -- CORRECTED: save_all now calls the internal save function with the bypass parameter. function bank_accounts.save_all(data) return save_accounts_file(data, true) end function bank_accounts.get_balance(player_name) local data = read_accounts_file(); return data.balance[player_name] or 0 end function bank_accounts.get_credit(player_name) local data = read_accounts_file(); return data.credit[player_name] or 0 end function bank_accounts.get_pin(player_name) local data = read_accounts_file(); return data.pin[player_name] or "0000" end function bank_accounts.add_balance(player_name, amount, trans_type, purpose, other_party) local data = read_accounts_file() local current_balance = data.balance[player_name] or 0 local new_balance = current_balance + tonumber(amount) data.balance[player_name] = new_balance log_transaction(data, player_name, "balance", amount, new_balance, trans_type, purpose, other_party) return save_accounts_file(data) end function bank_accounts.set_balance(player_name, amount, trans_type, purpose, other_party) local data = read_accounts_file() local current_balance = data.balance[player_name] or 0 local new_balance = tonumber(amount) local diff = new_balance - current_balance data.balance[player_name] = new_balance log_transaction(data, player_name, "balance", diff, new_balance, trans_type or "admin_set", purpose, other_party) return save_accounts_file(data) end function bank_accounts.add_credit(player_name, amount, trans_type, purpose, other_party) local data = read_accounts_file() local current_credit = data.credit[player_name] or 0 local new_credit = current_credit + tonumber(amount) data.credit[player_name] = new_credit log_transaction(data, player_name, "credit", amount, new_credit, trans_type, purpose, other_party) return save_accounts_file(data) end function bank_accounts.set_credit(player_name, amount, trans_type, purpose, other_party) local data = read_accounts_file() local current_credit = data.credit[player_name] or 0 local new_credit = tonumber(amount) local diff = new_credit - current_credit data.credit[player_name] = new_credit log_transaction(data, player_name, "credit", diff, new_credit, trans_type or "admin_set_credit", purpose, other_party) return save_accounts_file(data) end function bank_accounts.set_pin(player_name, pin) local data = read_accounts_file() data.pin[player_name] = tostring(pin) return save_accounts_file(data) end function bank_accounts.player_has_account(player_name) local data = read_accounts_file() return data.balance[player_name] ~= nil end function bank_accounts.create_account(player_name) if bank_accounts.player_has_account(player_name) then return false end local data = read_accounts_file() data.balance[player_name] = 0 data.pin[player_name] = "0000" data.credit[player_name] = 0 if not data.history then data.history = {} end data.history[player_name] = {} log_transaction(data, player_name, "system", 0, 0, "Account Created", "Initial account setup") return save_accounts_file(data) end