Skip to content

Commit 9a9280f

Browse files
committed
refactor(api): use helper function for standard error responses
1 parent a5a5d8e commit 9a9280f

File tree

1 file changed

+32
-26
lines changed

1 file changed

+32
-26
lines changed

src/lua/api.lua

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
local socket = require("socket")
22
local json = require("json")
33

4+
-- Constants
5+
local UDP_BUFFER_SIZE = 65536
6+
local SOCKET_TIMEOUT = 0
7+
local EVENT_QUEUE_THRESHOLD = 3
8+
49
API = {}
510
API.socket = nil
611
API.functions = {}
@@ -16,7 +21,7 @@ function API.update(_)
1621
-- Create socket if it doesn't exist
1722
if not API.socket then
1823
API.socket = socket.udp()
19-
API.socket:settimeout(0)
24+
API.socket:settimeout(SOCKET_TIMEOUT)
2025
local port = BALATRO_BOT_CONFIG.port
2126
API.socket:setsockname("127.0.0.1", tonumber(port))
2227
sendDebugMessage("UDP socket created on port " .. port, "BALATROBOT")
@@ -31,33 +36,28 @@ function API.update(_)
3136
end
3237

3338
-- Parse received data and run the appropriate function
34-
local raw_data, client_ip, client_port = API.socket:receivefrom(65536)
39+
local raw_data, client_ip, client_port = API.socket:receivefrom(UDP_BUFFER_SIZE)
3540
if raw_data and client_ip and client_port then
3641
-- Store the last client connection
3742
API.last_client_ip = client_ip
3843
API.last_client_port = client_port
3944

4045
local ok, data = pcall(json.decode, raw_data)
4146
if not ok then
42-
sendErrorMessage("Invalid JSON", "BALATROBOT")
43-
API.send_response({ error = "Invalid JSON" })
47+
API.send_error_response("Invalid JSON")
4448
return
4549
end
4650
if data.name == nil then
47-
sendErrorMessage("Message must contain a name", "BALATROBOT")
48-
API.send_response({ error = "Message must contain a name" })
51+
API.send_error_response("Message must contain a name")
4952
elseif data.arguments == nil then
50-
sendErrorMessage("Message must contain arguments", "BALATROBOT")
51-
API.send_response({ error = "Message must contain arguments" })
53+
API.send_error_response("Message must contain arguments")
5254
else
5355
local func = API.functions[data.name]
5456
local args = data.arguments
5557
if func == nil then
56-
sendErrorMessage("Unknown function name: " .. data.name, "BALATROBOT")
57-
API.send_response({ error = "Unknown function name: " .. data.name })
58+
API.send_error_response("Unknown function name", { function_name = data.name })
5859
elseif type(args) ~= "table" then
59-
sendErrorMessage("Arguments must be a table", "BALATROBOT")
60-
API.send_response({ error = "Arguments must be a table: " .. type(args) })
60+
API.send_error_response("Arguments must be a table", { received_type = type(args) })
6161
else
6262
sendDebugMessage(data.name .. "(" .. json.encode(args) .. ")", "BALATROBOT")
6363
func(args)
@@ -74,6 +74,15 @@ function API.send_response(response)
7474
end
7575
end
7676

77+
function API.send_error_response(message, context)
78+
sendErrorMessage(message, "BALATROBOT")
79+
local response = { error = message, state = G.STATE }
80+
if context then
81+
response.context = context
82+
end
83+
API.send_response(response)
84+
end
85+
7786
function API.init()
7887
-- Hook into the game's update loop
7988
local original_update = love.update
@@ -140,8 +149,7 @@ API.functions["start_run"] = function(args)
140149
end
141150
end
142151
if not deck_found then
143-
sendErrorMessage("Invalid deck arg for start_run: " .. tostring(args.deck), "BALATROBOT")
144-
API.send_response({ error = "Invalid deck arg for start_run: " .. tostring(args.deck) })
152+
API.send_error_response("Invalid deck arg for start_run", { deck = args.deck })
145153
return
146154
end
147155

@@ -206,16 +214,17 @@ API.functions["skip_or_select_blind"] = function(args)
206214
end,
207215
}
208216
else
209-
sendErrorMessage("Invalid action arg for skip_or_select_blind: " .. args.action, "BALATROBOT")
210-
API.send_response({ error = "Invalid action arg for skip_or_select_blind: " .. args.action })
217+
API.send_error_response("Invalid action arg for skip_or_select_blind", { action = args.action })
211218
return
212219
end
213220
end
214221

215222
API.functions["play_hand_or_discard"] = function(args)
216223
if args.action == "discard" and G.GAME.current_round.discards_left == 0 then
217-
sendErrorMessage("No discards left to perform discard", "BALATROBOT")
218-
API.send_response({ error = "No discards left to perform discard", state = G.STATE })
224+
API.send_error_response(
225+
"No discards left to perform discard",
226+
{ discards_left = G.GAME.current_round.discards_left }
227+
)
219228
return
220229
end
221230

@@ -227,8 +236,7 @@ API.functions["play_hand_or_discard"] = function(args)
227236
-- Check that all cards are selectable
228237
for _, card_index in ipairs(args.cards) do
229238
if not G.hand.cards[card_index] then
230-
sendErrorMessage("Invalid card index: " .. tostring(card_index), "BALATROBOT")
231-
API.send_response({ error = "Invalid card index: " .. tostring(card_index) })
239+
API.send_error_response("Invalid card index", { card_index = card_index, hand_size = #G.hand.cards })
232240
return
233241
end
234242
end
@@ -247,16 +255,15 @@ API.functions["play_hand_or_discard"] = function(args)
247255
local discard_button = UIBox:get_UIE_by_ID("discard_button", G.buttons.UIRoot)
248256
G.FUNCS["discard_cards_from_highlighted"](discard_button)
249257
else
250-
sendErrorMessage("Invalid action arg for play_hand_or_discard: " .. args.action, "BALATROBOT")
251-
API.send_response({ error = "Invalid action arg for play_hand_or_discard: " .. args.action })
258+
API.send_error_response("Invalid action arg for play_hand_or_discard", { action = args.action })
252259
return
253260
end
254261

255262
-- Defer sending response until the run has started
256263
API.pending_requests["play_hand_or_discard"] = {
257264
condition = function()
258265
-- TODO: maybe remove brittle G.E_MANAGER check
259-
if #G.E_MANAGER.queues.base < 3 and G.STATE_COMPLETE then
266+
if #G.E_MANAGER.queues.base < EVENT_QUEUE_THRESHOLD and G.STATE_COMPLETE then
260267
-- round still going
261268
if G.buttons and G.STATE == G.STATES.SELECTING_HAND then
262269
return true
@@ -280,15 +287,14 @@ end
280287
API.functions["cash_out"] = function(_)
281288
-- Validate current game state is appropriate for cash out
282289
if G.STATE ~= G.STATES.ROUND_EVAL then
283-
sendErrorMessage("Cannot cash out when not in shop. Current state: " .. tostring(G.STATE), "BALATROBOT")
284-
API.send_response({ error = "Cannot cash out when not in shop", state = G.STATE })
290+
API.send_error_response("Cannot cash out when not in shop", { current_state = G.STATE })
285291
return
286292
end
287293

288294
G.FUNCS.cash_out({ config = {} })
289295
API.pending_requests["cash_out"] = {
290296
condition = function()
291-
return G.STATE == G.STATES.SHOP and #G.E_MANAGER.queues.base < 3 and G.STATE_COMPLETE
297+
return G.STATE == G.STATES.SHOP and #G.E_MANAGER.queues.base < EVENT_QUEUE_THRESHOLD and G.STATE_COMPLETE
292298
end,
293299
action = function()
294300
local game_state = utils.get_game_state()

0 commit comments

Comments
 (0)