Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Turtle Diamond ATM Script (diamond_turtle_atm.lua)
- -- Secure Transaction Logic: Acquire -> Verify -> Server -> Commit/Revert
- local modem = peripheral.find("modem")
- if not modem then error("No modem found") end
- modem.open(1) -- Open channel 1 for communication with server
- modem.open(4) -- Open channel 4 for communication with Diamond ATM terminal
- local function debug(message)
- print(os.date("%H:%M:%S") .. ": " .. message)
- end
- local function sendToServer(message)
- modem.transmit(1, 1, message)
- local timer = os.startTimer(5)
- while true do
- local event, id, channel, replyChannel, response = os.pullEvent()
- if event == "modem_message" and channel == 1 then
- return response
- elseif event == "timer" and id == timer then
- return nil
- end
- end
- end
- local function sendToATM(message)
- modem.transmit(4, 4, message)
- end
- local function findBlock(blockType)
- for i = 1, 4 do
- local success, data = turtle.inspect()
- if success and data.name:find(blockType) then
- return true
- end
- turtle.turnRight()
- end
- return false
- end
- local function isDiamond(item)
- return item and item.name == "minecraft:diamond"
- end
- -- Helper to dump specific item type to front
- local function dumpItems(itemCheckFn)
- for i = 1, 16 do
- local item = turtle.getItemDetail(i)
- if item and itemCheckFn(item) then
- turtle.select(i)
- turtle.drop()
- end
- end
- turtle.select(1)
- end
- -- Securely acquire X diamonds from front inventory (Barrel or Chest)
- -- Keeps diamonds in turtle inventory. Returns success, message.
- -- If targetAmount is "max", grabs everything valid.
- local function acquireDiamonds(targetAmount)
- local acquired = 0
- local isMax = (targetAmount == "max")
- while true do
- local needed = 64
- if not isMax then
- needed = targetAmount - acquired
- if needed > 64 then needed = 64 end
- if needed <= 0 then break end
- end
- -- Try to suck needed amount
- if turtle.suck(needed) then
- -- Verify what we just sucked
- local foundInvalid = false
- for i = 1, 16 do
- local item = turtle.getItemDetail(i)
- if item and not isDiamond(item) then
- -- Junk item, spit it back
- turtle.select(i)
- turtle.drop()
- turtle.select(1)
- foundInvalid = true
- end
- end
- if foundInvalid and not isMax then
- -- If we found invalid items during a specific request, we might want to fail or just retry?
- -- simple retry logic: just continue loop, we counted valid ones below
- end
- -- Recount total diamonds in inventory to update progress
- local currentTotal = 0
- for i = 1, 16 do
- local item = turtle.getItemDetail(i)
- if item and isDiamond(item) then
- currentTotal = currentTotal + item.count
- end
- end
- -- If we haven't increased, break (empty source)
- if currentTotal <= acquired then
- break
- end
- acquired = currentTotal
- else
- break -- Sucking failed (empty)
- end
- end
- if isMax then
- if acquired > 0 then
- return true, acquired
- else
- return false, "No Diamonds Found"
- end
- else
- if acquired >= targetAmount then
- return true, acquired
- else
- return false, "Insufficient Diamonds: Found " .. acquired .. " of " .. targetAmount
- end
- end
- end
- -- Dumps ALL diamonds in inventory to front
- local function commitDiamonds()
- dumpItems(isDiamond)
- end
- local function handleDeposit(studentId, password, amount)
- local isMax = (amount == "max")
- local targetDiamonds = 0
- if not isMax then
- targetDiamonds = amount -- 1 Eddie = 1 Diamond
- end
- if isMax then
- debug("Deposit: MAX Eddies")
- else
- debug("Deposit: " .. amount .. " Eddies (" .. targetDiamonds .. " Diamonds)")
- end
- if not findBlock("barrel") then
- sendToATM({action="complete", success=false, error="Barrel not found"})
- return
- end
- -- 1. Acquire from Barrel
- local success, result = acquireDiamonds(isMax and "max" or targetDiamonds)
- if not success then
- sendToATM({action="complete", success=false, error=result or "Failed to collect diamonds"})
- -- Refund what we grabbed back to Barrel
- commitDiamonds()
- return
- end
- local totalDiamonds = result
- -- 1 Diamond = 1 Eddie, so no remainder calculation needed usually.
- local eddies = totalDiamonds
- if eddies <= 0 then
- sendToATM({action="complete", success=false, error="No diamonds found"})
- commitDiamonds()
- return
- end
- -- 2. Server Update
- local response = sendToServer({type="update_balance", studentId=studentId, password=password, action="deposit", amount=eddies})
- if response and response.type == "balance_update_response" and response.success then
- -- 3. Success: Move to Chest
- if findBlock("chest") then
- commitDiamonds() -- Dump to chest
- sendToATM({action="complete", success=true, newBalance=response.result, amountDeposited=eddies})
- else
- -- Critical Error: Chest missing after transaction!
- debug("CRITICAL: Chest missing during deposit commit!")
- -- Attempt to return to barrel to be safe
- if findBlock("barrel") then
- commitDiamonds()
- sendToATM({action="complete", success=true, newBalance=response.result, warning="System Error: Chest full/missing. Diamonds returned to barrel but balance updated. Contact Admin.", amountDeposited=eddies})
- else
- sendToATM({action="complete", success=true, newBalance=response.result, warning="System Error: Diamonds stuck in Turtle.", amountDeposited=eddies})
- end
- end
- else
- -- 4. Fail: Refund to Barrel
- debug("Server deposit failed: " .. (response and response.message or "Timeout"))
- if findBlock("barrel") then
- commitDiamonds() -- Dump back to barrel
- end
- sendToATM({action="complete", success=false, error="Server rejected deposit"})
- end
- end
- local function handleWithdraw(studentId, password, amount)
- local diamondAmount = amount
- debug("Withdraw: " .. amount .. " Eddies (" .. diamondAmount .. " Diamonds)")
- -- 0. Check Balance First (Quick fail)
- local balanceResponse = sendToServer({type="get_balance", studentId=studentId, password=password})
- if not (balanceResponse and balanceResponse.type == "balance_response") then
- sendToATM({action="complete", success=false, error="Failed to retrieve balance"})
- return
- end
- if tonumber(balanceResponse.balance) < amount then
- sendToATM({action="complete", success=false, error="Insufficient balance"})
- return
- end
- if not findBlock("chest") then
- sendToATM({action="complete", success=false, error="Chest not found"})
- return
- end
- -- 1. Acquire from Chest
- local success, msg = acquireDiamonds(diamondAmount)
- if not success then
- sendToATM({action="complete", success=false, error=msg or "ATM empty/low funds"})
- -- Revert to Chest
- commitDiamonds()
- return
- end
- -- 2. Server Update
- local response = sendToServer({type="update_balance", studentId=studentId, password=password, action="withdraw", amount=amount})
- if response and response.type == "balance_update_response" and response.success then
- -- 3. Success: Move to Barrel
- if findBlock("barrel") then
- commitDiamonds() -- Dump to barrel
- sendToATM({action="complete", success=true, newBalance=response.result})
- else
- debug("CRITICAL: Barrel missing during withdraw commit!")
- -- Refund to Chest? But user already paid on server.
- -- This is a 'money lost' scenario for user if we refund.
- -- Better to keep trying or dump?
- -- In production, we'd log this. Here, we return to Chest to save the diamonds and error out.
- -- User loses balance but ATM keeps diamonds. Admin intervention needed.
- commitDiamonds()
- sendToATM({action="complete", success=false, error="Hardware error: Barrel missing. Diamonds retained in ATM. Contact Admin."})
- end
- else
- -- 4. Fail: Revert to Chest
- commitDiamonds()
- sendToATM({action="complete", success=false, error="Server rejected withdraw"})
- end
- end
- while true do
- local event, side, channel, replyChannel, message = os.pullEvent("modem_message")
- if channel == 4 then
- if message.action == "deposit" then
- handleDeposit(message.studentId, message.password, message.amount)
- elseif message.action == "withdraw" then
- handleWithdraw(message.studentId, message.password, message.amount)
- end
- end
- end
Add Comment
Please, Sign In to add comment