rednet.open("left") local databasePath = "players" local database -- AfterCoin Bridge config local BRIDGE_URL = "http://afc-bridge:3001" local BRIDGE_SECRET = "afterlife_bridge_dev_2024" local SYNC_INTERVAL = 30 -- seconds between chain sync polls -- HTTP helper: POST to bridge API local function bridgePost(endpoint, body) local url = BRIDGE_URL .. endpoint local jsonBody = textutils.serialiseJSON(body) local ok, result = pcall(function() local response, failReason = http.post(url, jsonBody, { ["Content-Type"] = "application/json", ["x-bridge-secret"] = BRIDGE_SECRET }) if not response then print("[Bridge] POST failed: " .. tostring(failReason)) return nil end local data = textutils.unserialiseJSON(response.readAll()) response.close() return data end) if not ok then print("[Bridge] POST error: " .. tostring(result)) return nil end return result end -- HTTP helper: GET from bridge API local function bridgeGet(endpoint) local url = BRIDGE_URL .. endpoint local ok, result = pcall(function() local response, failReason = http.get(url) if not response then print("[Bridge] GET failed: " .. tostring(failReason)) return nil end local data = textutils.unserialiseJSON(response.readAll()) response.close() return data end) if not ok then print("[Bridge] GET error: " .. tostring(result)) return nil end return result end -- Sync on-chain balance for a player (returns on-chain balance or nil) local function syncFromChain(diskId) local resp = bridgeGet("/api/balance/" .. tostring(diskId)) if resp and resp.success then return resp.balance end return nil end -- Save database to disk local function saveDatabase() local file = fs.open(databasePath, "w") file.write(textutils.serialise(database)) file.close() end if not fs.exists(databasePath) then database = {} local file, err = fs.open(databasePath, "w") if not file then print("ERROR creating db: "..tostring(err)) print("Trying alternate path...") databasePath = "/players.txt" file, err = fs.open(databasePath, "w") if not file then print("FATAL: "..tostring(err)) return end end file.write("{}") file.close() else local file = fs.open(databasePath, "r") database = textutils.unserialise(file.readAll()) file.close() end print("Database loaded.") print("AfterCoin bridge: " .. BRIDGE_URL) -- Rednet message handler local function messageLoop() while true do local id, data = rednet.receive("otto") print(textutils.serialise(data)) if data.type == "getPlayerBalance" then print("Fetching balance for ", data.player) local chainBalance = syncFromChain(data.player) if chainBalance and database[data.player] then database[data.player].balance = chainBalance end rednet.send(id, database[data.player], "otto") elseif data.type == "setPlayerBalance" then print("Setting balance for ", data.player, " to ", data.balance) local oldBalance = database[data.player].balance local diff = data.balance - oldBalance database[data.player].balance = data.balance saveDatabase() if diff > 0 then print("[Bridge] Minting " .. diff .. " AFC") bridgePost("/api/deposit", {diskId=tostring(data.player), amount=diff}) elseif diff < 0 then print("[Bridge] Burning " .. math.abs(diff) .. " AFC") bridgePost("/api/withdraw", {diskId=tostring(data.player), amount=math.abs(diff)}) end rednet.send(id, nil, "otto") elseif data.type == "addPlayer" then print("Adding player: #"..data.player, data.name) database[data.player] = { name=data.name, balance=0 } saveDatabase() print("[Bridge] Registering wallet for " .. data.name) bridgePost("/api/register", {diskId=tostring(data.player), name=data.name}) rednet.send(id, nil, "otto") elseif data.type == "getLeaderboard" then print("Sending leaderboard") local leaderboard = {} for pid, pdata in pairs(database) do table.insert(leaderboard, {name=pdata.name, balance=pdata.balance}) end table.sort(leaderboard, function(a, b) return a.balance > b.balance end) rednet.send(id, leaderboard, "otto") end end end -- Periodic chain sync loop local function syncLoop() while true do os.sleep(SYNC_INTERVAL) local changed = false for pid, pdata in pairs(database) do local chainBalance = syncFromChain(pid) if chainBalance and chainBalance ~= pdata.balance then print("[Sync] " .. pdata.name .. ": " .. pdata.balance .. " -> " .. chainBalance .. " AFC") database[pid].balance = chainBalance changed = true end end if changed then saveDatabase() print("[Sync] Database updated from chain.") end end end -- Run both loops in parallel print("Starting message handler + chain sync (every " .. SYNC_INTERVAL .. "s)...") parallel.waitForAll(messageLoop, syncLoop)