Allow unlocked doors and containers to notify server for access

Fixed issue where unlocked doors/containers couldn't be opened because
the server wasn't being notified to add them to unlockedRooms/unlockedObjects.

Server changes (game.rb):
- Updated validate_unlock to accept method='unlocked' for unlocked targets
- Added logic to grant access for unlocked doors/objects without validation

Client changes (doors.js):
- Updated handleDoorInteraction to notify server for unlocked doors
- Calls notifyServerUnlock with method='unlocked' before opening

Client changes (interactions.js):
- Added notifyServerForUnlockedContainer helper function
- Updated container interaction to notify server before launching minigame

This ensures that all room/container access is properly authorized on the
server side, preventing 403 Forbidden errors when loading room/container data.
This commit is contained in:
Claude
2025-11-21 19:10:13 +00:00
parent 1b38c7c9b9
commit 0827be2ce1
3 changed files with 66 additions and 24 deletions

View File

@@ -144,7 +144,13 @@ module BreakEscape
if target_type == 'door'
room = room_data(target_id)
return false unless room && room['locked']
return false unless room
# Handle unlocked doors - allow access without lock validation
if method == 'unlocked' || !room['locked']
Rails.logger.info "[BreakEscape] Door is unlocked, granting access"
return true
end
case method
when 'key', 'lockpick', 'biometric', 'bluetooth', 'rfid'
@@ -166,21 +172,25 @@ module BreakEscape
if object
Rails.logger.info "[BreakEscape] Found object: id=#{object['id']}, name=#{object['name']}, locked=#{object['locked']}, requires=#{object['requires']}"
end
next unless object && object['locked']
# Handle unlocked objects - allow access without lock validation
if method == 'unlocked' || !object['locked']
Rails.logger.info "[BreakEscape] Object is unlocked, granting access"
return true
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
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
end
Rails.logger.warn "[BreakEscape] Object not found or not locked: #{target_id}"
Rails.logger.warn "[BreakEscape] Object not found: #{target_id}"
false
end
end

View File

@@ -544,37 +544,40 @@ export function createDoorSpritesForRoom(roomId, position) {
}
// Function to handle door interactions
function handleDoorInteraction(doorSprite) {
async function handleDoorInteraction(doorSprite) {
const player = window.player;
if (!player) return;
const distance = Phaser.Math.Distance.Between(
player.x, player.y,
doorSprite.x, doorSprite.y
);
const DOOR_INTERACTION_RANGE = 2 * TILE_SIZE;
if (distance > DOOR_INTERACTION_RANGE) {
console.log('Door too far to interact');
return;
}
const props = doorSprite.doorProperties;
console.log(`Interacting with door: ${props.roomId} -> ${props.connectedRoom}`);
// Check if locks are disabled for testing
if (window.DISABLE_LOCKS) {
console.log('LOCKS DISABLED FOR TESTING - Opening door directly');
openDoor(doorSprite);
return;
}
if (props.locked) {
console.log(`Door is locked. Type: ${props.lockType}, Requires: ${props.requires}`);
// Use unified unlock system for consistent behavior with items
handleUnlock(doorSprite, 'door');
} else {
console.log('Door is unlocked, notifying server to grant access');
// Notify server to add room to unlockedRooms even for unlocked doors
const serverResponse = await notifyServerUnlock(doorSprite, 'door', 'unlocked');
openDoor(doorSprite);
}
}

View File

@@ -13,6 +13,33 @@ export function setGameInstance(gameInstance) {
gameRef = gameInstance;
}
// Helper function to notify server for unlocked container access
async function notifyServerForUnlockedContainer(sprite) {
const apiClient = window.ApiClient || window.APIClient;
const gameId = window.breakEscapeConfig?.gameId;
if (!apiClient || !gameId) {
console.warn('ApiClient or gameId not available, skipping server notification');
return;
}
try {
const targetId = sprite.scenarioData?.id || sprite.scenarioData?.name || sprite.objectId;
console.log(`Notifying server of unlocked container access: ${targetId}`);
const response = await apiClient.unlock('object', targetId, null, 'unlocked');
// If server returned contents, populate them
if (response.hasContents && response.contents && sprite.scenarioData) {
console.log('Server returned container contents:', response.contents);
sprite.scenarioData.contents = response.contents;
}
} catch (error) {
console.error('Failed to notify server of unlocked container access:', error);
// Continue anyway - don't block the user experience
}
}
// Helper function to calculate interaction distance with direction-based offset
// Extends reach from the edge of the player sprite in the direction the player is facing
function getInteractionDistance(playerSprite, targetX, targetY) {
@@ -695,17 +722,19 @@ export function handleObjectInteraction(sprite) {
// Handle container items (suitcase, briefcase, bags, bins, etc.) - check BEFORE lock check
if (data.type === 'suitcase' || data.type === 'briefcase' || data.type === 'bag1' || data.type === 'bin1' || data.contents) {
console.log('CONTAINER ITEM INTERACTION', data);
// Check if container is locked
if (data.locked === true) {
console.log('CONTAINER LOCKED - UNLOCK SYSTEM WILL HANDLE', data);
handleUnlock(sprite, 'item');
return;
}
// Container is unlocked (or has no lock) - launch the container minigame
console.log('CONTAINER UNLOCKED/OPEN - LAUNCHING MINIGAME', data);
handleContainerInteraction(sprite);
// Container is unlocked (or has no lock) - notify server and launch the container minigame
console.log('CONTAINER UNLOCKED/OPEN - NOTIFYING SERVER AND LAUNCHING MINIGAME', data);
notifyServerForUnlockedContainer(sprite).then(() => {
handleContainerInteraction(sprite);
});
return;
}