Skip to content

Latest commit

 

History

History
612 lines (511 loc) · 24 KB

File metadata and controls

612 lines (511 loc) · 24 KB

HackerMystery Development Log

Project Overview

A 1990s hacker-themed adventure game with a hybrid terminal/windowed interface reminiscent of early Macintosh computers and the movie "Hackers" (1995). Built with Enyo 1 framework for compatibility with webOS devices and modern browsers.

Target Platforms:

  • webOS/LuneOS tablets and phones (legacy WebKit ~Safari 5-6 era)
  • Modern browsers (Chrome, Firefox, Safari)
  • Android (via Cordova wrapper)

Technical Constraints:

  • ES5 JavaScript only (no let/const, arrow functions, etc.)
  • Simple CSS (no flexbox, grid, CSS variables)
  • Must work offline with optional online features

Milestone 1: Playable Shell (12/6/2025)

Status: Complete Date: December 6, 2024

Features Implemented

Core UI Components

  • Desktop (source/ui/Desktop.js)

    • Icon grid with double-tap to launch programs
    • CSS-based terminal icon fallback (no image required)
  • Window (source/ui/Window.js)

    • Draggable Mac-style windows with touch support
    • Title bar with horizontal stripes
    • Close button
  • WindowManager (source/ui/WindowManager.js)

    • Z-order management (tap to bring to front)
    • Window positioning with cascade offset
    • Open/close lifecycle
  • Terminal (source/ui/Terminal.js)

    • Command input with history (up/down arrows)
    • Typewriter effect for output
    • Built-in commands: help, clear, echo, whoami, date, about, save, load

Core Systems

  • GameState (source/core/GameState.js)

    • Singleton state manager
    • Tracks inventory, flags, unlocked programs, command history
    • JSON serialization for save/load
  • SaveManager (source/core/SaveManager.js)

    • Abstracted save/load system (currently localStorage)
    • 10 save slots (0-9)
    • Ready for future server-side save implementation
    • Methods to replace: _saveToStorage(), _loadFromStorage()

Styling

  • Retro CSS (css/retro.css)
    • 1-bit Mac aesthetic (black/white window chrome)
      • This didn't work well on modern screens, need to think about how to do it better
    • Green-on-black terminal display
    • Bold 14px monospace text for readability on older displays

Cross-Browser Compatibility Lessons Learned

  1. CSS Gradients don't work on old WebKit

    • Solution: Use tiled data URI GIF/PNG images instead
    • Use @supports to provide gradient override for modern browsers
  2. CSS Transforms need -webkit- prefix

    • Always include both -webkit-transform and transform
  3. CSS calc() needs -webkit- prefix

    • Use -webkit-calc() for old Safari
  4. Multiple box-shadows may not work on old WebKit

    • Fallback to background images or pseudo-elements
  5. Vertical alignment in inputs

    • Use line-height and vertical-align: middle
    • May need @supports for browser-specific margin tweaks

File Structure

enyo-app/
├── depends.js              # Dependency loader
├── index.html              # Entry point
├── appinfo.json            # App metadata
├── source/
│   ├── App.js              # Main application
│   ├── core/
│   │   ├── GameState.js    # State management
│   │   └── SaveManager.js  # Save/load system
│   └── ui/
│       ├── Desktop.js      # Desktop with icons
│       ├── Window.js       # Draggable window
│       ├── WindowManager.js # Window lifecycle
│       └── Terminal.js     # Terminal emulator
├── css/
│   ├── game.css            # Core layout
│   └── retro.css           # Retro Mac styling
└── images/
    ├── icons/              # (placeholder for icons)
    └── ui/                 # (placeholder for UI assets)

Terminal Commands

Command Description
help Show available commands
clear Clear the terminal
echo [text] Print text to terminal
whoami Display current user
date Show current date and time
about About this system
save [0-9] Save game to slot
load [0-9] Load game from slot

Milestone 2: Core Programs (12/6/2025)

Status: Complete Date: December 6, 2024

Branding

  • Renamed game to "Hacker Mystery 95"
  • New app icon generated by Gemini Nano Banana image model

Features Implemented

Menu Bar (source/ui/MenuBar.js)

  • Mac-style persistent menu bar at top of screen
  • Hacker Mystery 95 menu: About dialog
  • File menu: Save Game, Load Game
  • View menu: Show Hidden Files toggle (context-sensitive, only for FileViewer)
  • Checkmark indicator for toggled options (using √ for old device compatibility)
  • Touch-friendly event handling with old WebKit compatibility

Window Focus Indication

  • Title bar stripes only appear on focused window
  • Unfocused windows have plain white title bars
  • Focus updates on window click, close, and z-order changes

Alert Modal System

  • Reusable modal dialog in App.js
  • Used for: About dialog, Save/Load confirmations, Network locked message

Virtual Filesystem (source/data/FileSystem.js)

  • Singleton pattern for global access
  • Tree structure with folders and files
  • File types: text, folder, encrypted
  • Support for locked files (unlock with game flags)
  • Support for hidden files/folders
  • Encrypted files with password protection and decrypted content
  • Chapter 1 story content integrated:
    • /home/readme.txt - Initial hook
    • /home/documents/ - Manifesto, notes pointing to Acid Burn
    • /home/logs/ - System and connection logs with BBS hints
    • /home/trash/ - Deleted email about Project GIBSON, password fragment
    • /home/secrets/ - Hidden folder with encrypted secrets and BBS list

File Viewer (source/programs/FileViewer.js)

  • Finder-style icon grid layout
  • Double-tap to open files/folders
  • Path bar showing current location
  • Visual indicators for locked/hidden files
  • Parent folder navigation (..)
  • Fires onOpenFile event to open files in TextEditor

Text Editor (source/programs/TextEditor.js)

  • View text files from the virtual filesystem
  • Info bar with filename
  • Scrollable content area
  • Password prompt for encrypted files
  • Sets game flags when files are decrypted
  • HTML-escaped content for security

Desktop Updates

  • Added Files icon (folder-style)
  • Added Network icon (locked/disabled)
  • CSS-based icons for all desktop items

Window System Updates

  • contentOptions support for passing data to window content
  • Event forwarding from content to App (e.g., onOpenFile)

Story Progression (Chapter 1)

  1. Player starts with Terminal auto-launched
  2. Opens Files from Desktop icon
  3. Explores /home/readme.txt - mysterious message from "X"
  4. Finds /home/documents/notes.txt - learns about Acid Burn and BBS
  5. Checks /home/trash/fragment.txt - discovers password "hackers"
  6. Finds /home/secrets/ folder (hidden, needs discovery)
  7. Opens too_many_secrets.enc - uses password to decrypt
  8. Learns about Project GIBSON and need to find Acid Burn on BBS

Updated File Structure

enyo-app/
├── source/
│   ├── App.js              # Main app with menu handling, alerts
│   ├── core/
│   │   ├── GameState.js    # Singleton state manager
│   │   └── SaveManager.js  # localStorage save/load (sync + async APIs)
│   ├── data/
│   │   └── FileSystem.js   # Virtual filesystem with hidden files
│   ├── programs/
│   │   ├── FileViewer.js   # Icon-grid file browser with Show Hidden
│   │   └── TextEditor.js   # Text file viewer with encryption support
│   └── ui/
│       ├── Desktop.js      # Desktop with Files, Network, Terminal icons
│       ├── MenuBar.js      # NEW: Mac-style menu bar
│       ├── Window.js       # Draggable windows with focus indication
│       ├── WindowManager.js # Z-order and focus management
│       └── Terminal.js     # Terminal emulator
├── css/
│   └── retro.css           # Retro Mac styling including menu bar
└── images/
    ├── icon.png            # App icon (Gemini Nano Banana)
    └── dither.gif          # Desktop background pattern

Bug Fixes & Improvements

  • Fixed desktop icons not responding to clicks (manual DOM event binding)
  • Fixed FileViewer appending content instead of replacing on navigation
  • Fixed window contentOptions not passing through to content components
  • Fixed menu dropdowns closing immediately on touch devices
  • Fixed z-index stacking for menu dropdowns vs overlay
  • Added synchronous saveGame()/loadGame() convenience methods to SaveManager
  • Added a beautiful icon for the game, thanks to Nano Banana.
  • Named the game "Hacker Mystery 95"

Technical Lessons Learned

Event Handling on Old WebKit

  • Enyo's onclick handlers can conflict with manual DOM event bindings
  • Solution: Use only manual DOM bindings in rendered() for complex interactive components
  • Always include old browser fallbacks:
    e = e || window.event;
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;

Character Compatibility

  • Avoid Unicode characters outside basic Latin (U+0000-U+00FF)
  • Use √ (U+221A) instead of ✓ (U+2713) for checkmarks
  • Old devices may not render newer Unicode symbols

Component Ownership in Enyo

  • When using createComponent(), the owner property determines which component's this.$ hash contains the new component
  • Components owned by a parent aren't destroyed when calling destroyComponents() on a container
  • Solution: Track dynamically created components in an array and destroy manually

Milestone 3: Puzzle Foundation (12/7/2025)

Status: Complete Date: December 7, 2024

Features Implemented

Puzzle Engine (source/core/PuzzleEngine.js)

  • Singleton pattern for global access
  • Puzzle states: locked, available, completed
  • Register puzzles with:
    • Required flags (prerequisites)
    • Completion flags (triggers)
    • Rewards (flags to set, puzzles to unlock)
  • Event system for puzzle completion listeners
  • Chapter progress tracking
  • Auto-completion chains (completing one puzzle can unlock others)

Chapter 1 Puzzles Registered

Puzzle ID Name Trigger
decrypt_secrets Too Many Secrets Decrypt the encrypted file
find_acid_burn Finding Acid Burn Contact Acid Burn on BBS
chapter1_complete Chapter 1 Complete Gain Acid Burn's trust

BBS System (Terminal-based)

Instead of a separate Email Client, email is integrated into the BBS systems accessed via the terminal - true to the 1995 experience.

BBSData (source/data/BBSData.js)

  • Data definitions for all BBS systems
  • Message boards with conditional visibility
  • Email system with flag-based message unlocking
  • Reply system with trigger words

BBSHandler (source/core/BBSHandler.js)

  • Session state machine (password, main menu, boards, email, etc.)
  • Menu navigation (boards, email, who's online, help)
  • Message board browsing
  • Email inbox with read/unread tracking
  • Reply composition with trigger-word detection
  • Automatic flag setting on story progression

BBS Systems

Phone Name Status Notes
555-0199 The Underground Active Password protected, main story BBS
555-0134 CyberDen Banned Player is banned
555-2176 PhreakHole Dead Shut down by feds
555-0200 The Gibson Files Locked Future content

Terminal Updates

  • New commands: dial <number>, hangup
  • BBS session routing (input goes to BBSHandler when connected)
  • Password prompt mode
  • "Press any key" handling for menus

TextEditor Integration

  • Notifies PuzzleEngine when files are decrypted
  • Consistent flag naming (strips file extension)

Story Progression (Chapter 1 Complete)

  1. Player explores filesystem, finds hints about BBS
  2. Discovers password "hackers" in trash folder
  3. Decrypts too_many_secrets.enc - learns about GIBSON → Puzzle 1: Too Many Secrets
  4. Uses terminal: dial 555-0199 to connect to The Underground
  5. Enters password "hackers"
  6. Reads Acid Burn's message on General board
  7. Replies to Acid Burn on board → Puzzle 2: First Contact
  8. Returns to main menu, gets new email notification
  9. Reads email from Acid Burn asking about what player found
  10. Replies mentioning "gibson" → Puzzle 3: Prove Yourself
  11. Returns to main menu, checks email again
  12. Reads Acid Burn's response about trusting player → Puzzle 4: Trusted Hacker
  13. Goes to Boards, Elite Section now visible
  14. Reads Acid Burn's message about inside contact → Puzzle 5: Inside Information
  15. Reads unlocked contacts.txt file → Sets knows_gibson_number flag for Chapter 2

Updated File Structure

enyo-app/
├── source/
│   ├── App.js
│   ├── core/
│   │   ├── GameState.js
│   │   ├── SaveManager.js
│   │   ├── PuzzleEngine.js    # NEW: Puzzle management
│   │   └── BBSHandler.js      # NEW: BBS session handler
│   ├── data/
│   │   ├── FileSystem.js
│   │   └── BBSData.js         # NEW: BBS content data
│   ├── programs/
│   │   ├── FileViewer.js
│   │   └── TextEditor.js      # Updated: PuzzleEngine integration
│   └── ui/
│       ├── Desktop.js
│       ├── MenuBar.js
│       ├── Window.js
│       ├── WindowManager.js
│       └── Terminal.js        # Updated: BBS commands

New Terminal Commands

Command Description
dial [number] Dial a BBS (e.g., dial 555-0199)
hangup Disconnect from current BBS

Score Counter (Menu Bar)

  • Displays puzzle completion count in top-right of menu bar
  • Clicking shows dropdown: "Hacked X of Y puzzles"
  • Updates in real-time when puzzles are completed

Sound Manager (source/core/SoundManager.js)

  • Cross-compatible audio system
  • Web Audio API for modern browsers (with buffer preloading)
  • HTML5 Audio fallback for older devices (webOS)
  • Graceful failure - sound errors never break game logic
  • onEnded callback support for sequenced audio
  • Preloads sounds at startup

Sound Effects

Sound Trigger
success.mp3 Puzzle completed (score increases)
dialup.mp3 Dialing a valid BBS
dialup-fail.mp3 Dialing an unknown number
dialup-noservice.mp3 Dialing a disconnected BBS (PhreakHole)
victory.mp3 Completing Chapter 1 (connecting to Gibson Files)

Quality of Life Improvements

  • Phone format validation: dial command requires XXX-XXXX format with helpful error message
  • Password flexibility: Both "hacker" and "hackers" accepted (case-insensitive)
  • GameState → PuzzleEngine integration: setFlag() automatically notifies puzzle engine
  • Delayed BBS replies: Board posts and emails queued until returning to main menu
  • New message indicators: "NEW MAIL" for email, "(New Reply!)" for board messages in main menu
  • Per-BBS dial sounds: Each BBS can specify its own dial sound (e.g., dialup-noservice.mp3 for dead lines)
  • Connect sounds: BBS can specify a sound to play on successful connection (e.g., victory sound)
  • Input disabled during dialing: Prevents command input race conditions while dial sound plays
  • Puzzle timing separation: "Prove Yourself" triggers when sending reply, "Trusted Hacker" triggers when reading response email
  • Chapter 1 ending: Gibson Files BBS shows congratulations message with victory sound

Updated Puzzle Flow

# Puzzle ID Name Trigger Required Flags
1 decrypt_secrets Too Many Secrets Decrypt too_many_secrets.enc None
2 reply_to_acid_burn First Contact Reply to Acid Burn on BBS board None
3 prove_yourself Prove Yourself Send email to Acid Burn mentioning GIBSON replied_to_acid_burn + decrypted_too_many_secrets
4 gain_trust Trusted Hacker Read Acid Burn's response email contacted_acid_burn
5 inside_contact Inside Information Read Elite board message from Acid Burn acid_burn_trusts_player

Updated File Structure

enyo-app/
├── source/
│   ├── App.js
│   ├── core/
│   │   ├── GameState.js       # Updated: notifies PuzzleEngine on flag changes
│   │   ├── SaveManager.js
│   │   ├── PuzzleEngine.js
│   │   ├── BBSHandler.js      # Updated: delayed message delivery
│   │   └── SoundManager.js    # NEW: Cross-compatible audio
│   ├── data/
│   │   ├── FileSystem.js
│   │   └── BBSData.js
│   ├── programs/
│   │   ├── FileViewer.js
│   │   └── TextEditor.js      # Updated: accepts hacker/hackers password
│   └── ui/
│       ├── Desktop.js
│       ├── MenuBar.js         # Updated: score counter
│       ├── Window.js
│       ├── WindowManager.js
│       └── Terminal.js        # Updated: dialup sound, format validation
├── sounds/
│   ├── success.mp3            # Puzzle completion sound
│   ├── dialup.mp3             # BBS dialing sound (~11 seconds)
│   ├── dialup-fail.mp3        # Invalid number dial sound
│   ├── dialup-noservice.mp3   # Disconnected number sound
│   └── victory.mp3            # Chapter completion fanfare

Technical Lessons Learned

Audio Compatibility on webOS

  • cloneNode() on Audio elements causes INVALID_STATE_ERR on old WebKit
  • Solution: Create fresh Audio() element each time, wait for canplay event
  • Always wrap audio operations in try-catch to prevent game logic failures
  • Call onEnded callback even when audio fails so game continues

Defensive Sound Design

  • Update game state (score) BEFORE playing sounds
  • Wrap sound playback in try-catch
  • Provide onEnded callback that fires even on failure
  • Check audioSupported flag before any audio operations

Milestone 4: BBS Bug Fixes & Guestbook Feature (12/24/2024)

Status: Complete Date: December 24, 2024

BBS Bug Fixes

Navigation Bug After Posting Board Reply

  • Issue: After posting a reply to a board message, pressing [B] went to the boards list instead of the message list
  • Fix: Changed state from STATE_READING_BOARD to STATE_READING_MESSAGE after posting, so [B] correctly maps to showBoardMessages()

Help and Who's Online "Press Any Key" Not Working

  • Issue: After viewing Help or Who's Online, pressing any key did nothing - user was stuck
  • Root Cause: showHelp() and showWhosOnline() returned waitForKey: true but didn't change the session state, so input was still processed by handleMainMenu() which expected a valid menu choice
  • Fix: Added new state STATE_PRESS_ANY_KEY that returns to main menu on any input

Terminal Empty Input for "Press Any Key"

  • Issue: In Terminal, empty input was rejected before reaching the waitForKey handler
  • Fix: Modified executeCurrentInput() to allow empty input when bbsWaitingForKey is true, and skip echo/history for "press any key" mode

BBS Disconnect on Terminal Close

  • Issue: Closing the terminal window while connected to a BBS left the connection in a stale state
  • Fix: Added bbsHandler.disconnect() call in Terminal's destroy() method

Guestbook Feature

Added an online guestbook to The Underground BBS (555-0199).

Client-Side Implementation (source/core/BBSHandler.js)

New States:

  • STATE_GUESTBOOK - Viewing guestbook entries
  • STATE_SIGNING_GUESTBOOK_NAME - Entering name to sign
  • STATE_SIGNING_GUESTBOOK - Entering message to sign

Features:

  • [S] See Guestbook option in main menu
  • Fetches guestbook.csv from remote server with cache-busting
  • Displays entries in BBS style with formatted dates (using toLocaleDateString())
  • Two-step signing process: name first, then message
  • Profanity filter using bad words list from GitHub
  • Offline fallback to hardcoded entry when server unreachable
  • Protocol detection - uses http by default, upgrades to https if app is served over https (for older devices with TLS issues)

New Methods:

  • showGuestbook() - Fetches and displays guestbook
  • fetchGuestbook(url, callback) - Async fetch helper
  • displayGuestbook(data) - Renders guestbook entries
  • handleGuestbook(input) - Handles S/M menu choice
  • handleSigningGuestbookName(input) - Collects username
  • handleSigningGuestbook(input) - Collects message and POSTs to server
  • loadBadWords() - Fetches profanity list on startup
  • containsProfanity(text) - Checks text with word boundary matching
  • upgradeUrlsIfSecure() - Upgrades http to https if needed
  • parseCSVLine(line) - Parses CSV handling quoted fields

Server-Side Implementation (server/guestbook.php)

Features:

  • Receives POST with username and message
  • Profanity filter with word boundary matching (same as client)
  • Caches bad words list in data/badwords_cache.txt for 30 days
  • Appends entries to data/guestbook.csv
  • Keeps only last 100 entries
  • CORS headers for cross-origin access
  • Error suppression to prevent broken responses

Security:

  • Sanitizes username (alphanumeric only, max 20 chars)
  • Truncates message to 200 chars
  • Escapes CSV special characters (commas, quotes, newlines)

Technical Lessons Learned

Word Boundary Matching for Profanity Filter

  • Simple substring matching causes false positives (e.g., "hello" contains "hell")
  • Solution: Use regex with word boundaries \b
  • JavaScript: new RegExp("\\b" + word + "\\b", "i")
  • PHP: '/\b' . preg_quote($word, '/') . '\b/i'

CSV Safety

  • Strip commas from user input to prevent CSV injection
  • Alternatively, wrap fields containing commas/quotes in double quotes and escape internal quotes

PHP Error Suppression

  • PHP warnings output to response body can break JSON/text responses
  • Use error_reporting(0) at script start
  • Use @ prefix on file operations that might fail
  • Store cache files in writable data/ directory, not code directory

Protocol Detection for Legacy Devices

  • Older devices may have issues with modern TLS certificates
  • Default to http for maximum compatibility
  • Upgrade to https only if app is already served over https: window.location.protocol === "https:"

Updated File Structure

enyo-app/
├── source/
│   ├── core/
│   │   └── BBSHandler.js      # Updated: guestbook, bug fixes
│   └── ui/
│       └── Terminal.js        # Updated: press-any-key fix, disconnect on close
├── server/
│   ├── guestbook.php          # NEW: Guestbook API
│   └── data/
│       ├── guestbook.csv      # Guestbook entries
│       └── badwords_cache.txt # Cached profanity list

Milestone 5: Chapter 2 - The Heist

Status: Planning Date: TBD

Story Hooks from Chapter 1

  • Player learned about Project GIBSON - a supercomputer at Ellingson Mineral with a backdoor
  • Acid Burn is now an ally and has unlocked the Elite section
  • Crash Override is identified as an inside contact at Ellingson (from contacts.txt)
  • Player knows Crash Override's modem number: 555-0200 (The Gibson Files BBS)
  • The goal: Find proof of the backdoor/worm before "they" use it

Potential Chapter 2 Elements

  • Activate The Gibson Files BBS with actual content
  • Communication with Crash Override
  • Infiltrating Ellingson Mineral systems
  • Finding evidence of the worm/backdoor
  • New puzzles and file discoveries
  • Possible new programs (e.g., hex editor, network scanner)

Technical Considerations

  • Expand BBSData with Gibson Files content
  • Add new files to FileSystem for Chapter 2 discoveries
  • Consider adding a "Network" program to access remote systems
  • New puzzle chain for Chapter 2

Notes

Testing on webOS

  • Use nginx with proper permissions for local development
  • Ensure _www user has execute permission on all parent directories
  • nginx config requires trailing slashes when using alias directive

Building

# webOS/LuneOS
./build.sh webos

# Web
./build.sh www

# Android
./build.sh android