Fix scenario endpoint to return filtered data and revert client to use /scenario

Changes:
- Revert client game.js to use /scenario endpoint instead of /scenario_map
- Update /scenario endpoint to return filtered_scenario_for_bootstrap
- Add filter_requires_recursive method to remove sensitive 'requires' fields
- Keep /scenario_map for potential future navigation queries
- Add error handling to scenario endpoint

Rationale:
- The game client expects full scenario structure (startRoom, rooms, startItemsInInventory, etc.)
- scenario_map returns minimal structure incompatible with game initialization
- filtered_scenario_for_bootstrap provides room metadata without objects (lazy-loaded)
- filter_requires_recursive ensures puzzle solutions aren't exposed to client
- Maintains security while providing necessary data for game initialization
This commit is contained in:
Z. Cliffe Schreuders
2025-11-22 00:46:55 +00:00
parent 25dff05d39
commit 905c5643b5
2 changed files with 30 additions and 4 deletions

View File

@@ -10,10 +10,23 @@ module BreakEscape
end
# GET /games/:id/scenario
# Returns scenario JSON for this game instance
# Returns filtered scenario JSON for this game instance
# Uses filtered_scenario_for_bootstrap for lazy-loading support
def scenario
authorize @game if defined?(Pundit)
render json: @game.scenario_data
begin
# Use filtered bootstrap scenario and remove 'requires' fields for security
filtered = @game.filtered_scenario_for_bootstrap
# Remove 'requires' fields recursively for security
filter_requires_recursive(filtered)
render json: filtered
rescue => e
Rails.logger.error "[BreakEscape] scenario error: #{e.message}\n#{e.backtrace.first(5).join("\n")}"
render_error("Failed to generate scenario: #{e.message}", :internal_server_error)
end
end
# GET /games/:id/scenario_map
@@ -277,6 +290,19 @@ module BreakEscape
@game = Game.find(params[:id])
end
def filter_requires_recursive(obj)
case obj
when Hash
# Remove 'requires' (the answer/solution) from all objects
obj.delete('requires')
# Recursively filter nested structures
obj.each_value { |value| filter_requires_recursive(value) }
when Array
obj.each { |item| filter_requires_recursive(item) }
end
end
def track_npc_encounters(room_id, room_data)
return unless room_data['npcs'].present?

View File

@@ -441,9 +441,9 @@ export function preload() {
// Load scenario from Rails API endpoint if available, otherwise try URL parameter
if (window.breakEscapeConfig?.apiBasePath) {
// Load scenario map from Rails API endpoint (minimal metadata for lazy-loading)
// Load scenario from Rails API endpoint (returns filtered scenario for security)
// Use absolute URL with origin to prevent Phaser baseURL from interfering
const scenarioUrl = `${window.location.origin}${window.breakEscapeConfig.apiBasePath}/scenario_map`;
const scenarioUrl = `${window.location.origin}${window.breakEscapeConfig.apiBasePath}/scenario`;
this.load.json('gameScenarioJSON', scenarioUrl);
} else {
// Fallback to old behavior for standalone HTML files