function sanitizeNumber(value, defaultValue = 0, min = 0, max = Infinity) {
const num = parseInt(value, 10);
if(isNaN(num) || num < min || num > max) return defaultValue;
return num;
}Perlindungan terhadap:
- Invalid data types (string, object, undefined)
- Out of range values
- NaN (Not a Number)
- Negative scores (jika tidak diinginkan)
- Extremely large numbers yang bisa menyebabkan overflow
// Load high score dengan validation
gameState.highscore = sanitizeNumber(data.highscore, 0, 0, 999999999);Browser modern memerlukan user interaction sebelum audio dapat diputar (autoplay policy).
function ensureAudio() {
if(!audioCtx) {
try {
audioCtx = new AudioCtx();
} catch(e) {
console.error('Audio not supported:', e);
audioCtx = null;
}
}
}Perlindungan terhadap:
- Browser yang tidak support Web Audio API
- Permission errors
- Autoplay policy violations
- Memory leaks dari audio context
function loadHighscore() {
try {
const saved = localStorage.getItem(CONFIG.STORAGE_KEY);
if(saved) {
const data = JSON.parse(saved);
gameState.highscore = sanitizeNumber(data.highscore, 0, 0, 999999999);
}
} catch(e) {
console.error('Error loading highscore:', e);
gameState.highscore = 0;
}
}Perlindungan terhadap:
- QuotaExceededError (storage penuh)
- SecurityError (private browsing mode)
- JSON parsing errors (corrupt data)
- localStorage tidak available
function loadSettings() {
try {
const saved = localStorage.getItem(CONFIG.SETTINGS_KEY);
if(saved) {
const parsed = JSON.parse(saved);
if(typeof parsed === 'object') {
gameState.settings = {
soundEnabled: parsed.soundEnabled !== false,
ghostEnabled: parsed.ghostEnabled !== false,
gridEnabled: parsed.gridEnabled !== false
};
updateSettingsUI();
}
}
} catch(e) {
console.error('Error loading settings:', e);
}
}Perlindungan terhadap:
- Type coercion attacks
- Undefined properties
- Invalid boolean values
Kode diorganisir dalam sections yang jelas:
// ==================== CONFIGURATION ====================
// ==================== GAME STATE ====================
// ==================== DOM ELEMENTS ====================
// ==================== TETROMINO DEFINITIONS ====================
// ==================== AUDIO SYSTEM ====================
// ==================== UTILITY FUNCTIONS ====================
// ==================== DRAWING FUNCTIONS ====================
// ==================== GAME LOGIC ====================
// ==================== GAME LOOP ====================
// ==================== INPUT HANDLERS ====================
// ==================== INITIALIZATION ====================Setiap fungsi didokumentasikan:
/**
* Validate and sanitize localStorage data
*/
function sanitizeNumber(value, defaultValue = 0, min = 0, max = Infinity) {
// implementation
}Semua konstanta dikumpulkan di satu tempat:
const CONFIG = {
COLS: 10,
ROWS: 20,
CELL_SIZE: 28,
STORAGE_KEY: 'tetris_v2_data',
SETTINGS_KEY: 'tetris_v2_settings'
};Keuntungan:
- Easy to modify
- No magic numbers
- Centralized configuration
- Version control friendly
State management yang terpusat:
const gameState = {
arena: null,
score: 0,
lines: 0,
level: 1,
highscore: 0,
dropCounter: 0,
dropInterval: 1000,
lastTime: 0,
paused: false,
running: false,
gameStartTime: 0,
totalPieces: 0,
holdUsed: false,
settings: {
soundEnabled: true,
ghostEnabled: true,
gridEnabled: true
}
};Keuntungan:
- Single source of truth
- Easy debugging
- Clear state management
- Prevent global variable pollution
Semua elemen interaktif memiliki data-testid:
<canvas id="tetris" data-testid="game-canvas"></canvas>
<button id="btn-left" data-testid="btn-left">β</button>
<div id="score" data-testid="score">0</div>Keuntungan:
- Automated testing dengan Playwright/Cypress
- Stable selectors (tidak bergantung pada class/id)
- Better test maintainability
Menggunakan requestAnimationFrame untuk game loop:
function update(time = 0) {
if(!gameState.running) return;
// game logic
requestAnimationFrame(update);
}Keuntungan:
- Browser-optimized rendering
- Better frame rate
- Automatic pause when tab inactive
- Efficient CPU usage
Clear dan redraw yang efisien:
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw only what's needed
}Efficient event handling:
// Single event listener untuk keyboard
window.addEventListener('keydown', handleKey);// High score data
{
"highscore": 12500,
"timestamp": 1704067200000
}
// Settings data
{
"soundEnabled": true,
"ghostEnabled": true,
"gridEnabled": true
}- Data disimpan di browser user (localStorage)
- Tidak ada data yang dikirim ke server
- User dapat clear data kapan saja (browser settings)
- No cookies, no tracking, no analytics
- Input validation untuk semua user input
- Sanitasi data localStorage
- Error handling untuk external APIs
- No eval() atau dangerous functions
- No inline event handlers
- Content Security Policy compatible
- HTTPS ready
- No external dependencies (no supply chain attacks)
- No user-generated content execution
- Private browsing mode compatible
Untuk pengalaman terbaik:
- Enable JavaScript
- Enable localStorage
- Allow Web Audio API
- Modern browser (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
Jika menemukan kerentanan keamanan, silakan laporkan ke:
- Email: security@ahliweb.co.id
- Atau buat issue di GitHub dengan label "security"
Last Updated: January 2025
Version: 2.0.0