mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
Add server-side validation support to password and PIN minigames
- Update password minigame to call server API when correctPassword is null - Update PIN minigame to call server API when correctPin is null - Pass lockable and type parameters to minigames for server validation - Maintain backwards compatibility with client-side validation - Handle network errors gracefully without counting failed attempts This allows minigames to validate attempts server-side for security, preventing client-side answer spoofing.
This commit is contained in:
@@ -373,23 +373,68 @@ export class PasswordMinigame extends MinigameScene {
|
||||
}
|
||||
}
|
||||
|
||||
submitPassword() {
|
||||
async submitPassword() {
|
||||
if (!this.gameState.isActive) return;
|
||||
|
||||
|
||||
const enteredPassword = this.passwordField.value.trim();
|
||||
|
||||
|
||||
if (!enteredPassword) {
|
||||
this.showFailure("Please enter a password", false, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.gameData.attempts++;
|
||||
this.attemptsDisplay.textContent = this.gameData.attempts;
|
||||
|
||||
if (enteredPassword === this.correctPassword) {
|
||||
this.passwordCorrect();
|
||||
|
||||
// Check if we need server-side validation (correctPassword is null)
|
||||
if (this.correctPassword === null && window.APIClient && window.gameId) {
|
||||
await this.validatePasswordWithServer(enteredPassword);
|
||||
} else {
|
||||
this.passwordIncorrect();
|
||||
// Client-side validation (backwards compatibility)
|
||||
if (enteredPassword === this.correctPassword) {
|
||||
this.passwordCorrect();
|
||||
} else {
|
||||
this.passwordIncorrect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async validatePasswordWithServer(enteredPassword) {
|
||||
try {
|
||||
// Get lockable object and type from params
|
||||
const lockable = this.params.lockable || this.params.sprite;
|
||||
const targetType = this.params.type || 'object'; // 'door' or 'object'
|
||||
|
||||
// Get target ID from lockable
|
||||
let targetId;
|
||||
if (targetType === 'door') {
|
||||
targetId = lockable.doorProperties?.connectedRoom || lockable.doorProperties?.roomId;
|
||||
} else {
|
||||
targetId = lockable.scenarioData?.id || lockable.scenarioData?.name || lockable.objectId;
|
||||
}
|
||||
|
||||
if (!targetId) {
|
||||
console.error('Could not determine targetId for unlock validation');
|
||||
this.passwordIncorrect();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Validating password with server:', { targetType, targetId, attempt: enteredPassword });
|
||||
|
||||
// Call server API for validation
|
||||
const response = await window.APIClient.unlock(targetType, targetId, enteredPassword, 'password');
|
||||
|
||||
if (response.success) {
|
||||
this.passwordCorrect();
|
||||
} else {
|
||||
this.passwordIncorrect();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Server validation error:', error);
|
||||
this.showFailure("Network error. Please try again.", false, 3000);
|
||||
// Decrease attempts counter since this wasn't a real attempt
|
||||
this.gameData.attempts--;
|
||||
this.attemptsDisplay.textContent = this.gameData.attempts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,31 +240,73 @@ export class PinMinigame extends MinigameScene {
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
handleEnter() {
|
||||
async handleEnter() {
|
||||
if (this.isLocked || this.currentInput.length !== this.pinLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.attemptCount++;
|
||||
const isCorrect = this.currentInput === this.correctPin;
|
||||
|
||||
|
||||
// Check if we need server-side validation (correctPin is null)
|
||||
let isCorrect;
|
||||
if (this.correctPin === null && window.APIClient && window.gameId) {
|
||||
isCorrect = await this.validatePinWithServer(this.currentInput);
|
||||
} else {
|
||||
// Client-side validation (backwards compatibility)
|
||||
isCorrect = this.currentInput === this.correctPin;
|
||||
}
|
||||
|
||||
// Record attempt
|
||||
const attempt = {
|
||||
input: this.currentInput,
|
||||
isCorrect: isCorrect,
|
||||
timestamp: new Date(),
|
||||
feedback: (this.infoLeakToggleElement?.checked || this.infoLeakMode) ? this.calculateFeedback(this.currentInput) : null
|
||||
feedback: (this.infoLeakToggleElement?.checked || this.infoLeakMode) && this.correctPin ? this.calculateFeedback(this.currentInput) : null
|
||||
};
|
||||
|
||||
|
||||
this.attempts.push(attempt);
|
||||
this.updateAttemptsDisplay();
|
||||
|
||||
|
||||
if (isCorrect) {
|
||||
this.handleSuccess();
|
||||
} else {
|
||||
this.handleFailure();
|
||||
}
|
||||
}
|
||||
|
||||
async validatePinWithServer(enteredPin) {
|
||||
try {
|
||||
// Get lockable object and type from params
|
||||
const lockable = this.params.lockable || this.params.sprite;
|
||||
const targetType = this.params.type || 'object'; // 'door' or 'object'
|
||||
|
||||
// Get target ID from lockable
|
||||
let targetId;
|
||||
if (targetType === 'door') {
|
||||
targetId = lockable.doorProperties?.connectedRoom || lockable.doorProperties?.roomId;
|
||||
} else {
|
||||
targetId = lockable.scenarioData?.id || lockable.scenarioData?.name || lockable.objectId;
|
||||
}
|
||||
|
||||
if (!targetId) {
|
||||
console.error('Could not determine targetId for unlock validation');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('Validating PIN with server:', { targetType, targetId, attempt: enteredPin });
|
||||
|
||||
// Call server API for validation
|
||||
const response = await window.APIClient.unlock(targetType, targetId, enteredPin, 'pin');
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error('Server validation error:', error);
|
||||
this.showFailure("Network error. Please try again.", false, 3000);
|
||||
// Decrease attempts counter since this wasn't a real attempt
|
||||
this.attemptCount--;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
calculateFeedback(input) {
|
||||
// Mastermind-style feedback: right number in right place, right number in wrong place
|
||||
|
||||
@@ -526,9 +526,11 @@ export function startPinMinigame(lockable, type, correctPin, callback) {
|
||||
title: `Enter PIN for ${type}`,
|
||||
correctPin: correctPin,
|
||||
maxAttempts: 3,
|
||||
pinLength: correctPin.length,
|
||||
pinLength: correctPin ? correctPin.length : 4, // Default to 4 if null (server-side validation)
|
||||
hasPinCracker: hasPinCracker,
|
||||
allowBackspace: true,
|
||||
lockable: lockable,
|
||||
type: type, // Pass type for server validation
|
||||
onComplete: (success, result) => {
|
||||
if (success) {
|
||||
console.log('PIN MINIGAME SUCCESS');
|
||||
@@ -579,6 +581,7 @@ export function startPasswordMinigame(lockable, type, correctPassword, callback,
|
||||
postitNote: options.postitNote || '',
|
||||
showPostit: options.showPostit || false,
|
||||
lockable: lockable,
|
||||
type: type, // Pass type for server validation
|
||||
requiresKeyboardInput: true, // Password minigame needs keyboard for text input
|
||||
onComplete: (success, result) => {
|
||||
if (success) {
|
||||
|
||||
Reference in New Issue
Block a user