Fix TypeError in track_npc_encounters and add robust error handling to room endpoint

Changes:
- Fix track_npc_encounters to handle case where encounteredNPCs is not an array
- Add type checking to ensure encounteredNPCs is always an array before operations
- Add comprehensive error handling and logging to room endpoint
- Add scenario_data presence check before processing room request
- Wrap room endpoint in try-catch to catch and log all errors

Fixes:
- TypeError: no implicit conversion of String into Array on line 312
- Prevents 500 errors when player_state has malformed data
- Provides clear error messages and stack traces for debugging
This commit is contained in:
Z. Cliffe Schreuders
2025-11-22 00:46:55 +00:00
parent 905c5643b5
commit c2ed2fb033

View File

@@ -82,34 +82,45 @@ module BreakEscape
def room
authorize @game if defined?(Pundit)
room_id = params[:room_id]
return render_error('Missing room_id parameter', :bad_request) unless room_id.present?
begin
room_id = params[:room_id]
return render_error('Missing room_id parameter', :bad_request) unless room_id.present?
# Check if room is accessible (starting room OR in unlockedRooms)
is_start_room = @game.scenario_data['startRoom'] == room_id
is_unlocked = @game.player_state['unlockedRooms']&.include?(room_id)
# Check if scenario_data exists
unless @game.scenario_data.present?
Rails.logger.error "[BreakEscape] room: Game #{@game.id} has no scenario_data"
return render_error('Scenario data not available', :internal_server_error)
end
unless is_start_room || is_unlocked
return render_error("Room not accessible: #{room_id}", :forbidden)
# Check if room is accessible (starting room OR in unlockedRooms)
is_start_room = @game.scenario_data['startRoom'] == room_id
is_unlocked = @game.player_state['unlockedRooms']&.include?(room_id)
unless is_start_room || is_unlocked
return render_error("Room not accessible: #{room_id}", :forbidden)
end
# Auto-add start room if missing (defensive programming)
if is_start_room && !is_unlocked
@game.player_state['unlockedRooms'] ||= []
@game.player_state['unlockedRooms'] << room_id
@game.save!
end
# Get and filter room data
room_data = @game.filtered_room_data(room_id)
return render_error("Room not found: #{room_id}", :not_found) unless room_data
# Track NPC encounters BEFORE sending response
track_npc_encounters(room_id, room_data)
Rails.logger.debug "[BreakEscape] Serving room data for: #{room_id}"
render json: { room_id: room_id, room: room_data }
rescue => e
Rails.logger.error "[BreakEscape] room error: #{e.message}\n#{e.backtrace.first(10).join("\n")}"
render_error("Failed to load room: #{e.message}", :internal_server_error)
end
# Auto-add start room if missing (defensive programming)
if is_start_room && !is_unlocked
@game.player_state['unlockedRooms'] ||= []
@game.player_state['unlockedRooms'] << room_id
@game.save!
end
# Get and filter room data
room_data = @game.filtered_room_data(room_id)
return render_error("Room not found: #{room_id}", :not_found) unless room_data
# Track NPC encounters BEFORE sending response
track_npc_encounters(room_id, room_data)
Rails.logger.debug "[BreakEscape] Serving room data for: #{room_id}"
render json: { room_id: room_id, room: room_data }
end
# GET /games/:id/container/:container_id
@@ -307,8 +318,15 @@ module BreakEscape
return unless room_data['npcs'].present?
npc_ids = room_data['npcs'].map { |npc| npc['id'] }
# Ensure encounteredNPCs is an array
@game.player_state['encounteredNPCs'] ||= []
# Handle case where encounteredNPCs might be a string (legacy data)
unless @game.player_state['encounteredNPCs'].is_a?(Array)
@game.player_state['encounteredNPCs'] = []
end
new_npcs = npc_ids - @game.player_state['encounteredNPCs']
return if new_npcs.empty?