Enhance object unlocking logic, add locked field to containers, and validate scenario schema

This commit is contained in:
Z. Cliffe Schreuders
2026-02-10 14:12:39 +00:00
parent 6ad3cda6ae
commit 321a500f24
5 changed files with 56 additions and 31 deletions

View File

@@ -274,42 +274,50 @@ module BreakEscape
return true
end
# Find object in all rooms - check both id and name
scenario_data['rooms'].each do |_room_id, room_data|
object = room_data['objects']&.find { |obj|
obj['id'] == target_id || obj['name'] == target_id
}
if object
Rails.logger.info "[BreakEscape] Found object: id=#{object['id']}, name=#{object['name']}, locked=#{object['locked']}, requires=#{object['requires']}"
# Handle method='unlocked' - verify against scenario data
if method == 'unlocked'
if !object['locked']
Rails.logger.info "[BreakEscape] Object is unlocked in scenario data, granting access"
return true
else
Rails.logger.warn "[BreakEscape] SECURITY VIOLATION: Client sent method='unlocked' for LOCKED object: #{target_id}"
return false
end
# Find object in all rooms - check id, name, or generated client ID
object = nil
scenario_data['rooms'].each do |room_id, room_data|
next unless room_data['objects']
room_data['objects'].each_with_index do |obj, index|
# Client generates IDs as: roomId_type_index
client_generated_id = "#{room_id}_#{obj['type']}_#{index}"
if obj['id'] == target_id || obj['name'] == target_id || client_generated_id == target_id
object = obj
break
end
end
break if object
end
# NPC unlock: Validate NPC has been encountered and has permission to unlock this object
if method == 'npc'
npc_id = attempt # NPC id is passed as 'attempt'
return validate_npc_unlock(npc_id, target_id)
end
if object
Rails.logger.info "[BreakEscape] Found object: id=#{object['id']}, name=#{object['name']}, locked=#{object['locked']}, requires=#{object['requires']}"
case method
when 'key', 'lockpick', 'biometric', 'bluetooth', 'rfid'
# Client validated the unlock - trust it
# Handle method='unlocked' - verify against scenario data
if method == 'unlocked'
if !object['locked']
Rails.logger.info "[BreakEscape] Object is unlocked in scenario data, granting access"
return true
when 'pin', 'password'
result = object['requires'].to_s == attempt.to_s
Rails.logger.info "[BreakEscape] Password validation: required='#{object['requires']}', attempt='#{attempt}', result=#{result}"
return result
else
Rails.logger.warn "[BreakEscape] SECURITY VIOLATION: Client sent method='unlocked' for LOCKED object: #{target_id}"
return false
end
end
# NPC unlock: Validate NPC has been encountered and has permission to unlock this object
if method == 'npc'
npc_id = attempt # NPC id is passed as 'attempt'
return validate_npc_unlock(npc_id, target_id)
end
case method
when 'key', 'lockpick', 'biometric', 'bluetooth', 'rfid'
# Client validated the unlock - trust it
return true
when 'pin', 'password'
result = object['requires'].to_s == attempt.to_s
Rails.logger.info "[BreakEscape] Password validation: required='#{object['requires']}', attempt='#{attempt}', result=#{result}"
return result
end
end
Rails.logger.warn "[BreakEscape] Object not found: #{target_id}"
false

View File

@@ -32,6 +32,7 @@
{
"type": "bag",
"name": "Heist Gear Backpack",
"locked": false,
"contents": [
{
"type": "lockpick",

View File

@@ -289,6 +289,8 @@
"tablet",
"safe",
"suitcase",
"bag",
"briefcase",
"bluetooth_scanner",
"fingerprint_kit",
"pin-cracker",
@@ -296,7 +298,8 @@
"flag-station",
"text_file",
"id_badge",
"rfid_cloner"
"rfid_cloner",
"office-misc-hdd4"
],
"description": "Item type. Custom types (like 'id_badge', 'rfid_cloner') are valid for #give_item tags in Ink scripts."
},

View File

@@ -207,6 +207,14 @@ def check_common_issues(json_data)
has_container_with_contents = true
end
# REQUIRED: Containers with contents must specify locked field explicitly
container_types = ['briefcase', 'bag', 'bag1', 'suitcase', 'safe', 'pc', 'bin1']
if container_types.include?(obj['type']) && obj['contents'] && !obj['contents'].empty?
unless obj.key?('locked')
issues << "❌ INVALID: '#{path}' is a container with contents but missing required 'locked' field - must be explicitly true or false for server-side validation"
end
end
# Track readable items (notes, documents)
if obj['readable'] || (obj['type'] == 'notes' && obj['text'])
has_readable_items = true

5
start_server.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# kill any puma processes
pkill -9 -f puma; sleep 1
# start the server
bundle exec rails server -b 0.0.0.0 -p 3000 2>&1 &