Files
project-afterlife/servers/minecraft/lua-scripts/mainframe_startup.lua
consultoria-as 7dc1d2e0e5
Some checks failed
Deploy / deploy (push) Has been cancelled
docs: add AfterCoin documentation and MetaMask guides
Comprehensive docs covering architecture, all components, Docker
services, environment variables, MetaMask connection (desktop + mobile),
administration commands, and troubleshooting.

Also adds Lua scripts to repo for version control, including the
periodic chain sync loop (every 30s) in the mainframe.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:48:24 +00:00

165 lines
4.7 KiB
Lua

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)